<?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/domagoj-korman/feed/" rel="self" type="application/rss+xml" />
		<link></link>
		<description>Building digital products</description>
		<lastBuildDate>Tue, 21 Apr 2026 07:42:10 +0000</lastBuildDate>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>

					<item>
				<image>
					<url>7875https://infinum.com/uploads/2019/11/i-cheated-on-android-with-javascript-and-my-boss-approved-0.webp</url>
				</image>
				<title>I Cheated on Android with JavaScript&#8230; And My Boss Approved</title>
				<link>https://infinum.com/blog/i-cheated-on-android-with-javascript-and-my-boss-approved/</link>
				<pubDate>Tue, 12 Nov 2019 13:35:00 +0000</pubDate>
				<dc:creator>Domagoj Korman</dc:creator>
				<guid isPermaLink="false">https://infinum.com/the-capsized-eight/i-cheated-on-android-with-javascript-and-my-boss-approved/</guid>
				<description>
					<![CDATA[<p>I fell in love with mobile development five years ago. Android was my first love, but everyone knows the first love never lasts.</p>
<p>The post <a href="https://infinum.com/blog/i-cheated-on-android-with-javascript-and-my-boss-approved/">I Cheated on Android with JavaScript&#8230; And My Boss Approved</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</description>
				<content:encoded>
					<![CDATA[<div
	class="wrapper"
	data-id="es-215"
	 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-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-94'
	>
	I fell in love with mobile development five years ago. I loved the excitement of working on an app, deploying it on your phone, and being able to show it to everyone right away. Android was my first love, but everyone knows the first love never lasts.</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-heading" data-id="es-96">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-97'
	>
	Existing in the Android bubble</h2></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 I first started as an Android developer at Infinum having just finished Infinum Academy, I loved every part of Android. I didn’t have any professional experience at the time, so I wasn’t aware of other technologies that existed outside of the little Android bubble.</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'
	>
	Being a junior, I had to focus on a single technology to learn the most I could, so I didn’t even consider other technologies at the time. As you gain more experience, you broaden your horizons. Little by little, you discover and learn about the <strong>technologies that exist outside of your bubble</strong>.</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-heading" data-id="es-105">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-106'
	>
	Choose what you love and let it kill you</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-110"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-108">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-109'
	>
	Also, as you become more proficient in your area, it’s smart to decide what to focus on. In my opinion, there are a couple of different paths to pursue:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-113"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-111">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-112'
	>
	<li><strong>Management</strong>–the focus is on people and communication, and your responsibility is leading your team forwards, giving them goals to improve over time, and giving a helping hand whenever they are stuck</li><li><strong>Technical</strong>–the focus is on the area of your expertise, and you’re expected to suggest new and better solutions, make important decisions about code style, architecture, and frameworks</li><li><strong>Independent contributor</strong>, diversity track–the focus is on programming but with an emphasis on learning how different technologies coexist and depend on one another, as well as understanding their pros and cons</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-116"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-114">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-115'
	>
	The management path</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-119"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-117">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-118'
	>
	When I had little experience, I thought the only way to progress was by becoming a team lead. “You gain recognition and get to influence others around you, I mean, what’s not to like?!” I thought to myself as I looked up to my team lead, imagining that one day I’d like to be like him.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-122"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-120">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-121'
	>
	As I got more experienced, I understood the biggest con of the team lead position that you have to spend a lot of time communicating with your team and <strong>making sure everyone is happy</strong>. To me, that was a deal breaker because I enjoy programming and learning new things every day, and it’s what drives me forward to this day.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-125"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-123">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-124'
	>
	The technical lead path</h3></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'
	>
	Another possible career path was to try and become a technical lead. This position seems more alluring because you can focus mainly on your programming skills and lead the team by suggesting new ideas everyone can benefit from. Personally, I think the biggest flaw of the technical lead position is <strong>focusing only one area of expertise</strong>. If you don’t understand and know other areas, it can be hard to see the bigger picture, and this could potentially lead your team in the wrong direction.</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-paragraph" data-id="es-129">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-130'
	>
	I started my journey as an Android developer, I only knew Android development, so I thought that it was still too soon for me to try and become a technical lead as I still had a lot to learn.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-134"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-132">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-133'
	>
	The chosen path</h3></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'
	>
	In order to fill the knowledge gap, I was slowly leaning towards the diversity track. It all started on probably the happiest day of my life as a developer, with Google introducing Kotlin as the official Android programming language. Kotlin was a breath of fresh air compared to the outdated Java and consequently <strong>increased our team’s productivity and happiness</strong>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-140"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-138">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-139'
	>
	But more importantly, it also made me curious to try out other new technologies and programming languages. Soon afterward, I started experimenting with Flutter, Dart, and JavaScript. I liked JavaScript and web development so much that I made a radical decision.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-143"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-141">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-142'
	>
	I wanted to change my area of expertise entirely and <strong>become a JavaScript developer</strong>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-146"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-144">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-145'
	>
	Full support from my company</h2></div>	</div>

<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'
	>
	The decision was not instantaneous. I thought about it and discussed the potential career switch with more experienced colleagues and superiors for several months, questioning whether it was worth it.</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'
	>
	It isn’t that common for companies to encourage switching careers, but I was very lucky that my company was forward-thinking and supported me through the entire process.</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'
	>
	Another important thing to take into account when thinking about career switch is the reasons you want to switch technologies. It’s highly important to understand that every technology has its flaws.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-160"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="blockquote block-blockquote__blockquote" data-id="es-156">
	
	<div class="blockquote__content">
		<i
	class="icon blockquote__icon icon--size-16 icon--scale-100"
	 aria-hidden='true' data-name='blockquote-24' data-id='es-157'>
	<svg fill='none' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'><path clip-rule='evenodd' d='m12 24c6.6274 0 12-5.3726 12-12 0-2.79685-.9568-5.37021-2.561-7.41062-.581.22951-1.0832.60583-1.5069 1.12898-.5132.60844-.7698 1.41969-.7698 2.43375v.07605h2.5789v5.59004h-5.6197v-5.01962c0-1.11547.154-2.06616.4619-2.85205.3336-.81125.757-1.48307 1.2702-2.01545.528-.52161 1.1175-.92155 1.7687-1.1998-2.0728-1.70651-4.7279-2.73128-7.6223-2.73128-6.62742 0-12 5.37258-12 12 0 6.6274 5.37258 12 12 12zm-3.53811-18.05347c-.30793.78589-.46189 1.73658-.46189 2.85205v5.01962h5.6197v-5.59004h-2.5789v-.07605c0-1.01406.2566-1.82531.7698-2.43375.5389-.63379 1.1804-1.05209 1.9245-1.2549v-2.28164c-.7441.07605-1.4626.25351-2.1555.53238-.6928.27887-1.3086.68449-1.84752 1.21688-.51321.53238-.9366 1.2042-1.27019 2.01545z' fill='currentColor' fill-rule='evenodd'/></svg></i><p	class='typography typography--size-36-text js-typography blockquote__quote'
	data-id='es-158'
	>
	You can’t seriously switch from one technology because of its disadvantages and expect the new technology to be all sunshine and rainbows.</p>
		<div class="blockquote__caption-wrap">
					</div>
	</div>
</div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-163"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-161">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-162'
	>
	How to decide whether to switch careers?</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-166"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-164">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-165'
	>
	A career switch is a huge decision that literally changes your daily life, so don’t take it lightly. Take your time and decide whether the pros outweigh the cons.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-169"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-167">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-168'
	>
	<strong>Pros:</strong></p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-172"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-170">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-171'
	>
	<li>working in a new technology, which is always interesting</li><li>broadening horizons and understanding the bigger picture</li><li>being flexible when something wild happens with one system (e.g. Flutter takes over the world and leaves Android in a crumpled heap on the floor)</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-175"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-173">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-174'
	>
	<strong>Cons:</strong></p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-178"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-176">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-177'
	>
	<li>being a senior experience-wise but a junior at the new technology</li><li>becoming easily frustrated because of it</li><li>influencing your salary</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-181"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-179">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-180'
	>
	From hero to… zero</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-184"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-182">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-183'
	>
	On the one hand, changing your area of expertise puts you in the position of knowing little if anything at all. You literally go from a senior to a junior in a day. On the other hand, the <strong>learning process is also much faster</strong> compared to when you first started programming, thanks to the previously acquired general knowledge that can be applied to any programming language and technology.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-187"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-185">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-186'
	>
	Perhaps the biggest change you need to make is to your thinking process. Basically, ask whatever question comes to mind, be curious and research whatever doesn’t make sense. You should spend a lot of time researching and reading documentation and online resources to learn as much as you can.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-190"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-188">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-189'
	>
	In fact, this is exactly why <strong>switching technologies can get really exhausting</strong>, but it’s crucial to success and reaching that same level of knowledge you had previously reached in another technology.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-193"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-191">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-192'
	>
	Resist the urge to learn everything at once</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-196"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-194">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-195'
	>
	You should also be aware that your previously gained knowledge can easily backfire. The more knowledge you gain, the more aware you become of everything from frameworks and cool language features to dependency injection etc. You’ll feel the urge to learn all of it at the same time because you kicked ass in your previous area of expertise, but I strongly advise against that.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-201"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="blockquote block-blockquote__blockquote" data-id="es-197">
	
	<div class="blockquote__content">
		<i
	class="icon blockquote__icon icon--size-16 icon--scale-100"
	 aria-hidden='true' data-name='blockquote-24' data-id='es-198'>
	<svg fill='none' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'><path clip-rule='evenodd' d='m12 24c6.6274 0 12-5.3726 12-12 0-2.79685-.9568-5.37021-2.561-7.41062-.581.22951-1.0832.60583-1.5069 1.12898-.5132.60844-.7698 1.41969-.7698 2.43375v.07605h2.5789v5.59004h-5.6197v-5.01962c0-1.11547.154-2.06616.4619-2.85205.3336-.81125.757-1.48307 1.2702-2.01545.528-.52161 1.1175-.92155 1.7687-1.1998-2.0728-1.70651-4.7279-2.73128-7.6223-2.73128-6.62742 0-12 5.37258-12 12 0 6.6274 5.37258 12 12 12zm-3.53811-18.05347c-.30793.78589-.46189 1.73658-.46189 2.85205v5.01962h5.6197v-5.59004h-2.5789v-.07605c0-1.01406.2566-1.82531.7698-2.43375.5389-.63379 1.1804-1.05209 1.9245-1.2549v-2.28164c-.7441.07605-1.4626.25351-2.1555.53238-.6928.27887-1.3086.68449-1.84752 1.21688-.51321.53238-.9366 1.2042-1.27019 2.01545z' fill='currentColor' fill-rule='evenodd'/></svg></i><p	class='typography typography--size-36-text js-typography blockquote__quote'
	data-id='es-199'
	>
	You should focus on one thing at a time, master it, and only when that piece of the puzzle really sinks in, go to the next one.</p>
		<div class="blockquote__caption-wrap">
					</div>
	</div>
</div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-204"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-202">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-203'
	>
	If you try to process everything simultaneously, things will become chaotic and everything will mix together. You’ll end up with a mushy pile of confusion about what is in front of your screen and what you are doing with your life in general.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-207"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-205">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-206'
	>
	Don’t bother unless you really want it</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-210"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-208">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-209'
	>
	It’s important to decide which career path you want to take. If you decide to make a career switch and try a new technology or language, you must be <strong>100% sure that you are ready to learn new things every day</strong> because it can become really exhausting over time. It is also important to understand the flaws of each technology you tackle and not expect everything to be ideal.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-213"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-211">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-212'
	>
	Finally, the most important switch you need to make is the one within your mindset. Ask stupid questions, make stupid mistakes, and learn something new every day. I promise that in time, you will grab the new technology by the balls and catch up with your senior colleagues. Just don’t do it the other way around.</p></div>	</div>
</div>
</div>		</div>
	</div><p>The post <a href="https://infinum.com/blog/i-cheated-on-android-with-javascript-and-my-boss-approved/">I Cheated on Android with JavaScript&#8230; And My Boss Approved</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</content:encoded>
			</item>
					<item>
				<image>
					<url>8127https://infinum.com/uploads/2019/10/new-and-improved-fingerprint-authentication-in-Android-apps-0.webp</url>
				</image>
				<title>New &#038; Improved Fingerprint Authentication in Android Apps</title>
				<link>https://infinum.com/blog/new-and-improved-fingerprint-authentication-in-android-apps/</link>
				<pubDate>Tue, 01 Oct 2019 14:25:00 +0000</pubDate>
				<dc:creator>Domagoj Korman</dc:creator>
				<guid isPermaLink="false">https://infinum.com/the-capsized-eight/new-and-improved-fingerprint-authentication-in-android-apps/</guid>
				<description>
					<![CDATA[<p>When Google released `BiometricPrompt`, we tried integrating it in our Android library for fingerprint authentication.</p>
<p>The post <a href="https://infinum.com/blog/new-and-improved-fingerprint-authentication-in-android-apps/">New &amp; Improved Fingerprint Authentication in Android Apps</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</description>
				<content:encoded>
					<![CDATA[<div
	class="wrapper"
	data-id="es-263"
	 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-216">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-219"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-217">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-218'
	>
	Google released the new <code>BiometricPrompt</code> as a replacement for <code>FingerprintManager</code> last September. We decided to test out whether we would be able to integrate it in our Android library which simplifies fingerprint authentication, called the Goldfinger–and whether a library like Goldfinger would even be necessary with the new API.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-222"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-220">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-221'
	>
	FingerprintManager vs. BiometricPrompt</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-225"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-223">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-224'
	>
	During Google I/O ’18, Google announced <code>BiometricPrompt</code> which is used as a replacement for <code>FingerprintManager</code>. <code>BiometricPrompt</code> should choose the most appropriate biometric (fingerprint, face, iris) and display system dialog so that users have consistent behavior across all applications.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-228"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-226">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-227'
	>
	<code>BiometricPrompt</code> was introduced as an Android P feature, but Google also mentioned backwards-compatible <code>BiometricPrompt</code>, similar to how <code>FingerprintManager</code> and <code>FingerprintManagerCompat</code> behave.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-231"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-229">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-230'
	>
	Google finally released the first alpha version of <code>androidx.biometrics</code> library last September. While we expected the development of the library to be fast, it was stuck in the alpha version for almost a year. There was even an <a href="https://issuetracker.google.com/issues/139861235">issue</a> open!</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-234"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-232">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-233'
	>
	At last, they released the <code>beta1</code> version in August. We tested the <code>beta1</code> version and were quite happy with how it works. There are several smaller issues, but those should be resolved by the time a stable version is released, hopefully.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-237"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-235">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-236'
	>
	Replacing FingerprintManager in Goldfinger</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-240"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-238">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-239'
	>
	Two years ago, we created an Android library that simplifies Fingerprint authentication called <a href="https://github.com/infinum/Android-Goldfinger">Goldfinger</a>. Our library was using deprecated <code>FingerprintManager</code> to that point, so we wanted to test out if we would be able to integrate <code>BiometricPrompt</code> in it, and whether a library like Goldfinger would even be neccessary with the new API.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-243"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-241">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-242'
	>
	We came to the conclusion that <code>BiometricPrompt</code> and <code>FingerprintManager</code> APIs are almost the same. The biggest improvement over <code>FingerprintManager</code> is that you don’t even need to know what <code>CryptoObject</code> is if you need simple user authentication without data encryption.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-246"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-244">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-245'
	>
	All other complexities regarding user authentication are still there–<code>CryptoObject</code> creation if a cipher is needed, cancel dispatched as an error after the developer invokes cancel himself, save and restore initialization vector, and others. For all those reasons, we decided to refactor Goldfinger’s internals to use the new <code>BiometricPrompt</code> API.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-249"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-247">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-248'
	>
	Welcome Goldfinger 2.0</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-252"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-250">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-251'
	>
	Nowadays, Goldfinger API uses <code>BiometricPrompt</code> and has a new API that requires <code>Goldfinger.PromptParams</code> instance. <code>Goldfinger.PromptParams</code> are constructed via a builder pattern and you give it all values that are required for constructing <code>BiometricPrompt</code>. Afterwards, Goldfinger encapsulates all biometric complexities for you.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-255"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-253">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-254'
	>
	It creates <code>CryptoObject</code> asynchronously, starts biometric authentication, saves and restores initialization vector, handles weird edge cases introduced by on-screen fingerprint scanners, handles all system exceptions and returns them in a single callback, as well as offers you a separate Rx module.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-258"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-256">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-257'
	>
	Feedback is welcome</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-261"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-259">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-260'
	>
	The <a href="https://github.com/infinum/Android-Goldfinger">current version</a> is still a release candidate so we are looking for potential improvements and suggestions from the community. We plan on releasing the final, stable version as soon as <code>androidx.biometrics</code> leaves beta–feel free to drop us a comment, suggestion or even a pull request so that we can prepare Goldfinger 2.0 to be the best possible.</p></div>	</div>
</div>
</div>		</div>
	</div><p>The post <a href="https://infinum.com/blog/new-and-improved-fingerprint-authentication-in-android-apps/">New &amp; Improved Fingerprint Authentication in Android Apps</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</content:encoded>
			</item>
					<item>
				<image>
					<url>8080https://infinum.com/uploads/2019/05/conquering-android-camera-api-0.webp</url>
				</image>
				<title>Conquering Android Camera APIs</title>
				<link>https://infinum.com/blog/conquering-android-camera-api/</link>
				<pubDate>Tue, 21 May 2019 11:00:00 +0000</pubDate>
				<dc:creator>Domagoj Korman</dc:creator>
				<guid isPermaLink="false">https://infinum.com/the-capsized-eight/conquering-android-camera-api/</guid>
				<description>
					<![CDATA[<p>If you’ve ever worked with Android Camera APIs, you probably know why almost everyone dislikes it. But it doesn't have to be this way!</p>
<p>The post <a href="https://infinum.com/blog/conquering-android-camera-api/">Conquering Android Camera APIs</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</description>
				<content:encoded>
					<![CDATA[<div
	class="wrapper"
	data-id="es-408"
	 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-264">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-267"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-265">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-266'
	>
	As an Android developer, one of the worst phrases I could hear was, “We want a custom in-app camera.” If you’ve ever worked with Android Camera APIs, you probably know why almost everyone dislikes it. If you haven’t had to tackle this challenge, be grateful.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-270"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-268">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-269'
	>
	I recently worked with Android Camera APIs, and I think I’ve finally conquered them. In this blog post, I will discuss the behavior of the camera, which issues I had along the way, and how I solved them.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-273"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-271">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-272'
	>
	Two Versions</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-276"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-274">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-275'
	>
	To implement a custom in-app camera, you have no choice but to use Android Camera API. Android Camera API has two versions: First, there’s the deprecated version (Camera1), which supports devices older than Lollipop. Then there’s the Camera2 version that is supported only on Lollipop and newer devices. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-279"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-277">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-278'
	>
	The main difference between these two APIs is that Camera1 is simpler, and in my opinion, more consistent. Camera2 offers more features, but in most cases these features are unnecessary. Both camera APIs have weird behavior and numerous edge cases, so let’s start with an issue that both APIs share.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-282"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-280">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-281'
	>
	Preview</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-285"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-283">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-284'
	>
	When working with the camera API, the first thing you want to implement is the preview. It’s funny (or actually quite frustrating), because as soon as you start, Android stabs you in the back: the preview you are trying to display does not work as expected, and you see a preview that looks distorted like the image below.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-288"
	 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-286"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-287">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2019/05/conquering-android-camera-api-1.webp"
					class="image__img block-media__image-img"
					alt=""
										height="620"
															width="1000"
										loading="lazy"
					 />
					</picture>

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

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-291"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-289">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-290'
	>
	Once you see the preview, you’ll probably wonder what you did wrong. Sadly, nothing. This is the default camera behavior, and now it is up to you to decipher what exactly happened. First, you need to understand how matrix transformations work—otherwise, you are screwed. Matrix transformations are used to rotate and scale the preview. When the camera is started, it automatically rotates the preview to be in sync with the camera’s orientation, and it scales the preview to fit whole view. The preview image is distorted because the X and Y axes are scaled individually—so while the X axis is scaled down, the Y axis could be scaled up to fit the view.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-294"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-292">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-293'
	>
	<strong>The camera has its own orientation</strong>, which is not the same as device orientation. The camera’s orientation is usually 90 degrees (regardless of device orientation), and that is why your preview is rotated. The first step to solving this is reversing the camera orientation. Here you can see the method that calculates the <a href="https://github.com/infinum/Android-GoldenEye/blob/master/goldeneye/src/main/java/co/infinum/goldeneye/utils/CameraUtils.kt#L33">difference between camera and device orientation</a>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-297"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-295">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-296'
	>
	Step two is reversing the scaling of the X and Y axes. To reverse the process, first you need to reverse the matrix to get the original preview size and then apply your own scale depending on your end goal. <a href="https://github.com/infinum/Android-GoldenEye/blob/master/goldeneye/src/main/java/co/infinum/goldeneye/utils/CameraUtils.kt#L58">Here</a> is the method that calculates and applies the correct scale to the preview. The GIF below demonstrates what you have to do to create a nice-looking preview.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-300"
	 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-298"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-299">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2019/05/conquering-android-camera-api-2.gif"
					class="image__img block-media__image-img"
					alt=""
										height="600"
															width="800"
										loading="lazy"
					 />
					</picture>

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

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-303"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-301">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-302'
	>
	Camera1</h2></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'
	>
	Camera1 API has been deprecated since Android Lollipop. We work on a lot of apps that must support older devices than Lollipop, so simply ignoring the deprecated API was a no-go. As Camera1 is supported on all Android devices, I decided to first dig into its API.</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-heading" data-id="es-307">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-308'
	>
	Parameters</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-312"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-310">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-311'
	>
	To configure the camera, you need to use <code>Camera.Parameters</code> object, modify it and then set it to the camera instance. <code>Camera.Parameters</code> object contains setters that can be used to change flash, focus, zoom, etc. Just from this explanation, you might think this process is all sunshine and rainbows. Not so much.</p></div>	</div>

<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-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-314'
	>
	The issue with <code>Camera.Parameters</code> is not its complexity, but rather how invalid parameters are reported back to you. They are applied and validated after you execute <code>Camera#setParameters(Camera.Parameters)</code>, and if any parameter is invalid, the camera just crashes with message – <code>setParameters failed</code>.</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-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-317'
	>
	I could not find a more effective way to debug the issue than removing parameter by parameter until I figured out which one exactly caused the crash. My suggestion here is to wrap basically everything that communicates with the native camera API into try/catch blocks and implement better error handling. This allows you to handle all your errors in one place because <strong>the camera API is chaotic and unpredictable</strong>.</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-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-320'
	>
	While the preview is active, almost all parameters can be dynamically updated except the preview and picture size. In case you need to dynamically change the preview and picture size, restarting the preview with new parameters should do the trick.</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'
	>
	Sizes</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-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-326'
	>
	This was one of my favorite chuckle moments because the issue was easy to solve—but it was a clear indicator that you should not trust the camera API documentation. Camera API can return a list of sizes that can be used to set the preview and picture size—and I had to deal with two screw ups here.</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-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-329'
	>
	The first problem arose because I assumed the API will be deterministic. The first three devices I tested with all returned a list in descending order, so I assumed the first element will always be the highest resolution available. This worked fine until I tested on a device that had a blurry preview. I was confused at first, but after some debugging I noticed this specific device returns a list of sizes in ascending order. To solve the issue, I manually sorted sizes in the same order.</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-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-332'
	>
	The second issue I encountered was this: On Samsung S3 Mini, I received a crash with <code>NullPointerException</code> when trying to sort the list. The issue was clear: the map returned by the camera API was null. The funny part was what what Javadoc said:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-335"
	 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">
</span></span><span class="line"><span class="token" style="color: #6a737d;">/**</span><span class="token" style="color: #6a737d;">
</span></span><span class="line"><span class="token" style="color: #6a737d;">* Gets the supported picture sizes.
</span></span><span class="line"><span class="token" style="color: #6a737d;">*
</span></span><span class="line"><span class="token" style="color: #6a737d;">* </span><span class="token" style="color: #d73a49;">@return</span><span class="token" style="color: #6a737d;"> a list of supported picture sizes. This method will always
</span></span><span class="line"><span class="token" style="color: #6a737d;">*         return a list with at least one element.
</span></span><span class="line"><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">public</span><span class="token"> List</span><span class="token" style="color: #d73a49;">&lt;</span><span class="token">Size</span><span class="token" style="color: #d73a49;">&gt;</span><span class="token"> </span><span class="token">getSupportedPictureSizes</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-338"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-336">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-337'
	>
	The fix was to fallback to the list of preview sizes if the list of picture sizes is not available. Now that I’ve learned from my mistakes, I know to <strong>never trust the camera API documentation</strong>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-341"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-339">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-340'
	>
	Taking a picture</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-344"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-342">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-343'
	>
	To take a picture, you call <code>Camera#takePicture</code> and pass two callbacks. When the picture is taken, you receive an array of bytes as a result. If you convert the array into a Bitmap and display the Bitmap, your picture will be rotated. The fix is similar to the preview: you must calculate the difference between the device and camera orientation and rotate the image yourself.</p></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'
	>
	There is one small hidden issue that can happen if you are not careful with image manipulation. When working with Bitmaps and Bitmap transformation, always be aware of potential <code>OutOfMemoryException</code>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-350"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-348">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-349'
	>
	Tap to focus</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-353"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-351">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-352'
	>
	The tap to focus functionality requires some mathematical skills because there are multiple conversions you must calculate. When you press on your screen, you receive a touch event with X and Y coordinates that correspond to your device’s coordinate system. When you want to tell the camera which area to focus on, you need to give it X and Y coordinates that correspond to the camera’s coordinate system. The mapping is not simple because you also need to calculate in the scaling.</p></div>	</div>

<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'
	>
	I personally found the last step of implementing tap to focus amusing. This is taken from Javadoc:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-358"
	 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">
</span></span><span class="line"><span class="token" style="color: #d73a49;">*</span><span class="token"> The bounds are relative to the camera’s
</span></span><span class="line"><span class="token" style="color: #d73a49;">*</span><span class="token"> current </span><span class="token" style="color: #d73a49;">field</span><span class="token"> of view. The coordinates are mapped so </span><span class="token">that </span><span class="token">(</span><span class="token" style="color: #d73a49;">-</span><span class="token" style="color: #005cc5;">1000</span><span class="token">, </span><span class="token" style="color: #d73a49;">-</span><span class="token" style="color: #005cc5;">1000</span><span class="token">)
</span></span><span class="line"><span class="token" style="color: #d73a49;">*</span><span class="token"> </span><span class="token" style="color: #d73a49;">is</span><span class="token"> always the top</span><span class="token" style="color: #d73a49;">-</span><span class="token">left corner of the current </span><span class="token" style="color: #d73a49;">field</span><span class="token"> of view, </span><span class="token">and </span><span class="token">(</span><span class="token" style="color: #005cc5;">1000</span><span class="token">,
</span></span><span class="line"><span class="token" style="color: #d73a49;">*</span><span class="token"> </span><span class="token" style="color: #005cc5;">1000</span><span class="token">) </span><span class="token" style="color: #d73a49;">is</span><span class="token"> always the bottom</span><span class="token" style="color: #d73a49;">-</span><span class="token">right corner of the current </span><span class="token" style="color: #d73a49;">field</span><span class="token"> of
</span></span><span class="line"><span class="token" style="color: #d73a49;">*</span><span class="token"> view.
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-361"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-359">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-360'
	>
	In short, after you map everything into the camera’s coordinate system, you have to map it into this [-1000, 1000] coordinate system and pass it to the camera. Why? I honestly have no idea.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-364"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-362">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-363'
	>
	Camera2</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-367"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-365">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-366'
	>
	To be honest, after finishing the Camera1 wrapper, I figured Camera2 is newer, and thus, better. My assumption was incorrect. Understanding Camera2 API was much harder than I anticipated, and I found it much less intuitive than Camera1 API.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-370"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-368">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-369'
	>
	Taking a picture</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-373"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-371">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-372'
	>
	Camera2 API gives you more control over the camera, but at the same time it does not provide a concise API so you have to handle everything manually. In retrospect, Camera1 at least had the <code>takePicture</code> method, which was convenient to use. Camera2 API has multiple states, and you must handle those states manually and execute specific methods when the state changes.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-376"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-374">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-375'
	>
	I think a single look into this table is enough to see the <a href="https://developer.android.com/reference/android/hardware/camera2/CaptureResult#CONTROL_AE_STATE">complexity behind Camera2</a>. Also, the issue was that some devices would lock into one state and it would never trigger the state to take a picture. Other devices would trigger capture state and record a blurry image. I feel like these states are something that should be handled by the API behind the curtains, the same way Camera1 handles it.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-379"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-377">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-378'
	>
	I also noticed that introducing flash mode into Camera2 picture taking just introduced more bugs and more mess because it added more states that you have to handle manually. Torch mode is also separate from all other flash modes, so when you want to turn on the torch, you must turn off flash and vice versa.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-382"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-380">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-381'
	>
	Recording a video</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-385"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-383">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-384'
	>
	I used <code>MediaRecorder</code> class to record a video with both Camera1 and Camera2 API. I have to admit that out of all features, video recording was the easiest to implement. Even so, there are some potential traps you should avoid.</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-paragraph" data-id="es-386">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-387'
	>
	<code>MediaRecorder</code> works almost the same as <code>Camera.Parameters</code>. You have to initialize the whole <code>MediaRecorder</code> and when you want to start the video recording—it will crash if you set some unsupported parameter value. Also, just like <code>Camera.Parameters</code>, it does not tell you which parameter caused the crash. Most often, the <code>MediaRecorder</code> would crash because of either non-existent video resolution or video resolution that is too big for it to handle.</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'
	>
	To configure <code>MediaRecorder</code>, you can use <code>CamcorderProfile</code> class. It contains a set of parameters that are automatically applied to the <code>MediaRecorder</code> when applied via the <code>MediaRecorder#setProfile(CamcorderProfile)</code> method. Be careful when using this method with Camera2 as <strong>some profiles returned by the camera API are false positives</strong>. To receive valid profiles, you should check that the returned profile contains the resolution that is supported by the camera.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-394"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-392">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-393'
	>
	Camera1 API is awesome</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-397"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-395">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-396'
	>
	When compared to Camera2 API, Camera1 API is awesome simply because of its consistency. It behaves oddly at times, but once you fix those weird edge cases, it mostly works.</p></div>	</div>

<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'
	>
	Camera2 is difficult to use, has too many properties and states that must be handled by the developer, and it is not consistent between devices. Sometimes, for some reason, if you do not turn off property A when setting property B, the camera preview goes black without any error. It is discouraging when you have to work with that kind of an API with so many side effects.</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'
	>
	I would suggest to use camera1 API even if you work on an application that is intended only for Lollipop and newer devices. Just be extra careful because Camera1 is deprecated and could be removed in the future.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-406"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-404">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-405'
	>
	Handling the Camera API is challenging, but you can learn a lot when you want to expose a simple and easy to use interface over something as complex as the Android camera API. Also, when you manage to tame the beast in the end, you’ll have a sense of satisfaction. Hopefully, Google is doing something regarding the camera API (<a href="https://developer.android.com/training/camerax">CameraX</a>). In the meantime, feel free to use our <a href="https://github.com/infinum/Android-GoldenEye">GoldenEye library</a> and let me know what you think about the camera API.</p></div>	</div>
</div>
</div>		</div>
	</div><p>The post <a href="https://infinum.com/blog/conquering-android-camera-api/">Conquering Android Camera APIs</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</content:encoded>
			</item>
					<item>
				<image>
					<url>7940https://infinum.com/uploads/2018/08/top-10-android-libraries-every-android-developer-should-know-about-0.webp</url>
				</image>
				<title>Top 10 Android Libraries Every Android Developer Should Know About</title>
				<link>https://infinum.com/blog/top-10-android-libraries-every-android-developer-should-know-about/</link>
				<pubDate>Thu, 09 Aug 2018 15:15:00 +0000</pubDate>
				<dc:creator>Domagoj Korman</dc:creator>
				<guid isPermaLink="false">https://infinum.com/the-capsized-eight/top-10-android-libraries-every-android-developer-should-know-about/</guid>
				<description>
					<![CDATA[<p>Jump right into the list of best Android libraries for developers that make Android development much more enjoyable for anyone who uses them.</p>
<p>The post <a href="https://infinum.com/blog/top-10-android-libraries-every-android-developer-should-know-about/">Top 10 Android Libraries Every Android Developer Should Know About</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</description>
				<content:encoded>
					<![CDATA[<div
	class="wrapper"
	data-id="es-544"
	 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-409">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-412"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-410">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-411'
	>
	Back in 2015, we published an article discussing the <a href="https://infinum.com/blog/top-5-android-libraries-every-android-developer-should-know-about/">top five Android libraries</a> we used at the time. However, the Android platform has grown rapidly since then. As you know, in the fast-paced field of software development, things change and evolve at lightning speed. That’s why we’ve decided it’s time for an updated list of the ultimate Android libraries.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-415"
	 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-413"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-414">
	<picture class="image__picture block-media__image-picture">
								
			<source
				srcset=https://infinum.com/uploads/2018/08/top-10-android-libraries-every-android-developer-should-know-about-1-1400x840.webp				media='(max-width: 699px)'
				type=image/webp								height="840"
												width="1400"
				 />
												<img
					src="https://infinum.com/uploads/2018/08/top-10-android-libraries-every-android-developer-should-know-about-1.webp"
					class="image__img block-media__image-img"
					alt=""
										height="846"
															width="1410"
										loading="lazy"
					 />
					</picture>

	</figure></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'
	>
	Without further ado, let’s jump right into our list. We’ll kick off with one of the oldest, and I’d argue, one of the most useful Android libraries.</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-heading" data-id="es-419">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-420'
	>
	1. <a href="http://square.github.io/retrofit/">Retrofit</a></h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-424"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-422">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-423'
	>
	Retrofit is type-safe HTTP client that allows you to define your REST API as an interface. You can manipulate the API requests’ body, headers, query parameters, and much more via annotations, which makes everything clean and simple. Retrofit also allows synchronous and asynchronous API calls execution.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-426"
	 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">interface ApiService </span><span class="token">{
</span></span><span class="line"><span class="token">  </span><span class="token" style="color: #6f42c1;">@GET</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">movie/{id}</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">)
</span></span><span class="line"><span class="token">  </span><span class="token">fun getMovieDetails</span><span class="token">(</span><span class="token" style="color: #6f42c1;">@Path</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">id</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">) id</span><span class="token">: </span><span class="token" style="color: #6f42c1;">String</span><span class="token">) </span><span class="token">: </span><span class="token" style="color: #6f42c1;">Call</span><span class="token">&lt;</span><span class="token" style="color: #6f42c1;">MovieDetails</span><span class="token">&gt;
</span></span><span class="line"><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-429"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-427">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-428'
	>
	To top it off, Retrofit offers a separate Rx module. If you are using Rx, this module returns your API call as an Observable so you are able to chain it with the rest of your application. These are just a few of the many reasons we at Infinum still love and use Retrofit.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-432"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-430">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-431'
	>
	2. <a href="https://github.com/square/moshi">Moshi</a></h2></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-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-434'
	>
	Moshi is a library that converts JSON into Java and Kotlin models. A lot of people refer to the Moshi as GSON 3.0. This library is superior to GSON in several aspects: [it’s faster](https://stackoverflow.com/a/43578738/3920456 “StackOverflow”), it includes Kotlin support, it’s maintained, it throws predictable exceptions, and it doesn’t use broken <code>DateTime</code> adapter by default. Plus, converting JSON to Java model (and vice-versa) is straightforward with Moshi.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-437"
	 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">moshi </span><span class="token" style="color: #d73a49;">=</span><span class="token"> Moshi</span><span class="token">.Builder</span><span class="token">()</span><span class="token">.build</span><span class="token">()
</span></span><span class="line"><span class="token">val </span><span class="token">jsonAdapter </span><span class="token" style="color: #d73a49;">=</span><span class="token"> moshi</span><span class="token">.adapter</span><span class="token">(Model</span><span class="token">::</span><span class="token" style="color: #6f42c1;">class</span><span class="token">.java)
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #6a737d;">/*</span><span class="token" style="color: #6a737d;"> JSON to Model </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">val </span><span class="token">model </span><span class="token" style="color: #d73a49;">=</span><span class="token"> jsonAdapter</span><span class="token">.fromJson</span><span class="token">(json)
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #6a737d;">/*</span><span class="token" style="color: #6a737d;"> Model to JSON </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">val </span><span class="token">json </span><span class="token" style="color: #d73a49;">=</span><span class="token"> jsonAdapter</span><span class="token">.toJson</span><span class="token">(model)
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-440"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-438">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-439'
	>
	We also admire Moshi for its JSON API support. JSON API is the specification for building APIs, and a lot of APIs we work with are written using that specification. Moshi JSON API makes our life easier because it converts JSON API responses into meaningful Java objects. Retrofit also has support for Moshi, and it all just clicks together.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-443"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-441">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-442'
	>
	3. <a href="https://github.com/jgilfelt/chuck">Chuck</a></h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-446"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-444">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-445'
	>
	Chuck is an HTTP inspector for Android that allows you to dig into your application’s HTTP history on your mobile phone. The HTTP log is displayed as a notification, which you can expand to open full Chuck UI. When you use Chuck, your quality assurance team will applaud you as they will be able to see whether an issue persists on Android or the backend side. This library is also sometimes more useful than logcat. That’s because your HTTP history persists even if the app is killed, while logcat sometimes cleans itself after the app is restarted.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-449"
	 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-447"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-auto" data-id="es-448">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2021/12/top-10-android-libraries-every-android-developer-should-know-about-2.gif"
					class="image__img block-media__image-img"
					alt=""
										height="512"
															width="308"
										loading="lazy"
					 />
					</picture>

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

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-452"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-450">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-451'
	>
	4. <a href="https://bumptech.github.io/glide/">Glide</a></h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-455"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-453">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-454'
	>
	As you probably know by now, Android image loading and handling API is terrible. It’s a nightmare to even resize an image without getting “OutOfMemoryException.” Glide is an image loading library that exposes a nice API, allowing you to transform your image however you want.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-457"
	 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">GlideApp</span><span class="token">.with</span><span class="token">(context)
</span></span><span class="line"><span class="token">  </span><span class="token">.load</span><span class="token">(url)
</span></span><span class="line"><span class="token">  </span><span class="token">.centerCrop</span><span class="token">()
</span></span><span class="line"><span class="token">  </span><span class="token">.placeholder</span><span class="token">(R.drawable.ic_placeholder)
</span></span><span class="line"><span class="token">  </span><span class="token">.into</span><span class="token">(imageView)
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-460"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-458">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-459'
	>
	This library allows you to easily load a remote image into your ImageView, define fallback images, cache and scale your images, and much more. Try accomplishing all this without Glide, and you will quickly understand why this has become our core library. It even supports some common transformations like a circular image out of the box.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-463"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-461">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-462'
	>
	5. <a href="https://github.com/JakeWharton/ThreeTenABP">ThreeTen</a></h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-466"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-464">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-465'
	>
	ThreeTen is a date and time handling library for Android. It is a backport of JSR-310, which was included in Java 8 as a standard “java.time.*” package. We love this library because the standard Android Calendar API is a nightmare to work with.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-468"
	 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" style="color: #6a737d;">/*</span><span class="token" style="color: #6a737d;"> Current DateTime </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">LocalDateTime</span><span class="token">.now</span><span class="token">()
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #6a737d;">/*</span><span class="token" style="color: #6a737d;"> String to DateTime </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">val </span><span class="token">localDateTimeString </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">2011-12-03T10:15:30</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">
</span></span><span class="line"><span class="token">val </span><span class="token">localDateTime </span><span class="token" style="color: #d73a49;">=</span><span class="token"> LocalDateTime</span><span class="token">.parse</span><span class="token">(localDateTimeString)
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-471"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-469">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-470'
	>
	ThreeTen is much smaller than JodaTime in terms of method count and binary size. Its API is also more concise than JodaTime’s API. If you are currently using JodaTime, it is probably not worth the switch to ThreeTen. However, if you are starting a new project, I strongly suggest you try out ThreeTen instead.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-474"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-472">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-473'
	>
	6. <a href="https://github.com/JakeWharton/timber">Timber</a></h2></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'
	>
	Timber is powerful, yet simple, logging library built on top of Android “Log” class. It allows you to easily turn logging on or off. It also offers nice support for logging formatted strings and exceptions. Because of all these benefits, Timber is one of the core libraries we use on almost all our Android projects.</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-kotlin github-light" data-language="kotlin" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #6a737d;">/*</span><span class="token" style="color: #6a737d;"> Log error </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">Timber</span><span class="token">.e</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Error happened</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">)
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #6a737d;">/*</span><span class="token" style="color: #6a737d;"> Log warning with params </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">Timber</span><span class="token">.w</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Your variable value is %s</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">, variable)
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #6a737d;">/*</span><span class="token" style="color: #6a737d;"> Log exception </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">try</span><span class="token"> {
</span></span><span class="line"><span class="token">  </span><span class="token" style="color: #d73a49;">..</span><span class="token">.
</span></span><span class="line"><span class="token">} </span><span class="token" style="color: #d73a49;">catch</span><span class="token"> (e</span><span class="token">: </span><span class="token" style="color: #6f42c1;">Exception</span><span class="token">) {
</span></span><span class="line"><span class="token">  Timber</span><span class="token">.e</span><span class="token">(e)
</span></span><span class="line"><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-482"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-480">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-481'
	>
	7. <a href="https://developer.android.com/training/data-storage/room/">Room</a></h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-485"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-483">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-484'
	>
	Room is an official Android ORM, and there are multiple reasons for that status. This library features a beautiful API that is similar to Retrofit. It also relies heavily on annotations and standard SQL syntax.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-487"
	 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" style="color: #6f42c1;">@Dao</span><span class="token">
</span></span><span class="line"><span class="token">interface MovieDao </span><span class="token">{
</span></span><span class="line"><span class="token">  </span><span class="token" style="color: #6f42c1;">@Query</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">SELECT details FROM movies WHERE id = :id</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">)
</span></span><span class="line"><span class="token">  </span><span class="token">fun getMovieDetails</span><span class="token">(id</span><span class="token">: </span><span class="token" style="color: #6f42c1;">String</span><span class="token">)</span><span class="token">: </span><span class="token" style="color: #6f42c1;">MovieDetails</span><span class="token">
</span></span><span class="line"><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-490"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-488">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-489'
	>
	Additionally, Room includes out-of-the-box support for Rx and “LiveData,” so you can decide to use it however you like. The main benefit Room offers over other ORMs is its simplicity. Other ORMs come with much more complex APIs that require you to read all the documentation thoroughly before you can use them. Thanks to its standard SQL syntax, Room is incredibly easy to understand, allowing you to dive in without spending hours reading the documentation.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-493"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-491">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-492'
	>
	8. <a href="https://github.com/ReactiveX/RxJava">RxJava</a></h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-496"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-494">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-495'
	>
	RxJava is a Java implementation of ReactiveX API that allows you to chain asynchronous tasks and events into observable sequences. Users expect <a href="https://infinum.com/mobile-web-apps/">modern applications</a> to display data in real-time. In other words, they want to see data updated automatically. That’s where RxJava can help.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-499"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-497">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-498'
	>
	When fetching real-time data, it becomes extremely difficult to merge multiple API calls, switch threads, and handle errors. This is where RxJava shines, and it is the reason why we started using this library. I will admit that RxJava is confusing and hard to learn, but it is completely worth your time. Even after we switched to Kotlin, we continued to use RxJava because of its benefits. Together with Kotlin, the API is even better thanks to the additional extension functions.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-501"
	 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">Single</span><span class="token">.zip</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;"> Execute first API call and retry twice if it fails </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">  apiService</span><span class="token">.getMovieActors</span><span class="token">(id)</span><span class="token">.retry</span><span class="token">(</span><span class="token" style="color: #005cc5;">2</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;"> Execute second API call and retry twice if it fails </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">  apiService</span><span class="token">.getMovieDetails</span><span class="token">(id)</span><span class="token">.retry</span><span class="token">(</span><span class="token" style="color: #005cc5;">2</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;"> Receive successful results and merge them into single model </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">  </span><span class="token">BiFunction</span><span class="token">&lt;</span><span class="token" style="color: #6f42c1;">List</span><span class="token">&lt;</span><span class="token" style="color: #6f42c1;">Actor</span><span class="token">&gt;, </span><span class="token" style="color: #6f42c1;">MovieDetails</span><span class="token">, </span><span class="token" style="color: #6f42c1;">Movie</span><span class="token">&gt;</span><span class="token"> </span><span class="token">{ actors, details </span><span class="token" style="color: #d73a49;">-&gt;</span><span class="token"> </span><span class="token">Movie</span><span class="token">(details, actors) }
</span></span><span class="line"><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;"> Execute API calls on IO thread </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">  </span><span class="token">.subscribeOn</span><span class="token">(Schedulers</span><span class="token">.io</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;"> Receive results on MainThread </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">  </span><span class="token">.observeOn</span><span class="token">(AndroidSchedulers</span><span class="token">.mainThread</span><span class="token">())
</span></span><span class="line"><span class="token">  </span><span class="token">.subscribeBy</span><span class="token">(
</span></span><span class="line"><span class="token">    onError </span><span class="token" style="color: #d73a49;">=</span><span class="token"> { </span><span class="token" style="color: #6a737d;">/*</span><span class="token" style="color: #6a737d;"> Handle error </span><span class="token" style="color: #6a737d;">*/</span><span class="token"> },
</span></span><span class="line"><span class="token">    onSuccess </span><span class="token" style="color: #d73a49;">=</span><span class="token"> { </span><span class="token" style="color: #6a737d;">/*</span><span class="token" style="color: #6a737d;"> Handle full movie data </span><span class="token" style="color: #6a737d;">*/</span><span class="token"> }
</span></span><span class="line"><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-504"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-502">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-503'
	>
	Try accomplishing something similar to the above with plain Java. I dare you.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-507"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-505">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-506'
	>
	9. <a href="https://github.com/android/android-ktx">Android KTX</a></h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-510"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-508">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-509'
	>
	Android KTX is a set of Kotlin extensions that wraps Android API, making it more user-friendly. The whole purpose of this library is to make Android API more pleasant to use. It adds a lot of methods and cool new features of Kotlin, such as named parameters, lambdas, and default parameter values.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-512"
	 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" style="color: #6a737d;">/*</span><span class="token" style="color: #6a737d;"> Display View </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">view.isVisible </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">true</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #6a737d;">/*</span><span class="token" style="color: #6a737d;"> Apply padding to all sides </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">view</span><span class="token">.setPadding</span><span class="token">(padding)
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #6a737d;">/*</span><span class="token" style="color: #6a737d;"> Update padding on any side </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">view</span><span class="token">.updatePadding</span><span class="token">(left </span><span class="token" style="color: #d73a49;">=</span><span class="token"> newPadding, right </span><span class="token" style="color: #d73a49;">=</span><span class="token"> newPadding)
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #6a737d;">/*</span><span class="token" style="color: #6a737d;"> Quick Toast </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">toast</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Display test toast</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">)
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #6a737d;">/*</span><span class="token" style="color: #6a737d;"> New way to create bundle </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">bundleOf </span><span class="token">{
</span></span><span class="line"><span class="token">  </span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">key1</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> to item1
</span></span><span class="line"><span class="token">  </span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">key2</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> to item2
</span></span><span class="line"><span class="token">}
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #6a737d;">/*</span><span class="token" style="color: #6a737d;"> Better way to use SharedPreferences </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">sharedPreferences</span><span class="token">.edit </span><span class="token">{
</span></span><span class="line"><span class="token">  </span><span class="token">putString</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">key1</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">, </span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">value1</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">)
</span></span><span class="line"><span class="token">  </span><span class="token">putString</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">key2</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">, </span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">value2</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">)
</span></span><span class="line"><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-515"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-513">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-514'
	>
	Android KTX features a lot of extension functions, and the only way to learn about them is by digging through library code to see how they improved Android API. This library changes the way we consume Android API—and for that reason, it should become the core of every Android application written in Kotlin.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-518"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-516">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-517'
	>
	10. <a href="https://google.github.io/dagger/">Dagger</a></h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-521"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-519">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-520'
	>
	Without Dagger, our Top 10 libraries list would be incomplete. Dagger is a fully static, compile-time dependency injection framework. Similar to RxJava, Dagger is really hard to understand (it took me a while to understand their CoffeeMaker example) but completely worth your time and effort.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-524"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-522">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-523'
	>
	Dependency injection is a way to provide smaller components to another model and glue them together with minimum effort. For example, if you have a Car model, you can provide tires to it and easily replace the tire implementation in the future—all without having to change a single line in your Car model.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-527"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-525">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-526'
	>
	When developing larger applications, you don’t want to handle dependency injection yourself because the code will grow quickly and become extremely difficult to maintain. Dagger helps you avoid this because it creates your dependency injection graph in compile-time via annotation processing.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-530"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-528">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-529'
	>
	In the last year, Google also included a separate Dagger Android module, which allows you to write less boilerplate code and inject dependencies easier.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-533"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-531">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-532'
	>
	Conclusion</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-536"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-534">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-535'
	>
	So, there you have it: Our 2018 List of Top 10 Android Libraries. These ten superior options make <a href="https://infinum.com/mobile-web-apps/">Android development</a> much more enjoyable for anyone who uses them. Looking for more options? It just so happens that we at Infinum have developed some Android libraries of our own. Check them out:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-539"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-537">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-538'
	>
	<li><a href="https://github.com/infinum/MjolnirRecyclerView" target="_blank" rel="noreferrer noopener">MjolnirRecyclerView</a> &#8211; extends RecyclerView with support for headers, footers, empty view, and DiffUtils</li><li><a href="https://github.com/infinum/Android-Goldfinger" target="_blank" rel="noreferrer noopener">Goldfinger</a> &#8211; simplifies Fingerprint authentication implementation</li><li><a href="https://github.com/infinum/android_dbinspector" target="_blank" rel="noreferrer noopener">DBInspector</a> &#8211; view and share your app databases</li><li><a href="https://github.com/infinum/android-complexify" target="_blank" rel="noreferrer noopener">Complexify</a> &#8211; an easy way to check the quality of a user&#8217;s password</li><li><a href="https://github.com/infinum/Android-Prince-of-Versions" target="_blank" rel="noreferrer noopener">Prince of Versions</a> &#8211; handle app updates inside the app</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-542"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-540">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-541'
	>
	If you want to hear about newest Android libraries, subscribe to our weekly <a href="https://infinum.com/android-sweets/">Android newsletter</a></p></div>	</div>
</div>
</div>		</div>
	</div><p>The post <a href="https://infinum.com/blog/top-10-android-libraries-every-android-developer-should-know-about/">Top 10 Android Libraries Every Android Developer Should Know About</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</content:encoded>
			</item>
					<item>
				<image>
					<url>8057https://infinum.com/uploads/2017/02/share-files-using-fileprovider-0.webp</url>
				</image>
				<title>Share Files Using FileProvider on Android</title>
				<link>https://infinum.com/blog/share-files-using-fileprovider/</link>
				<pubDate>Tue, 31 Jan 2017 15:05:00 +0000</pubDate>
				<dc:creator>Domagoj Korman</dc:creator>
				<guid isPermaLink="false">https://infinum.com/the-capsized-eight/share-files-using-fileprovider/</guid>
				<description>
					<![CDATA[<p>Once you understand how it works, implementing your own FileProvider is truly simple.</p>
<p>The post <a href="https://infinum.com/blog/share-files-using-fileprovider/">Share Files Using FileProvider on Android</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</description>
				<content:encoded>
					<![CDATA[<div
	class="wrapper"
	data-id="es-670"
	 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-545">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-548"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-546">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-547'
	>
	Several weeks ago I was given a task to open an internal PDF file in any PDF reader application on an Android phone. I thought it would be straightforward, but things turned out to be complicated. Google’s documentation on FileProvider proved to be confusing and lacking concrete examples. Nevertheless, I knew I had to use ContentProvider to tackle the issue.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-551"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-549">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-550'
	>
	What is FileProvider</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-554"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-552">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-553'
	>
	ContentProvider is an Android component which encapsulates data and provides it to other applications. It is required only if you need to share data between multiple applications. For example, the contacts data is shared with other applications using ContactsProvider which is a subclass of ContentProvider.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-557"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-555">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-556'
	>
	FileProvider is a subclass of ContentProvider. While ContentProvider is a component that enables you to securely share any kind of data, FileProvider is used specifically for sharing the app’s internal files. The FileProvider class is part of the v4 Support Library so make sure to include it in your project.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-560"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-558">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-559'
	>
	To make FileProvider work follow these three steps:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-563"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-561">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-562'
	>
	<li>Define the FileProvider in your AndroidManifest file</li><li>Create an XML file that contains all paths that the FileProvider will share with other applications</li><li>Bundle a valid URI in the Intent and activate it</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-566"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-564">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-565'
	>
	Defining a FileProvider</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-569"
	 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-567"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-568">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2017/02/share-files-using-fileprovider-1.webp"
					class="image__img block-media__image-img"
					alt=""
										height="400"
															width="960"
										loading="lazy"
					 />
					</picture>

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

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-572"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-570">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-571'
	>
	To define a FileProvider inside your AndroidManifest you need to be familiar with these attributes and elements:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-575"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-573">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-574'
	>
	<li>android:authorities</li><li>android:exported</li><li>android:grantUriPermissions</li><li>android:name</li><li>&lt;meta-data&gt; subelement</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-578"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-576">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-577'
	>
	If all of these seem extremely familiar, you’ll find your way around FileProvider a bit easier, otherwise I’ve prepared a detailed description of each attribute and its purpose.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-581"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-579">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-580'
	>
	android:authorities</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-584"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-582">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-583'
	>
	You <strong>must</strong> define at least one unique authority. Android System keeps a list of all providers and it distinguishes them by authority. Authority defines the FileProvider just like the application ID defines an Android application.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-587"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-585">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-586'
	>
	In general, the Android System uses a specific URI scheme for ContentProviders. The scheme is defined as <strong>content://&lt;authority&gt;/&lt;path&gt;</strong> so the system will know which ContentProvider is requested by matching the URI’s authority with the ContentProvider’s authority.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-590"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-588">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-589'
	>
	android:exported</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-593"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-591">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-592'
	>
	This attribute can easily be misused because its name is misleading. To understand this attribute, think of your FileProvider as a room with locked doors. If you set the value to true, you’ve basically opened your doors to everyone. Everything will work from your point of view, but you’ve just created a huge security issue as every other app will be able to use your FileProvider without being given permission.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-596"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-594">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-595'
	>
	This can teach you to <strong>never program by coincidence</strong> and always be aware of the side-effects of your code. Also, always define this attribute because the default value on SDK 16 and lower is <strong>true</strong>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-599"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-597">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-598'
	>
	android:grantUriPermissions</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-602"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-600">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-601'
	>
	If we continue thinking of a FileProvider as a locked room, then this attribute is used to give a temporary one-time key to an external app. This attribute allows you to securely share your app’s internal storage. All you have to do is add either FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION to the Intent that activates the component to open your app’s internal file. To use these flags set their value to <strong>true</strong>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-605"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-603">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-604'
	>
	The &lt;provider&gt; element can also have &lt;grant-uri-permission&gt; subelements. The only difference being that by using the attribute you can share anything inside your app’s internal storage, while subelements allow you to choose a specific data subset for sharing. To use subelements instead, set the value to <strong>false</strong>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-608"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-606">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-607'
	>
	&lt;meta-data&gt; subelement</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-611"
	 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-609"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-610">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2017/02/share-files-using-fileprovider-2.webp"
					class="image__img block-media__image-img"
					alt=""
										height="400"
															width="960"
										loading="lazy"
					 />
					</picture>

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

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-614"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-612">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-613'
	>
	This subelement must be defined when using the FileProvider. You have to define a path to the XML file which contains all data paths your FileProvider can share with external apps.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-617"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-615">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-616'
	>
	The XML file must have the \&lt;paths&gt; element as its root. The \&lt;paths&gt; element must have at least one subelement which can be one of the following:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-620"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-618">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-619'
	>
	<li>&lt;files-path/&gt; &#8211; internal app storage, Context#getFilesDir()</li><li>&lt;cache-path/&gt; &#8211; internal app cache storage, Context#getCacheDir()</li><li>&lt;external-path/&gt; &#8211; public external storage, Environment.getExternalStorageDirectory()</li><li>&lt;external-files-path/&gt; &#8211; external app storage, Context#getExternalFilesDir(null)</li><li>&lt;external-cache-path/&gt; &#8211; external app cache storage, Context#getExternalCacheDir()</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-623"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-621">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-622'
	>
	You might have noticed that they vary according to the app’s directory which they define.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-626"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-624">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-625'
	>
	Each element must have a path and a name attribute. The path attribute defines the subdirectory you want to share and it <strong>does not</strong> support wildcards. The name attribute is used for security reasons and it will replace your subdirectory name with its value.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-629"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-627">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-628'
	>
	android:name</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-632"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-630">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-631'
	>
	We set this value to <strong>android.support.v4.content.FileProvider</strong>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-635"
	 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-633"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-634">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2017/02/share-files-using-fileprovider-3.webp"
					class="image__img block-media__image-img"
					alt=""
										height="400"
															width="960"
										loading="lazy"
					 />
					</picture>

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

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-638"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-636">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-637'
	>
	Once you have defined a FileProvider in your AndroidManifest file, you are finally ready to use it. To share a file you have to create an Intent and give it a valid URI. The URI is generated using the FileProvider class.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-641"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-639">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-640'
	>
	Code implementation</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-644"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-642">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-643'
	>
	AndroidManifest.xml</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-646"
	 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;">provider</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.support.v4.content.FileProvider</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;">grantUriPermissions</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><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">
</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;">authorities</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">${applicationId}</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><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;">android.support.FILE_PROVIDER_PATHS</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;">resource</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">@xml/file_provider_paths</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><span class="line"><span class="token">&lt;/</span><span class="token" style="color: #22863a;">provider</span><span class="token">&gt;</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-649"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-647">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-648'
	>
	Notice that I’m using the app’s ID for authority. That’s because I have multiple flavors in the project and they can be installed on the device at the same time. Android System won’t let you install multiple applications with the same FileProvider so each flavor needs an unique authority.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-652"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-650">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-651'
	>
	file_provider_paths.xml</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-654"
	 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;">paths</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;">cache-path</span><span class="token"> </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;">cache</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token" style="color: #6f42c1;">path</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">/</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;">files-path</span><span class="token"> </span><span class="token" style="color: #6f42c1;">name</span><span class="token">=</span><span class="token">”files”</span><span class="token" style="color: #6f42c1;"> pat</span><span class="token">h=</span><span class="token">”/” </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;">paths</span><span class="token">&gt;</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-657"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-655">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-656'
	>
	By defining paths like this, I allow the FileProvider to share all files that are inside the app’s internal cache and files directory.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-660"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-658">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-659'
	>
	Use your FileProvider</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-662"
	 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: #6a737d;">//</span><span class="token" style="color: #6a737d;"> create new Intent</span><span class="token" style="color: #6a737d;">
</span></span><span class="line"><span class="token" style="color: #24292e;">Intent</span><span class="token"> </span><span class="token" style="color: #24292e;">intent</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #d73a49;">new</span><span class="token"> </span><span class="token" style="color: #6f42c1;">Intent</span><span class="token">(</span><span class="token" style="color: #24292e;">Intent</span><span class="token">.</span><span class="token" style="color: #24292e;">ACTION_VIEW</span><span class="token">)</span><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: #6a737d;">//</span><span class="token" style="color: #6a737d;"> set flag to give temporary permission to external app to use your FileProvider</span><span class="token" style="color: #6a737d;">
</span></span><span class="line"><span class="token" style="color: #24292e;">intent</span><span class="token">.</span><span class="token" style="color: #6f42c1;">setFlags</span><span class="token">(</span><span class="token" style="color: #24292e;">Intent</span><span class="token">.</span><span class="token" style="color: #24292e;">FLAG_GRANT_READ_URI_PERMISSION</span><span class="token">)</span><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: #6a737d;">//</span><span class="token" style="color: #6a737d;"> generate URI, I defined authority as the application ID in the Manifest, the last param is file I want to open</span><span class="token" style="color: #6a737d;">
</span></span><span class="line"><span class="token" style="color: #24292e;">String</span><span class="token"> </span><span class="token" style="color: #24292e;">uri</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #24292e;">FileProvider</span><span class="token">.</span><span class="token" style="color: #6f42c1;">getUriForFile</span><span class="token">(</span><span class="token" style="color: #005cc5;">this</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #24292e;">BuildConfig</span><span class="token">.</span><span class="token" style="color: #24292e;">APPLICATION_ID</span><span class="token">,</span><span class="token"> file</span><span class="token">)</span><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: #6a737d;">//</span><span class="token" style="color: #6a737d;"> I am opening a PDF file so I give it a valid MIME type</span><span class="token" style="color: #6a737d;">
</span></span><span class="line"><span class="token" style="color: #24292e;">intent</span><span class="token">.</span><span class="token" style="color: #6f42c1;">setDataAndType</span><span class="token">(</span><span class="token">uri</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">application/pdf</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">)</span><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: #6a737d;">//</span><span class="token" style="color: #6a737d;"> validate that the device can open your File!</span><span class="token" style="color: #6a737d;">
</span></span><span class="line"><span class="token" style="color: #24292e;">PackageManager</span><span class="token"> </span><span class="token" style="color: #24292e;">pm</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #6f42c1;">getActivity</span><span class="token">(</span><span class="token">)</span><span class="token">.</span><span class="token" style="color: #6f42c1;">getPackageManager</span><span class="token">(</span><span class="token">)</span><span class="token">;</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">if</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #24292e;">intent</span><span class="token">.</span><span class="token" style="color: #6f42c1;">resolveActivity</span><span class="token">(</span><span class="token">pm</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: #005cc5;">null</span><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #6f42c1;">startActivity</span><span class="token">(</span><span class="token">intent</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><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-665"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-663">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-664'
	>
	Once you understand how it works, implementing your own FileProvider is truly simple.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-668"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-666">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-667'
	>
	The complexity of the problem is not the code itself, but the documentation and understanding of how everything is interconnected. I hope this article will help you implement the FileProvider for your own use case and make the official documentation easier to read.</p></div>	</div>
</div>
</div>		</div>
	</div><p>The post <a href="https://infinum.com/blog/share-files-using-fileprovider/">Share Files Using FileProvider on Android</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</content:encoded>
			</item>
		
	</channel>
</rss>