<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/">
	<channel>
		<title>Author at Infinum</title>
		<atom:link href="https://infinum.com/blog/author/karlo-ceh/feed/" rel="self" type="application/rss+xml" />
		<link></link>
		<description>Building digital products</description>
		<lastBuildDate>Thu, 16 Apr 2026 15:26:52 +0000</lastBuildDate>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>

					<item>
				<image>
					<url>45464https://infinum.com/uploads/2023/11/IoT-device-onboarding-hero.webp</url>
				</image>
				<title>Implementing IoT Device Onboarding in Mobile Apps</title>
				<link>https://infinum.com/blog/iot-device-onboarding/</link>
				<pubDate>Wed, 08 Nov 2023 12:25:58 +0000</pubDate>
				<dc:creator>Karlo Čeh</dc:creator>
				<guid isPermaLink="false">https://infinum.com/?p=45464</guid>
				<description>
					<![CDATA[<p>Discover what challenges to expect when implementing IoT device onboarding in a mobile app and how to approach them best. </p>
<p>The post <a href="https://infinum.com/blog/iot-device-onboarding/">Implementing IoT Device Onboarding in Mobile Apps</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</description>
				<content:encoded>
					<![CDATA[<div
	class="wrapper"
	data-id="es-115"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-92">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-95"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-93">
	<p	class='typography typography--size-36-text js-typography block-paragraph__paragraph'
	data-id='es-94'
	>
	<strong>Implementing IoT device onboarding in a mobile app is a complex process dependent on a number of factors. Discover what challenges to expect and how to approach them best. </strong></p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-98"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-96">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-97'
	>
	To achieve its full potential, <a href="https://infinum.com/blog/secure-iot-connectivity/" target="_blank" rel="noreferrer noopener">any Internet of Things device needs to be connected to a network</a>, usually one with access to the internet. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-101"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-99">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-100'
	>
	When bringing a shiny new device home for the first time, a user will be met with the task of establishing this connection. To make it easier for them, the device’s interface can guide the user through setup and the network connection process.&nbsp;</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-104"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-102">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-103'
	>
	Some IoT devices also come with a companion <a href="https://infinum.com/mobile-web-apps/">mobile app</a> that takes the users through the onboarding process. There are various benefits to having this type of app, but a smoother and altogether better onboarding experience is the main one. When a companion app is used, a connection must be established between the phone and the IoT device – typically over Bluetooth or WiFi. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-107"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-105">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-106'
	>
	In this article, we’ll go over the expected hurdles in this multi-layered process and present some best practices for mitigating them.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-110"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-108">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-109'
	>
	IoT device onboarding is complex</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-113"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-111">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-112'
	>
	Let’s demonstrate the steps to onboarding an IoT device through a mobile app with a real-world example: the onboarding of a smart coffee machine to its companion app, Coffee+. </p></div>	</div>
</div>
</div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-121"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-116"
	 data-media-type='video'>

	<div class="video__wrapper" data-id="es-117">
		<video
		class="video block-media__video js-video js-block-media-video"
		 loop playsinline controls muted preload='metadata'>
		<source  src='https://infinum.com/uploads/2023/11/Onboarding.mp4' type='video/mp4' />	</video>
	<button	class="btn btn--color-infinum btn--size-medium btn--width-default btn__icon-position--center block-media__btn js-block-media-btn video__btn"
	data-id="es-118"
	>
		<div class="btn__inner">
					<div	class='typography typography--size-none js-typography btn__label'
	data-id='es-119'
	>
	 </div>		
		<i
	class="icon btn__icon icon--size-16 icon--scale-100"
	 aria-hidden='true' data-name='arrow-right-16' data-id='es-120'>
	<svg fill='none' height='16' viewBox='0 0 17 16' width='17' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'><g stroke='currentColor' stroke-width='2'><path d='m.5 7.99999 14 .00001'/><path d='m9.23352 2.7251 5.97848 5.97852'/><path d='m9.23352 13.2744 5.97848-5.9785'/></g></svg></i>	</div>
	</button></div></div></div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-139"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-122">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-125"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-123">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-124'
	>
	As we can see in this example, IoT device onboarding is quite a complex process. To give a high-level explanation, we first need to establish a connection with the device (in this case, the coffee machine) by connecting to its Wi-Fi network. Then, we need to connect the coffee machine to the internet (via the local Wi-Fi network) by sending network credentials. After that, we need to do the final configuration to sync the mobile app, the coffee machine, and the cloud. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-128"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-126">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-127'
	>
	As you can imagine, there are many things that can go wrong here. Not just for the end user but also for the development team working on the implementation.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-131"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-129">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-130'
	>
	Covering the unhappy flows in IoT device onboarding</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-134"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-132">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-133'
	>
	Setting up everything correctly during IoT device onboarding makes for what we call a <em>happy flow. </em>Most users will go through one of those. However, if there’s a problem in the process, they end up in an <em>unhappy flow. </em></p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-137"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-135">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-136'
	>
	Problems can occur on any side – they can be caused by the phone, the IoT device, the backend, or the network. In the example below, the user is having issues with their internet connection and the Wi-Fi being turned off on their phone.</p></div>	</div>
</div>
</div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-145"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-140"
	 data-media-type='video'>

	<div class="video__wrapper" data-id="es-141">
		<video
		class="video block-media__video js-video js-block-media-video"
		 playsinline controls muted preload='metadata'>
		<source  src='https://infinum.com/uploads/2023/11/Unhappy-flow.mp4' type='video/mp4' />	</video>
	<button	class="btn btn--color-infinum btn--size-medium btn--width-default btn__icon-position--center block-media__btn js-block-media-btn video__btn"
	data-id="es-142"
	>
		<div class="btn__inner">
					<div	class='typography typography--size-none js-typography btn__label'
	data-id='es-143'
	>
	 </div>		
		<i
	class="icon btn__icon icon--size-16 icon--scale-100"
	 aria-hidden='true' data-name='arrow-right-16' data-id='es-144'>
	<svg fill='none' height='16' viewBox='0 0 17 16' width='17' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'><g stroke='currentColor' stroke-width='2'><path d='m.5 7.99999 14 .00001'/><path d='m9.23352 2.7251 5.97848 5.97852'/><path d='m9.23352 13.2744 5.97848-5.9785'/></g></svg></i>	</div>
	</button></div></div></div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-187"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-146">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-149"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-147">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-148'
	>
	Ideally, to create a great user experience, all the unhappy flows need to be covered. In practice, this usually means forking the happy flow and navigating the user back to the screen containing instructions on how to resolve the problem. We can present the user with a “Retry” action or take them back a step or two in the flow to reset all the necessary logic. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-152"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-150">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-151'
	>
	Unfortunately, covering all the unhappy flows takes a lot of time and involves several members of the project team, not just the developers but also <a href="https://infinum.com/blog/testing-bluetooth-app-iot/" target="_blank" rel="noreferrer noopener">QA engineers</a> and designers.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-155"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-153">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-154'
	>
	However, failing to cater to the unhappy flows correctly, we can bring the app to an unexpected state, and the user can get stuck in a flow. Very often, that will result in the user leaving the app and possibly leaving a bad review.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-158"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-156">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-157'
	>
	To cover unhappy flows (and avoid unhappy users), the development team should follow a couple of core principles.&nbsp;</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-161"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-159">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-160'
	>
	Documentation is a valuable resource</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-164"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-162">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-163'
	>
	Not favored by many, but useful to all, the first line of defense against unhappy onboarding flows is documentation. Quality documentation should be produced at three levels: </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-167"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-165">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-166'
	>
	<li>Developers can use UML diagrams to visually document each step in the onboarding process or specific error cases. UML sequence diagrams are a great choice for this use case.</li><li>Designers can use tools for visually displaying the whole happy flow and all the unhappy ones. A visual representation of a flow is an excellent reference for anyone involved in the project, including stakeholders or non-technical persons. On Coffee+ we used Figma, but there are many similar tools available.</li><li>The QA team can define the test cases. In those cases, the steps to replicating unhappy flows should be explained in detail. This shouldn’t be limited to what to do in the mobile app but should also include information on how to get the device in a specific state, how to modify the network, etc.</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-170"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-168">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-169'
	>
	Another benefit to documentation is that it will also be useful for troubleshooting down the line.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-173"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-171">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-172'
	>
	Communicate efficiently</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-176"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-174">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-175'
	>
	Not necessarily limited to IoT device onboarding flows in supporting apps, good communication is another key factor for success. Of course, the whole team should strive for efficient communication, but let’s focus on the communication between the developers and the designers to illustrate. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-179"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-177">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-178'
	>
	Sometimes, developers can’t allow retries of specific actions because they would bring the app or the device to some undefined states. It is very important to communicate this clearly to the designers – what actions are safe to retry, where the flow needs to fork, etc. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-182"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-180">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-181'
	>
	Furthermore, the UX needs to be as simple as possible to hide the technical details, which are not relevant for the user.&nbsp;</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-185"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-183">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-184'
	>
	A great example of this is one of the final screens in the Coffee+ onboarding flow. This screen contains multiple checkpoints and a lot of things are going on under the hood. The user is unaware of them, but they get a sense of complexity and progress via the checkpoints.</p></div>	</div>
</div>
</div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-193"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-188"
	 data-media-type='video'>

	<div class="video__wrapper" data-id="es-189">
		<video
		class="video block-media__video js-video js-block-media-video"
		 playsinline controls muted preload='metadata'>
		<source  src='https://infinum.com/uploads/2023/11/Checkpoints.mp4' type='video/mp4' />	</video>
	<button	class="btn btn--color-infinum btn--size-medium btn--width-default btn__icon-position--center block-media__btn js-block-media-btn video__btn"
	data-id="es-190"
	>
		<div class="btn__inner">
					<div	class='typography typography--size-none js-typography btn__label'
	data-id='es-191'
	>
	 </div>		
		<i
	class="icon btn__icon icon--size-16 icon--scale-100"
	 aria-hidden='true' data-name='arrow-right-16' data-id='es-192'>
	<svg fill='none' height='16' viewBox='0 0 17 16' width='17' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'><g stroke='currentColor' stroke-width='2'><path d='m.5 7.99999 14 .00001'/><path d='m9.23352 2.7251 5.97848 5.97852'/><path d='m9.23352 13.2744 5.97848-5.9785'/></g></svg></i>	</div>
	</button></div></div></div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-238"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-194">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-197"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-195">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-196'
	>
	In this example, this is what the developer should tell the designer before implementing the screen:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-200"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-198">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-199'
	>
	<li>A couple of operations that take some time are going on</li><li>There is no need for multiple screens</li><li>A singular loading spinner might be too simple of a solution for this case because the user might feel the process is lingering for too long.</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-203"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-201">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-202'
	>
	Taking these insights into account, the designer can make the user feel much more comfortable in this situation.&nbsp;</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-206"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-204">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-205'
	>
	Sometimes you need to compromise</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-209"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-207">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-208'
	>
	Hand in hand with good communication comes another key tactic for success – compromise. If we want to unify the UX, including the unhappy flows, on all the platforms, sometimes we will need to compromise.&nbsp;&nbsp;</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-212"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-210">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-211'
	>
	Android, iOS, or even different versions of the same OS can handle crucial system calls that are needed for onboarding in different ways. For example, this happens when the user needs to connect to the <em>Philips</em> network in Coffee+. It might be possible to develop automatic network switching, meaning the app would connect to the network automatically. That kind of implementation would make for a better UX, surely. However, due to the differences in APIs between Android OS versions, custom restrictions some manufacturers add to their phones, and iOS guidelines on network change, this simply isn’t achievable in all scenarios.  </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-215"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-213">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-214'
	>
	To resolve this issue and keep just one version of the flow (as well as cover all related unhappy flows), we had to find a compromise through constant communication between developers working on different platforms, the QA team, and the designers. In this case, we settled for the user manually choosing the preferred network, <em>Phillips</em>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-218"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-216">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-217'
	>
	Supporting customers experiencing issues</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-221"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-219">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-220'
	>
	Of course, there are always cases of something unexpected happening during device onboarding, a case not covered by the development team. Did a user leave a bad review due to an onboarding problem or contact customer support? This is where analytics come into play.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-224"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-222">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-223'
	>
	Before diving into analytics, let’s not forget that we need user consent to track them. What is useful to track here? Many metrics come into play, but let’s just mention a few.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-227"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-225">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-226'
	>
	<li>The percentage of successful onboarding sessions can be an early warning that something wrong is happening in the system. The cause might not be in the app itself, but in backend deployment, the device’s firmware update, or the like. </li><li>Information about the screens the user visited can provide insight into the most common issues or point to the screens that need UX improvements. </li><li>Details about the user’s phone (version of OS, model, etc.) can help pinpoint phone-specific issues. They can also suggest where additional effort from the development team is needed. </li><li>Information about the circumstances in which crashes occurred. It is always a good idea to have a crash reporting system like <a href="https://firebase.google.com/products/crashlytics" target="_blank" rel="noreferrer noopener">Crashlytics</a> to know what fixes the app needs.</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-230"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-228">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-229'
	>
	Monitoring analytics is important because we can improve the UX based on data. And to reduce the number of issues as much as possible, good UX is of paramount importance.&nbsp;</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-233"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-231">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-232'
	>
	Sometimes, the details can make all the difference. For example, the user experience in the Coffee+ app needed to ensure that the app, the device and/or the cloud are ready for the next step in the onboarding process. Before the current implementation that included a required checkbox, the “Next” button was always enabled, which allowed some users to just speedrun the process without following the instructions. This way, the device would not be in the required state, the onboarding would eventually fail, and the users would get frustrated.&nbsp;</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-236"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-234">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-235'
	>
	However, based on user feedback and analytics, the development team iteratively improved the screen. With the new and improved UX, the failure (frustration) rate is much lower.</p></div>	</div>
</div>
</div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-244"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-239"
	 data-media-type='video'>

	<div class="video__wrapper" data-id="es-240">
		<video
		class="video block-media__video js-video js-block-media-video"
		 playsinline controls muted preload='metadata'>
		<source  src='https://infinum.com/uploads/2023/11/Improved-UX.mp4' type='video/mp4' />	</video>
	<button	class="btn btn--color-infinum btn--size-medium btn--width-default btn__icon-position--center block-media__btn js-block-media-btn video__btn"
	data-id="es-241"
	>
		<div class="btn__inner">
					<div	class='typography typography--size-none js-typography btn__label'
	data-id='es-242'
	>
	 </div>		
		<i
	class="icon btn__icon icon--size-16 icon--scale-100"
	 aria-hidden='true' data-name='arrow-right-16' data-id='es-243'>
	<svg fill='none' height='16' viewBox='0 0 17 16' width='17' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'><g stroke='currentColor' stroke-width='2'><path d='m.5 7.99999 14 .00001'/><path d='m9.23352 2.7251 5.97848 5.97852'/><path d='m9.23352 13.2744 5.97848-5.9785'/></g></svg></i>	</div>
	</button></div></div></div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-250"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-245">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-248"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-246">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-247'
	>
	Apart from analytics, there are other solutions that can help with resolving user issues. For example, when an error dialog is displayed, the development team might also provide the user with some additional info in the dialog itself. This might be an error code that customer service can use later on or a link to a guide with solutions to common issues.</p></div>	</div>
</div>
</div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-253"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-251"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-252">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2023/11/dialog-modified.webp"
					class="image__img block-media__image-img"
					alt=""
										height="2400"
															width="1080"
										loading="lazy"
					 />
					</picture>

	</figure></div></div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-311"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-254">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-257"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-255">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-256'
	>
	Users can have all kinds of network equipment and configurations. From routers with packet loss to a network configuration that blocks protocols used for IoT (e.g. SSDP). Customer service must be aware of this. As an addition, they can always reference developer documentation for troubleshooting.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-260"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-258">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-259'
	>
	What if the app is not the problem?</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-263"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-261">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-262'
	>
	Not all IoT device onboarding problems are caused by errors in the mobile app. The device and the cloud/backend can also be counted among the usual suspects. As explained above, analytics and customer support play a crucial part in troubleshooting, but the production team can also take certain steps during development to make troubleshooting easier. This can help identify current issues as well as future ones the users may face. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-266"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-264">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-265'
	>
	Use multiple environments </h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-269"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-267">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-268'
	>
	First of all, it is always a good idea to use multiple development/operating environments. Here’s one possible approach:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-272"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-270">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-271'
	>
	<li>The development environment can be used, as the name suggests, for feature development. At times, it can be unstable. </li><li>The staging environment should be more stable and can be used for testing before deploying to production.</li><li>The production environment that will be used by users should be rock solid. </li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-275"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-273">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-274'
	>
	Of course, it is possible to use both fewer or more environments, depending on your needs and preferences. For example, the device can only have two environments (development and production) and the mobile app and cloud can have three (development, staging, production). However, that way of working isn’t recommended because it can create confusion about which environments are compatible. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-278"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-276">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-277'
	>
	Expose relevant information</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-281"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-279">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-280'
	>
	Next, it’s important that the information about the device/app/cloud can be easily accessed. This means a delegated part of the development team needs to expose the relevant information to everyone else.&nbsp;</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-284"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-282">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-283'
	>
	The device team (also known as the embedded team) needs to expose the device’s firmware version and the environment the device is running on. This can be done by showing information on the device’s display, if available. Sending info to a database in the cloud or simply printing it in logs (either local logs via UART, for example, or cloud logs). The mobile app team can use that info to learn about the device’s capabilities, for example.<br />
<br />
The mobile team needs to expose the app’s version and the environment in which it is running. The best way for mobile teams to expose that info is to use a visual approach because team members who will use the app to test the onboarding process can have varying technical skills. This can be achieved by changing the app name and/or icon based on the environment and displaying it in the “About” section. Moreover, it is good practice to expose info about the app (client) to the cloud. This can be accomplished by sending the OS info, app version, build number, etc., in the <code>User-agent </code>header for HTTP requests.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-287"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-285">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-286'
	>
	Finally, <a href="https://infinum.com/cloud-application-development-services/">the cloud (backend) team</a> needs to provide the info about different environments. They can take it one step further and allow dynamic configuration of the mobile app or the device. This means the device or the mobile app switches environments based on the configuration sent by the cloud. Although this is not always achievable, it can make the development process less susceptible to errors. And as already mentioned, they can keep extensive logs to make troubleshooting easier.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-294"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<a	class="card-simple js-card-simple card-simple--is-ad block-card__card-simple card-simple--has-link js-card-simple-link card-simple__content-align--left"
	data-id="es-288"
	 target='_blank' rel='noopener noreferrer' href='https://infinum.com/iot-implementation/'>

	
	
	<div class="card-simple__content">
		<div class="card-simple__heading-wrap">
			<h2	class='typography typography--size-24-text js-typography card-simple__heading'
	data-id='es-289'
	>
	From strategy to deployment, we cover all aspects of IoT development lifecycle. <strong>Download our Guide to Successful IoT Implementation</strong>.</h2>		</div>

		<button	class="btn btn--color-infinum btn--size-small btn--width-default btn__icon-position--right card-simple__btn js-block-card-btn js-card-simple-link"
	data-id="es-291"
	 tabindex='-1'>
		<div class="btn__inner">
					<div	class='typography typography--size-none js-typography btn__label'
	data-id='es-292'
	>
	Learn more</div>		
		<i
	class="icon btn__icon icon--size-16 icon--scale-100"
	 aria-hidden='true' data-name='arrow-right-16' data-id='es-293'>
	<svg fill='none' height='16' viewBox='0 0 17 16' width='17' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'><g stroke='currentColor' stroke-width='2'><path d='m.5 7.99999 14 .00001'/><path d='m9.23352 2.7251 5.97848 5.97852'/><path d='m9.23352 13.2744 5.97848-5.9785'/></g></svg></i>	</div>
	</button>	</div>
</a>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-297"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-295">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-296'
	>
	Never lose focus on making your UX even better </h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-300"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-298">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-299'
	>
	Implementing an onboarding flow for IoT devices in mobile apps can present many challenges – for the end users, developers, and the QA team alike. Following these key principles can make them much easier to navigate.&nbsp;&nbsp;</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-303"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-301">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-302'
	>
	Supporting efficient communication in the whole project team and maintaining meticulous documentation can go a long way. Further, creating a system that allows the team to detect problems early on can help predict and mitigate many future issues.&nbsp;</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-306"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-304">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-305'
	>
	And finally, the key to making our UX the best it can be is our willingness to improve it constantly.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-309"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-307">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-308'
	>
	<em>If you&#8217;re interested in gaining a competitive advantage in the Internet of Things realm, check out our <a href="https://infinum.com/custom-iot-solutions/" target="_blank" rel="noreferrer noopener">IoT solutions page</a>.</em></p></div>	</div>
</div>
</div>		</div>
	</div><p>The post <a href="https://infinum.com/blog/iot-device-onboarding/">Implementing IoT Device Onboarding in Mobile Apps</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</content:encoded>
			</item>
					<item>
				<image>
					<url>28390https://infinum.com/uploads/2022/09/android-13-1.webp</url>
				</image>
				<title>Things to Keep in Mind When Migrating Your App to Android 13</title>
				<link>https://infinum.com/blog/migrating-your-app-to-android-13/</link>
				<pubDate>Fri, 30 Sep 2022 12:13:20 +0000</pubDate>
				<dc:creator>Karlo Čeh</dc:creator>
				<guid isPermaLink="false">https://infinum.com/?p=28390</guid>
				<description>
					<![CDATA[<p>Your app should work as expected out of the box (for the most part), but Android 13 provides a couple of new features to enhance it.</p>
<p>The post <a href="https://infinum.com/blog/migrating-your-app-to-android-13/">Things to Keep in Mind When Migrating Your App to Android 13</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</description>
				<content:encoded>
					<![CDATA[<div
	class="wrapper"
	data-id="es-349"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-312">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-315"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-313">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-314'
	>
	The new Android release is out, and it’s time to upgrade your app! Android 13 doesn’t introduce as many breaking changes as was the case with some previous releases. We can say that Google’s main focus was once again privacy and the most impactful change is related to notifications. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-318"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-316">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-317'
	>
	Your app should work as expected out of the box (for the most part), but Android 13 provides a couple of new features to enhance it. In this article, we will focus on some of them.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-321"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-319">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-320'
	>
	The first step in this journey is to set up the Android 13 SDK. In your <code>build.gradle.kts</code> or <code>build.gradle</code> file set your <code>compileSdk</code> (<code>compileSdkVersion</code>) and <code>targetSdk</code> (<code>targetSdkVersion</code>) to 33.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-324"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-322">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-323'
	>
	Keeping up with permissions</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-327"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-325">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-326'
	>
	The latest release brings some changes to permissions.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-330"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-328">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-329'
	>
	If your app needs to manage a device&#8217;s connections to nearby access points over Wi-Fi and you don’t need to derive physical location information from the Wi-Fi APIs you should add the <a href="https://developer.android.com/reference/android/Manifest.permission#NEARBY_WIFI_DEVICES" target="_blank" rel="noreferrer noopener">NEARBY_WIFI_DEVICES</a> permission to <code>Manifest</code>. This permission is like the <a href="https://developer.android.com/reference/android/Manifest.permission_group#NEARBY_DEVICES" target="_blank" rel="noreferrer noopener">NEARBY_DEVICES</a> permission (for Bluetooth) introduced in Android 12. Keep using <a href="https://developer.android.com/reference/android/Manifest.permission#ACCESS_FINE_LOCATION" target="_blank" rel="noreferrer noopener">ACCESS_FINE_LOCATION</a> if you need to derive the device location.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-333"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-331">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-332'
	>
	<br />
<a href="https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE" target="_blank" rel="noreferrer noopener">READ_EXTERNAL_STORAGE</a> is replaced by a more specific permission on Android 13. The type of media you want to read dictates which permissions you should request (ex. <a href="https://developer.android.com/reference/android/Manifest.permission#READ_MEDIA_IMAGES" target="_blank" rel="noreferrer noopener">READ_MEDIA_IMAGES</a>, <a href="https://developer.android.com/reference/android/Manifest.permission#READ_MEDIA_VIDEO" target="_blank" rel="noreferrer noopener">READ_MEDIA_VIDEO</a>, <a href="https://developer.android.com/reference/android/Manifest.permission#READ_MEDIA_AUDIO" target="_blank" rel="noreferrer noopener">READ_MEDIA_AUDIO</a>). If your app was previously granted the <a href="https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE" target="_blank" rel="noreferrer noopener">READ_EXTERNAL_STORAGE</a> permission, the system will auto-grant each new permission to your app. One less thing to worry about.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-336"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-334">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-335'
	>
	Manifest changes to migrate:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-338"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-xml github-light" data-language="xml" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">manifest</span><span class="token"> ...</span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">     </span><span class="token" style="color: #6a737d;">&lt;!--</span><span class="token" style="color: #6a737d;"> Required only if your app targets Android 13. </span><span class="token" style="color: #6a737d;">--&gt;</span><span class="token">
</span></span><span class="line"><span class="token">     </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">uses-permission</span><span class="token"> </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">name</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">android.permission.READ_MEDIA_IMAGES</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token">/&gt;</span><span class="token">
</span></span><span class="line"><span class="token">     </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">uses-permission</span><span class="token"> </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">name</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">android.permission.READ_MEDIA_AUDIO</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token">/&gt;</span><span class="token">
</span></span><span class="line"><span class="token">     </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">uses-permission</span><span class="token"> </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">name</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">android.permission.READ_MEDIA_VIDEO</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token">/&gt;</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">     </span><span class="token" style="color: #6a737d;">&lt;!--</span><span class="token" style="color: #6a737d;"> Required to maintain app compatibility. </span><span class="token" style="color: #6a737d;">--&gt;</span><span class="token">
</span></span><span class="line"><span class="token">     </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">uses-permission</span><span class="token">
</span></span><span class="line"><span class="token">         </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">name</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">android.permission.READ_EXTERNAL_STORAGE</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">
</span></span><span class="line"><span class="token">         </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">maxSdkVersion</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">32</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token">/&gt;</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">application</span><span class="token"> ...</span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">    ...
</span></span><span class="line"><span class="token">    </span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">application</span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">&lt;/</span><span class="token" style="color: #22863a;">manifest</span><span class="token">&gt;</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-341"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-339">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-340'
	>
	Another thing to note: starting with Android 13, your app can also revoke runtime permissions. This is Google’s way of allowing developers to adhere to best practices for permissions. Check the <a href="https://developer.android.com/training/permissions/requesting#remove-access" target="_blank" rel="noreferrer noopener">official documentation</a> for more information.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-344"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-342">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-343'
	>
	Permission to notify</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-347"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-345">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-346'
	>
	The biggest change to permissions is the introduction of runtime permission for notifications: <a href="https://developer.android.com/reference/android/Manifest.permission#POST_NOTIFICATIONS" target="_blank" rel="noreferrer noopener">POST_NOTIFICATIONS</a>. This means that apps require users to opt-in to notifications as with iOS. Just note that notifications related to media sessions are exempt from these changes in permissions.</p></div>	</div>
</div>
</div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-352"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-350"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-351">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2022/09/Permission-to-notify.webp"
					class="image__img block-media__image-img"
					alt=""
										height="2280"
															width="1080"
										loading="lazy"
					 />
					</picture>

			<figcaption class="image__figcaption block-media__image-figcaption">
			Notification permission prompt		</figcaption>
	</figure></div></div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-393"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-353">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-356"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-354">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-355'
	>
	Android 13 will handle notifications differently depending on the app target API and whether the app was just installed or available from before. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-359"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-357">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-358'
	>
	Newly installed apps</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-362"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-360">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-361'
	>
	Notifications are off by default. Your app has to be given permission to post notifications by the user.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-365"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-363">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-364'
	>
	<li>Target API level 33: the app fully controls when to show the permission dialog</li><li>Target API level 32 or lower: the system will show the permission dialog when the app creates its first notification channel</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-368"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-366">
	<h2	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-367'
	>
	Existing apps on devices upgraded to Android 13</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-371"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-369">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-370'
	>
	The system automatically pre-grants the new notification permission to all eligible apps when the user upgrades their device to Android 13. Users won’t see the permission dialog and notifications will be sent. Eligible apps have an existing notification channel and do not have their notifications explicitly disabled by the user.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-374"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-372">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-373'
	>
	The official documentation recommends requesting permission in context. Meaning it&#8217;s clear what the notifications are used for and why the user should opt in.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-377"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-375"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-376">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2022/09/notification-permission-a13-recommended-flow.webp"
					class="image__img block-media__image-img"
					alt=""
										height="803"
															width="1191"
										loading="lazy"
					 />
					</picture>

			<figcaption class="image__figcaption block-media__image-figcaption">
			Google’s example of requesting permission in context		</figcaption>
	</figure></div></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-380"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-378">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-379'
	>
	Without modifying your app’s flows too much, we could ask users for notification permission after they finish onboarding, land on the home screen for the first time, or something similar. It’s useful to know you can always check if notifications are enabled by invoking the <code>areNotificationsEnabled()</code> method from <code>NotificationManager</code>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-383"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-381">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-382'
	>
	Back navigation</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-386"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-384">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-385'
	>
	If you are using <code>OnBackPressed()</code>, you might notice that it is deprecated in API 33. The TLDR; answer is to use <code>OnBackInvokedCallback</code> or <code>androidx.activity.OnBackPressedCallback</code> to handle back navigation instead.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-388"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-highlighted-text">
	<p	class='typography typography--size-24-text js-typography block-highlighted-text__typography'
	data-id='es-387'
	>
	What’s interesting is that from Android 13 the system will provide a predictive back gesture. It will show users they are about to navigate back to the launcher from the application with a swipe-back action. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-391"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-389">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-390'
	>
	Your application should tell the system if it uses the swipe-back gesture or not. This feature is currently experimental, explore it if you want to future-proof your app.</p></div>	</div>
</div>
</div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-396"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-394"
	 data-media-type='video'>

	<div class="video__wrapper" data-id="es-395">
		<video
		class="video block-media__video js-video js-block-media-video video--cursor-takeover-use"
		 loop autoplay playsinline muted preload='metadata'>
		<source  src='https://infinum.com/uploads/2022/09/back_gesture.mp4' type='video/mp4' />	</video>
	</div></div></div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-446"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-397">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-400"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-398">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-399'
	>
	There are two cases to consider when implementing a predictive back gesture. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-403"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-401">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-402'
	>
	If you don’t want to intercept the back gesture, follow these steps:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-407"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="bullet bullet--left bullet__type--number bullet__color--black block-bullet__bullet" data-id="es-404">
	<p	class='typography typography--size-14-text js-typography bullet__dot'
	data-id='es-405'
	>
	1</p>	<div class="bullet__content">
		<p	class='typography typography--size-20-text-roman js-typography bullet__paragraph'
	data-id='es-406'
	>
	Add the dependency <code>"androidx.activity:activity:1.6.0-alpha05"</code> (or newer) to <code><code>build.gradle</code></code></p>	</div>
</div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-411"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="bullet bullet--left bullet__type--number bullet__color--black block-bullet__bullet" data-id="es-408">
	<p	class='typography typography--size-14-text js-typography bullet__dot'
	data-id='es-409'
	>
	2</p>	<div class="bullet__content">
		<p	class='typography typography--size-20-text-roman js-typography bullet__paragraph'
	data-id='es-410'
	>
	Add <code>android:enableOnBackInvokedCallback="true"</code> under <code>&lt;application&gt;</code> tag in manifest</p>	</div>
</div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-415"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="bullet bullet--left bullet__type--number bullet__color--black block-bullet__bullet" data-id="es-412">
	<p	class='typography typography--size-14-text js-typography bullet__dot'
	data-id='es-413'
	>
	3</p>	<div class="bullet__content">
		<p	class='typography typography--size-20-text-roman js-typography bullet__paragraph'
	data-id='es-414'
	>
	Enable Predictive back animations in the system’s developer options (for Android 13 this feature is still experimental but Google will expand upon it in future releases)</p>	</div>
</div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-418"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-416">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-417'
	>
	When using the gesture, you should be navigated back to the launcher if you followed the steps above.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-421"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-419">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-420'
	>
	On the other hand, if you want to intercept the back gesture:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-425"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="bullet bullet--left bullet__type--number bullet__color--black block-bullet__bullet" data-id="es-422">
	<p	class='typography typography--size-14-text js-typography bullet__dot'
	data-id='es-423'
	>
	1</p>	<div class="bullet__content">
		<p	class='typography typography--size-20-text-roman js-typography bullet__paragraph'
	data-id='es-424'
	>
	Repeat the steps from the previous case</p>	</div>
</div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-429"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="bullet bullet--left bullet__type--number bullet__color--black block-bullet__bullet" data-id="es-426">
	<p	class='typography typography--size-14-text js-typography bullet__dot'
	data-id='es-427'
	>
	2</p>	<div class="bullet__content">
		<p	class='typography typography--size-20-text-roman js-typography bullet__paragraph'
	data-id='es-428'
	>
	Register a callback that implements <code>OnBackInvokedCallback</code></p>	</div>
</div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-432"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-430">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-431'
	>
	If you register the mentioned callback, the system will not perform any of the predictive back gesture animations. You shouldn&#8217;t be navigated to the launcher every time.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-435"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-433">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-434'
	>
	Check out <a href="https://developer.android.com/about/versions/13/features/predictive-back-gesture" target="_blank" rel="noreferrer noopener">the documentation</a> for more info.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-438"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-436">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-437'
	>
	Poluglōttos</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-441"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-439">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-440'
	>
	If you are confused about this heading, don’t be. <em>Poluglōttos </em>is a greek word from which the word <em>polyglot</em> is derived. If your app is polyglot i.e., supports many languages, then you might want to give users the option to switch their in-app language. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-444"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-442">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-443'
	>
	<br />
You might be familiar with this problem because switching the language dynamically inside the app isn&#8217;t easy. Google it if you’re not. Solutions like <a href="https://github.com/infinum/android-localian" target="_blank" rel="noreferrer noopener">Localian</a> make language switching easier.</p></div>	</div>
</div>
</div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-449"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-447"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-448">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2022/09/polyglot.webp"
					class="image__img block-media__image-img"
					alt=""
										height="2280"
															width="1080"
										loading="lazy"
					 />
					</picture>

			<figcaption class="image__figcaption block-media__image-figcaption">
			System’s language options PER APP		</figcaption>
	</figure></div></div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-487"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-450">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-453"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-451">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-452'
	>
	But life could still be easier. That is why Android 13 is introducing per-app language preferences.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-456"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-454">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-455'
	>
	If your app implements the new per-app language preferences, then users can select the app’s language inside the system settings. What’s more, Android now provides APIs that make implementing an in-app language picker much easier and remove a lot of boilerplate code. <code>LocaleManager</code> is the class that contains new APIs. But to provide backward compatibility with older versions of Android, we will show the implementation using the <code>Appcompat</code> library (1.6.0-beta01 or higher).</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-459"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-457">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-458'
	>
	Implement <code>android:localeConfig</code> in <code>Manifest</code> to add supported languages to phone settings.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-461"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-xml github-light" data-language="xml" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">manifest</span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">    ...
</span></span><span class="line"><span class="token">    </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">application</span><span class="token">
</span></span><span class="line"><span class="token">        ...
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">localeConfig</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">@xml/locales_config</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">application</span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">&lt;/</span><span class="token" style="color: #22863a;">manifest</span><span class="token">&gt;</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-464"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-462">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-463'
	>
	Example of a local configuration file:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-466"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-xml github-light" data-language="xml" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">&lt;?</span><span class="token" style="color: #22863a;">xml</span><span class="token" style="color: #6f42c1;"> version</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">1.0</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #6f42c1;"> encoding</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">utf-8</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">?&gt;</span><span class="token">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">locale-config</span><span class="token"> </span><span class="token" style="color: #6f42c1;">xmlns</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">android</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">http://schemas.android.com/apk/res/android</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">  </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">locale</span><span class="token"> </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">name</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">en</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">/&gt;</span><span class="token">
</span></span><span class="line"><span class="token">  </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">locale</span><span class="token"> </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">name</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">hr</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">/&gt;</span><span class="token">
</span></span><span class="line"><span class="token">  </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">locale</span><span class="token"> </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">name</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">de</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">/&gt;</span><span class="token">
</span></span><span class="line"><span class="token">&lt;/</span><span class="token" style="color: #22863a;">locale-config</span><span class="token">&gt;</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-469"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-467">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-468'
	>
	How to set a user&#8217;s preferred language (assuming the user picked Croatian):</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-471"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-kotlin github-light" data-language="kotlin" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">val </span><span class="token">appLocale</span><span class="token">: </span><span class="token" style="color: #6f42c1;">LocaleListCompat</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> LocaleListCompat</span><span class="token">.forLanguageTags</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">hr</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">)
</span></span><span class="line"><span class="token">AppCompatDelegate</span><span class="token">.setApplicationLocales</span><span class="token">(appLocale)
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-474"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-472">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-473'
	>
	Be aware, invoking <code>setApplicationLocales()</code> recreates your Activity.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-477"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-475">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-476'
	>
	Additionally, you need to make one more change to <code>Manifest</code> to support Android 12 or lower:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-479"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-xml github-light" data-language="xml" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">application</span><span class="token">
</span></span><span class="line"><span class="token">  ...
</span></span><span class="line"><span class="token">  &lt;service
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">name</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">androidx.appcompat.app.AppLocalesMetadataHolderService</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">enabled</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">false</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">exported</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">false</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">meta-data</span><span class="token">
</span></span><span class="line"><span class="token">      </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">name</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">autoStoreLocales</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">
</span></span><span class="line"><span class="token">      </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">value</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">true</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token">/&gt;</span><span class="token">
</span></span><span class="line"><span class="token">  </span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">service</span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">  ...
</span></span><span class="line"><span class="token">&lt;/</span><span class="token" style="color: #22863a;">application</span><span class="token">&gt;</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-482"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-480">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-481'
	>
	And that&#8217;s it, switching languages should work now. For more information please check <a href="https://developer.android.com/guide/topics/resources/app-languages" target="_blank" rel="noreferrer noopener">the official documentation</a>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-485"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-483">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-484'
	>
	Photo picker I choose you</h2></div>	</div>
</div>
</div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-490"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-488"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-489">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2022/09/Photo-picker-I-choose-you.webp"
					class="image__img block-media__image-img"
					alt=""
										height="2280"
															width="1080"
										loading="lazy"
					 />
					</picture>

			<figcaption class="image__figcaption block-media__image-figcaption">
			PHoto picker in action		</figcaption>
	</figure></div></div>		</div>
	</div>

<div
	class="wrapper"
	data-id="es-516"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-491">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-494"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-492">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-493'
	>
	A great new addition to Android 13 is the Photo picker. Even though the name suggests this great new feature is just for picking photos, the Photo picker can also allow a user to pick videos from their media library. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-497"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-495">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-496'
	>
	Without going into details, here is a simple example for launching a Photo picker (to pick a single photo) and how to handle the result. For more details and examples be sure to check out <a href="https://developer.android.com/about/versions/13/features/photopicker" target="_blank" rel="noreferrer noopener">the official documentation. </a></p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-500"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-498">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-499'
	>
	Launch photo picker (user can select one photo):</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-502"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-java github-light" data-language="java" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> val pickMedia </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #6f42c1;">registerForActivityResult</span><span class="token">(</span><span class="token"> 
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #24292e;">ActivityResultContracts</span><span class="token">.</span><span class="token" style="color: #6f42c1;">PickVisualMedia</span><span class="token">(</span><span class="token">)</span><span class="token"> 
</span></span><span class="line"><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token"> uri </span><span class="token" style="color: #d73a49;">-&gt;</span><span class="token"> 
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #6a737d;">//</span><span class="token" style="color: #6a737d;"> handle result</span><span class="token" style="color: #6a737d;">
</span></span><span class="line"><span class="token">}</span><span class="token"> 
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> fun </span><span class="token" style="color: #6f42c1;">choosePhotoFromGallery</span><span class="token">(</span><span class="token">)</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #24292e;">pickMedia</span><span class="token">.</span><span class="token" style="color: #6f42c1;">launch</span><span class="token">(</span><span class="token"> 
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #6f42c1;">PickVisualMediaRequest</span><span class="token">(</span><span class="token"> 
</span></span><span class="line"><span class="token">      </span><span class="token" style="color: #24292e;">ActivityResultContracts</span><span class="token">.</span><span class="token" style="color: #24292e;">PickVisualMedia</span><span class="token">.</span><span class="token" style="color: #24292e;">ImageOnly</span><span class="token"> 
</span></span><span class="line"><span class="token">    </span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">)</span><span class="token"> 
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-505"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-503">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-504'
	>
	A new photo picker is available on apps targeting Android 13. But upcoming Google Play system updates will add photo picker support for apps that target Android 11 (API level 30) or higher.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-508"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-506">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-507'
	>
	Lucky 13</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-511"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-509">
	<p	class='typography typography--size-20-text-roman js-typography block-paragraph__paragraph'
	data-id='es-510'
	>
	In the last couple of Android releases Google has been focused on permissions and user privacy and Android 13 continues this trend. In addition, a new version of the OS brings a couple of new features that will make the lives of users (and developers!) easier. To see if 13 is your lucky number grab a nearby Pixel phone or emulator and get your app ready.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-514"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-512">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-513'
	>
	<em>Portions of this page are reproduced from work created and </em><a href="http://code.google.com/policies.html" target="_blank" rel="noreferrer noopener"><em>shared by the Android Open Source Project</em></a><em> and used according to terms described in the </em><a href="http://creativecommons.org/licenses/by/2.5/" target="_blank" rel="noreferrer noopener"><em>Creative Commons 2.5 Attribution License</em></a><em>.</em></p></div>	</div>
</div>
</div>		</div>
	</div><p>The post <a href="https://infinum.com/blog/migrating-your-app-to-android-13/">Things to Keep in Mind When Migrating Your App to Android 13</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</content:encoded>
			</item>
		
	</channel>
</rss>