<?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/mladen-rakonjac/feed/" rel="self" type="application/rss+xml" />
		<link></link>
		<description>Building digital products</description>
		<lastBuildDate>Wed, 22 Apr 2026 14:53:03 +0000</lastBuildDate>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>

					<item>
				<image>
					<url>19257545https://infinum.com/uploads/2024/09/AI-invoice-min.webp</url>
				</image>
				<title>Want an AI Invoice Processing Solution? Create It Yourself</title>
				<link>https://infinum.com/blog/ai-invoice-processing/</link>
				<pubDate>Wed, 25 Sep 2024 15:42:01 +0000</pubDate>
				<dc:creator>Mladen Rakonjac</dc:creator>
				<guid isPermaLink="false">https://infinum.com/?p=19257545</guid>
				<description>
					<![CDATA[<p>Explore our step-by-step guide for building a simple solution for AI invoice processing using Vertex AI and App Script. </p>
<p>The post <a href="https://infinum.com/blog/ai-invoice-processing/">Want an AI Invoice Processing Solution? Create It Yourself</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</description>
				<content:encoded>
					<![CDATA[<div
	class="wrapper"
	data-id="es-178"
	 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-typography" data-id="es-93">
	<p	class='typography typography--size-36-text js-typography block-typography__typography'
	data-id='es-94'
	>
	<strong>Automating routine office tasks with AI doesn’t have to be complicated. Learn how we built a simple AI-powered invoice processing system using Vertex AI and App Script. </strong></p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-98"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-96">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-97'
	>
	A year ago, we conducted <a href="https://infinum.com/decoding-the-digital-ai-revolution/" target="_blank" rel="noreferrer noopener">research with over 1,000 digital leaders</a> and found that many businesses are eager to <a href="https://infinum.com/blog/ai-automation/">integrate artificial intelligence into their operations</a> but often don’t know where to start. While generative AI is a powerful technology, that doesn’t mean its use is reserved only for groundbreaking innovation. If you want to find a use case for it, why not start small? </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-101"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-99">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-100'
	>
	It’s precisely what we did at our Montenegrin office. By using existing tools and a little creative thinking, we created a simple AI invoice processing solution, successfully streamlining a repetitive task every office encounters.</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-typography" data-id="es-102">
	<h2	class='typography typography--size-52-default js-typography block-typography__typography'
	data-id='es-103'
	>
	The invoice-processing challenge – a case for automation</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-107"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-105">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-106'
	>
	Every office is met with certain tedious, manual tasks that demand time and attention, particularly in accounts payable. If you&#8217;re aiming to optimize processes using artificial intelligence, this is the ideal place to start.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-110"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-108">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-109'
	>
	Most offices receive multiple invoices daily, and handling them usually involves manual data entry, which is both time-consuming and prone to errors.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-113"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-111">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-112'
	>
	Here’s how the process used to work in our Montenegrin office:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-116"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-114">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-115'
	>
	<li>Receive invoices in either paper or digital format</li><li>If the invoice is printed, digitize it</li><li>Rename the file so it’s easily searchable, including the sender’s name, invoice date, and amount</li><li>Upload the invoice to Google Drive, organized by month and year</li><li>Create a payment order</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-119"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-117">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-118'
	>
	Performing these steps for every single invoice takes time, and to make things even more complicated, invoices come in many shapes and sizes. Different companies use different invoice formats, and the relevant information is often found in completely different places. That’s why AI-based invoice processing offers a great solution.</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-typography" data-id="es-120">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-121'
	>
	To tackle this challenge, we developed a simple script using Vertex AI and App Script. This AI-powered invoice processing system transformed our entire payable workflow. Now, there&#8217;s no need for manual data entry– we simply forward any invoice we receive to a designated email address, and the process is automated from there. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-128"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<a	class="card-simple js-card-simple card-simple--is-ad block-card__card-simple card-simple--has-link js-card-simple-link card-simple__content-align--left"
	data-id="es-123"
	 href='https://infinum.com/artificial-intelligence/agent-development/'>

	
	
	<div class="card-simple__content">
		<div class="card-simple__heading-wrap">
			<p	class='typography typography--size-24-text js-typography card-simple__heading'
	data-id='es-124'
	>
	Curious about how artificial intelligence can streamline your business processes? Take the first step with our 14-day Agentic Sprint. It’s a fast, focused way to turn early ideas into validated AI prototypes—complete with clear business logic, system design, and intuitive UX.</p>		</div>

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

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-131"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-129">
	<h2	class='typography typography--size-52-default js-typography block-typography__typography'
	data-id='es-130'
	>
	AI invoice processing with Vertex AI – no need to reinvent the wheel</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-134"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-132">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-133'
	>
	First of all, you might wonder if Optical Character Recognition (OCR) would be enough for invoice processing automation. OCR is a technology that “reads” scanned paper documents or images taken by a digital camera, converting them into editable and searchable data. However, while OCR can extract data from a file, it&#8217;s not perfect for invoice data extraction because it falls short in identifying specific variables like vendor details or invoice amounts.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-137"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-135">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-136'
	>
	This is where natural language processing (NLP) comes in. By combining OCR with NLP, we can extract the required data regardless of the invoice format. The data extracted by OCR can be passed to a Large Language Model (LLM), which we&#8217;ll prompt to find the specific information we need. You may think this would require a specially trained model, but unless you receive thousands of invoices daily, there’s a fast and inexpensive out-of-the-box solution that does the trick. </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-typography" data-id="es-138">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-139'
	>
	The perfect fit for our use case was Vertex AI – Google Cloud’s unified platform that simplifies access to powerful LLMs, OCR capabilities, and multimodal AI solutions. By leveraging <a href="https://infinum.com/machine-learning-development-services/">machine learning</a>, VertexAI enables accurate data extraction and processing, and in combination with App Script, it offers a powerful way to automate tasks. n combination with App Script, it offers a powerful way to automate tasks. </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-typography" data-id="es-141">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-142'
	>
	<em>Note: We’ve chosen these tools because we use Google Workspace, but the same principle could easily be applied to a different ecosystem. For example, you could go with Microsoft Power Automate and OpenAI API if you use Microsoft 365.</em></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-typography" data-id="es-144">
	<h2	class='typography typography--size-52-default js-typography block-typography__typography'
	data-id='es-145'
	>
	Creating the script</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-typography" data-id="es-147">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-148'
	>
	The first step was to create an <a href="https://script.google.com/" target="_blank" rel="noreferrer noopener">App Script</a> with a trigger that runs every hour. If you would like to track your script on git, we recommend using <a href="https://github.com/google/clasp" target="_blank" rel="noreferrer noopener">clasp</a> (Command Line Apps Script Projects), a tool that helps you work on Apps Script projects locally. </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-typography" data-id="es-150">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-151'
	>
	For this purpose, we created a special email list, <a href="mailto:montenegro.invoices.import@infinum.com" target="_blank" rel="noreferrer noopener">montenegro.invoices.import@infinum.com</a>. All incoming invoices should be sent as attachments to this list. The hourly script we’ve created will search for each email thread sent to it. For increased security, we can also limit our search only to emails sent from specific email addresses. That way, we’ll only get invoices from senders we know and trust. In this example, we’ll use the address approved.sender@infinum.com.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-154"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-javascript github-light" data-language="javascript" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">function</span><span class="token"> </span><span class="token" style="color: #6f42c1;">forwardInvoices</span><span class="token">(</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: #d73a49;">const</span><span class="token"> </span><span class="token" style="color: #005cc5;">threads</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #24292e;">GmailApp</span><span class="token">.</span><span class="token" style="color: #6f42c1;">search</span><span class="token">(</span><span class="token" style="color: #032f62;">&#039;</span><span class="token" style="color: #032f62;">from:approved.sender@infinum.com to: montenegro.invoices.import@infinum.com AND NOT label:done</span><span class="token" style="color: #032f62;">&#039;</span><span class="token">)</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">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-157"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-155">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-156'
	>
	We should then check for any messages in the thread:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-159"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-javascript github-light" data-language="javascript" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">for</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #24292e;">i</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">0</span><span class="token">;</span><span class="token"> </span><span class="token" style="color: #24292e;">i</span><span class="token"> </span><span class="token" style="color: #d73a49;">&lt;</span><span class="token"> </span><span class="token" style="color: #24292e;">threads</span><span class="token">.</span><span class="token" style="color: #005cc5;">length</span><span class="token">;</span><span class="token"> </span><span class="token" style="color: #24292e;">i</span><span class="token" style="color: #d73a49;">++</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: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #24292e;">thread</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #24292e;">threads</span><span class="token">[</span><span class="token" style="color: #24292e;">i</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">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">i</span><span class="token" style="color: #d73a49;">f</span><span class="token"> (</span><span class="token" style="color: #24292e;">thread</span><span class="token">.</span><span class="token" style="color: #6f42c1;">getMessages</span><span class="token">(</span><span class="token">)</span><span class="token" style="color: #005cc5;">.lengt</span><span class="token">h</span><span class="token"> </span><span class="token" style="color: #d73a49;">==</span><span class="token"> </span><span class="token" style="color: #005cc5;">0</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: #24292e;">Logger</span><span class="token">.</span><span class="token" style="color: #6f42c1;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">No messages in thread</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">     </span><span class="token" style="color: #d73a49;">continue</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #6a737d;">//</span><span class="token" style="color: #6a737d;">there is a message </span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-162"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-160">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-161'
	>
	In the next step, we check if the message contains an attachment.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-164"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-javascript github-light" data-language="javascript" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #24292e;">attachments</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #24292e;">message</span><span class="token">.</span><span class="token" style="color: #6f42c1;">getAttachments</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;">attachments</span><span class="token">.</span><span class="token" style="color: #005cc5;">length</span><span class="token"> </span><span class="token" style="color: #d73a49;">==</span><span class="token"> </span><span class="token" style="color: #005cc5;">0</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: #24292e;">Logger</span><span class="token">.</span><span class="token" style="color: #6f42c1;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">There is no attachment </span><span class="token" style="color: #032f62;">&quot;</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">      </span><span class="token" style="color: #6f42c1;">replyError</span><span class="token">(</span><span class="token" style="color: #032f62;">`</span><span class="token" style="color: #032f62;">Please send an email with an attachment</span><span class="token" style="color: #032f62;">`</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #24292e;">thread</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token"> </span><span class="token" style="color: #d73a49;">else</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: #24292e;">Logger</span><span class="token">.</span><span class="token" style="color: #6f42c1;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Wohoo!There is an attachment</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">.</span><span class="token">.</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-167"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-165">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-166'
	>
	We save the script and run it. If there are emails matching the conditions, the logs will show the message “<strong>Wohoo! There is an attachment.” </strong></p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-170"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-168">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-169'
	>
	When the script finds an invoice in the attachment, it sends it to Vertex AI through the API, so we can use a prompt to analyze it.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-173"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-171">
	<h3	class='typography typography--size-36-text js-typography block-typography__typography'
	data-id='es-172'
	>
	Using the Vertex AI API</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-176"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-174">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-175'
	>
	First, you will need to visit <a href="https://console.cloud.google.com/" target="_blank" rel="noreferrer noopener">Google Cloud Console</a> and create a new project where you will enable the Vertex AI API and App Script API. When you create the project, you should go to Vertex → Vertex AI → Vertex AI Studio → Multimodal.</p></div>	</div>
</div>
</div>		</div>
	</div>

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

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-180">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2024/09/Extract.webp"
					class="image__img block-media__image-img"
					alt=""
										height="669"
															width="1244"
										loading="lazy"
					 />
					</picture>

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

<div
	class="wrapper"
	data-id="es-271"
	 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-182">
	

</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-185"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-183">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-184'
	>
	The good thing about Vertex AI is that it has a user-friendly interface and provides a great environment for testing your prompts on test files. You can also choose from different models – in our case, we used <strong>gemini-1.5-flash-001</strong>. Once you are happy with the results, you can click <strong>Get code</strong> and get an example request that replicates what you did in the UI. Examples are available for Python, Node.JS, Java, and CURL. There&#8217;s no option for App Script, but Node.JS will be helpful in our scenario.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-188"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-186">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-187'
	>
	A URL for the request to the Vertex AI should look something like this:</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-code">
	<pre class="phiki language-javascript github-light" data-language="javascript" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #005cc5;">API_ENDPOINT</span><span class="token"> </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;">us-central1-aiplatform.googleapis.com</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #005cc5;">PROJECT_ID</span><span class="token"> </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;">someProjectID</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #005cc5;">LOCATION_ID</span><span class="token"> </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;">us-central1</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #005cc5;">MODEL_ID</span><span class="token"> </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;">gemini-1.5-flash-001</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #24292e;">url</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #032f62;">`</span><span class="token" style="color: #032f62;">https://</span><span class="token" style="color: #032f62;">${</span><span class="token" style="color: #005cc5;">API_ENDPOINT</span><span class="token" style="color: #032f62;">}</span><span class="token" style="color: #032f62;">/v1/projects/</span><span class="token" style="color: #032f62;">${</span><span class="token" style="color: #005cc5;">PROJECT_ID</span><span class="token" style="color: #032f62;">}</span><span class="token" style="color: #032f62;">/locations/</span><span class="token" style="color: #032f62;">${</span><span class="token" style="color: #005cc5;">LOCATION_ID</span><span class="token" style="color: #032f62;">}</span><span class="token" style="color: #032f62;">/publishers/google/models/</span><span class="token" style="color: #032f62;">${</span><span class="token" style="color: #005cc5;">MODEL_ID</span><span class="token" style="color: #032f62;">}</span><span class="token" style="color: #032f62;">:g enerateContent</span><span class="token" style="color: #032f62;">`</span><span class="token">;</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-193"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-191">
	<p	class='typography typography--size-20-text-roman js-typography block-typography__typography'
	data-id='es-192'
	>
	Here, projectID is the ID of your Google Cloud Console project.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-196"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-194">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-195'
	>
	To be able to pass the file to Vertex AI for analysis, we will need to convert our PDF file to Base64.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-198"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-javascript github-light" data-language="javascript" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token"> </span><span class="token" style="color: #6a737d;">//</span><span class="token" style="color: #6a737d;"> Read the file from attachments</span><span class="token">
</span></span><span class="line"><span class="token"> </span><span class="token" style="color: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #24292e;">blob</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #24292e;">attachments</span><span class="token">[</span><span class="token" style="color: #005cc5;">0</span><span class="token">]</span><span class="token">.</span><span class="token" style="color: #6f42c1;">copyBlob</span><span class="token">(</span><span class="token">)</span><span class="token">.</span><span class="token" style="color: #6f42c1;">getAs</span><span class="token">(</span><span class="token" style="color: #24292e;">MimeType</span><span class="token" style="color: #005cc5;">.PD</span><span class="token">F</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: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #24292e;">pdfBase64</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #24292e;">Utilities</span><span class="token">.</span><span class="token" style="color: #6f42c1;">base64Encode</span><span class="token">(</span><span class="token" style="color: #24292e;">blob</span><span class="token">.</span><span class="token" style="color: #6f42c1;">getBytes</span><span class="token">(</span><span class="token">)</span><span class="token">)</span><span class="token">;</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-201"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-199">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-200'
	>
	Then, we should create an API request:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-203"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-javascript github-light" data-language="javascript" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #24292e;">jsonRequestBody</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #6f42c1;">getGeminiRequest</span><span class="token">(</span><span class="token" style="color: #24292e;">pdfBase64</span><span class="token">)</span><span class="token">
</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: #d73a49;">function</span><span class="token"> </span><span class="token" style="color: #6f42c1;">getGeminiRequest</span><span class="token">(</span><span class="token" style="color: #e36209;">pdfBase64</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: #d73a49;">return</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">contents</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">{</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token">role</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">user</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">,</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token">parts</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">{</span><span class="token">
</span></span><span class="line"><span class="token">           </span><span class="token">inlineData</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">mimeType</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><span class="line"><span class="token">             </span><span class="token">data</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #24292e;">pdfBase64</span><span class="token">
</span></span><span class="line"><span class="token">           </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">         </span><span class="token">}</span><span class="token">,</span><span class="token">
</span></span><span class="line"><span class="token">         </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">           </span><span class="token">text</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #032f62;">`</span><span class="token" style="color: #032f62;">You are a document entity extraction specialist. Given a document, your task is to extract the text value of the following entities:
</span></span><span class="line"><span class="token" style="color: #032f62;">                COMPANY_NAME is the name of the company issuing the invoice, and it should not be Infinum d.o.o.
</span></span><span class="line"><span class="token" style="color: #032f62;">                YYYY is the year of the invoice transaction in the format YYYY
</span></span><span class="line"><span class="token" style="color: #032f62;">                MM is the month of the invoice transaction in the format MM
</span></span><span class="line"><span class="token" style="color: #032f62;">                DATE_ISSUED is the date of the invoice issuance in the format DD.MM.YYYY (German format)
</span></span><span class="line"><span class="token" style="color: #032f62;">                TOTAL_AMOUNT_TO_PAY is in the format used in Germany without currency symbol or sign (examples &quot;0.000,00&quot; or &quot;00,00&quot;, or &quot;0,00&quot; ) and it should never be in format &quot;00.00&quot;
</span></span><span class="line"><span class="token" style="color: #032f62;">               - The values must only include the text found in the document, but you can format it
</span></span><span class="line"><span class="token" style="color: #032f62;">               - Do not normalize any entity value
</span></span><span class="line"><span class="token" style="color: #032f62;">               - If an entity is not found in the document, set the entity value to null.
</span></span><span class="line"><span class="token" style="color: #032f62;">               - Give just one answer and format it: COMPANY_NAME-YYYY-MM-DATE_ISSUED-TOTAL_AMOUNT_TO_PAY</span><span class="token" style="color: #032f62;">`</span><span class="token">
</span></span><span class="line"><span class="token">         </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token">]</span><span class="token">
</span></span><span class="line"><span class="token">     </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">]</span><span class="token">,</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">generationConfig</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">maxOutputTokens</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">8192</span><span class="token">,</span><span class="token">
</span></span><span class="line"><span class="token">     </span><span class="token">temperature</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">1</span><span class="token">,</span><span class="token">
</span></span><span class="line"><span class="token">     </span><span class="token">topP</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">0</span><span class="token" style="color: #005cc5;">.95</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token"> </span><span class="token">}</span><span class="token">;</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-206"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-204">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-205'
	>
	This API request JSON object contains two important things: the file we want to analyze and the prompt that helps the AI model return the right result.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-209"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-207">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-208'
	>
	It’s very important how you prompt here. To extract the information you need regardless of the invoice format, you must be clear and concise. We need to tell the AI model what role it should play and be precise about the information we expect to get and its format.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-212"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-210">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-211'
	>
	Another part of the URL request is <strong>options.</strong></p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-214"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-javascript github-light" data-language="javascript" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #24292e;">options</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #6f42c1;">getGeminiOptions</span><span class="token">(</span><span class="token" style="color: #24292e;">jsonRequestBody</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: #d73a49;">function</span><span class="token"> </span><span class="token" style="color: #6f42c1;">getGeminiOptions</span><span class="token">(</span><span class="token" style="color: #e36209;">jsonRequestBody</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: #d73a49;">return</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">method</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #032f62;">&#039;</span><span class="token" style="color: #032f62;">post</span><span class="token" style="color: #032f62;">&#039;</span><span class="token">,</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">contentType</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #032f62;">&#039;</span><span class="token" style="color: #032f62;">application/json</span><span class="token" style="color: #032f62;">&#039;</span><span class="token">,</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">payload</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">JSON</span><span class="token">.</span><span class="token" style="color: #6f42c1;">stringify</span><span class="token">(</span><span class="token" style="color: #24292e;">jsonRequestBody</span><span class="token">)</span><span class="token">,</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">headers</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">Authorization</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #032f62;">&#039;</span><span class="token" style="color: #032f62;">Bearer </span><span class="token" style="color: #032f62;">&#039;</span><span class="token"> </span><span class="token" style="color: #d73a49;">+</span><span class="token"> </span><span class="token" style="color: #24292e;">ScriptApp</span><span class="token">.</span><span class="token" style="color: #6f42c1;">getOAuthToken</span><span class="token">(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">}</span><span class="token">,</span><span class="token">
</span></span><span class="line"><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">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-217"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-215">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-216'
	>
	As you can see from the code, we are using ScriptApp OauthToken. Only authorized users can access Vertex AI API.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-220"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-218">
	<h3	class='typography typography--size-36-text js-typography block-typography__typography'
	data-id='es-219'
	>
	Vertex AI API response</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-223"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-221">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-222'
	>
	Let&#8217;s create a request:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-225"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-javascript github-light" data-language="javascript" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #24292e;">response</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #24292e;">UrlFetchApp</span><span class="token">.</span><span class="token" style="color: #6f42c1;">fetch</span><span class="token">(</span><span class="token" style="color: #24292e;">url</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #24292e;">options</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: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #24292e;">responseData</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">JSON</span><span class="token">.</span><span class="token" style="color: #6f42c1;">parse</span><span class="token">(</span><span class="token" style="color: #24292e;">response</span><span class="token">.</span><span class="token" style="color: #6f42c1;">getContentText</span><span class="token">(</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: #24292e;">Logger</span><span class="token">.</span><span class="token" style="color: #6f42c1;">log</span><span class="token">(</span><span class="token" style="color: #24292e;">responseData</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #24292e;">combinedText</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #6f42c1;">getInvoiceNameFromResponse</span><span class="token">(</span><span class="token" style="color: #24292e;">responseData</span><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-228"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-226">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-227'
	>
	The model will return tokens we will need to combine:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-230"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-javascript github-light" data-language="javascript" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">function</span><span class="token"> </span><span class="token" style="color: #6f42c1;">getInvoiceNameFromResponse</span><span class="token">(</span><span class="token" style="color: #e36209;">responseData</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: #d73a49;">if</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #d73a49;">!</span><span class="token" style="color: #24292e;">responseData</span><span class="token" style="color: #24292e;">.candidate</span><span class="token">s</span><span class="token"> </span><span class="token" style="color: #d73a49;">||</span><span class="token"> </span><span class="token" style="color: #d73a49;">!</span><span class="token" style="color: #24292e;">Array</span><span class="token">.</span><span class="token" style="color: #6f42c1;">isArray</span><span class="token">(</span><span class="token" style="color: #24292e;">responseData</span><span class="token" style="color: #24292e;">.candidate</span><span class="token">s</span><span class="token">)</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: #d73a49;">throw</span><span class="token"> </span><span class="token" style="color: #d73a49;">new</span><span class="token"> </span><span class="token" style="color: #6f42c1;">Error</span><span class="token">(</span><span class="token" style="color: #032f62;">&#039;</span><span class="token" style="color: #032f62;">Invalid response structure: candidates is missing or not an array</span><span class="token" style="color: #032f62;">&#039;</span><span class="token">)</span><span class="token">;</span><span class="token">
</span></span><span class="line"><span class="token"> </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token"> </span><span class="token" style="color: #d73a49;">for</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #d73a49;">let</span><span class="token"> </span><span class="token" style="color: #24292e;">candidate</span><span class="token"> </span><span class="token" style="color: #d73a49;">of</span><span class="token"> </span><span class="token" style="color: #24292e;">responseData</span><span class="token" style="color: #24292e;">.candidate</span><span class="token">s</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: #d73a49;">i</span><span class="token" style="color: #d73a49;">f</span><span class="token"> (</span><span class="token" style="color: #24292e;">candidate</span><span class="token" style="color: #24292e;">.conten</span><span class="token">t</span><span class="token"> </span><span class="token" style="color: #d73a49;">&amp;&amp;</span><span class="token"> </span><span class="token" style="color: #24292e;">Array</span><span class="token">.</span><span class="token" style="color: #6f42c1;">isArray</span><span class="token">(</span><span class="token" style="color: #24292e;">candidate</span><span class="token">.cont</span><span class="token" style="color: #24292e;">en</span><span class="token">t</span><span class="token" style="color: #24292e;">.part</span><span class="token">s</span><span class="token">)</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: #d73a49;">for</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #d73a49;">let</span><span class="token"> </span><span class="token" style="color: #24292e;">part</span><span class="token"> </span><span class="token" style="color: #d73a49;">of</span><span class="token"> </span><span class="token" style="color: #24292e;">candidate</span><span class="token">.co</span><span class="token" style="color: #24292e;">nten</span><span class="token">t</span><span class="token" style="color: #24292e;">.part</span><span class="token">s</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: #d73a49;">i</span><span class="token" style="color: #d73a49;">f</span><span class="token"> (</span><span class="token" style="color: #24292e;">part</span><span class="token" style="color: #24292e;">.tex</span><span class="token">t</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: #d73a49;">return</span><span class="token"> </span><span class="token" style="color: #24292e;">part</span><span class="token" style="color: #24292e;">.tex</span><span class="token">t</span><span class="token">.</span><span class="token" style="color: #6f42c1;">trim</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">}</span><span class="token">
</span></span><span class="line"><span class="token">     </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token"> </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token"> </span><span class="token" style="color: #d73a49;">throw</span><span class="token"> </span><span class="token" style="color: #d73a49;">new</span><span class="token"> </span><span class="token" style="color: #6f42c1;">Error</span><span class="token">(</span><span class="token" style="color: #032f62;">&#039;</span><span class="token" style="color: #032f62;">No text found in the response</span><span class="token" style="color: #032f62;">&#039;</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></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-233"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-231">
	<h3	class='typography typography--size-36-text js-typography block-typography__typography'
	data-id='es-232'
	>
	Storing the file to Google Drive</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-236"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-234">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-235'
	>
	With the procedure above, we get the name of the file, and we can save it on Google Drive under the same name.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-238"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-javascript github-light" data-language="javascript" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">  </span><span class="token" style="color: #24292e;">blob</span><span class="token">.</span><span class="token" style="color: #6f42c1;">setName</span><span class="token">(</span><span class="token" style="color: #032f62;">`</span><span class="token" style="color: #032f62;">${</span><span class="token" style="color: #24292e;">combinedText</span><span class="token" style="color: #032f62;">}</span><span class="token" style="color: #032f62;">`</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">  </span><span class="token" style="color: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #24292e;">googleDriveFolder</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #24292e;">DriveApp</span><span class="token">.</span><span class="token" style="color: #6f42c1;">getFolderById</span><span class="token">(</span><span class="token" style="color: #005cc5;">DIR_ID_INVOCIES</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">  </span><span class="token" style="color: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #24292e;">file</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #24292e;">googleDriveFolder</span><span class="token">.</span><span class="token" style="color: #6f42c1;">createFile</span><span class="token">(</span><span class="token" style="color: #24292e;">blob</span><span class="token">)</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-241"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-239">
	<h3	class='typography typography--size-36-text js-typography block-typography__typography'
	data-id='es-240'
	>
	Letting people know about the result</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-244"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-242">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-243'
	>
	You can use the script to automatically reply to the email thread:</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-code">
	<pre class="phiki language-javascript github-light" data-language="javascript" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #24292e;">thread</span><span class="token">.</span><span class="token" style="color: #6f42c1;">replyAll</span><span class="token">(</span><span class="token" style="color: #032f62;">`</span><span class="token" style="color: #032f62;">`</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">name</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #032f62;">&#039;</span><span class="token" style="color: #032f62;">Mladen Rakonjac</span><span class="token" style="color: #032f62;">&#039;</span><span class="token">,</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token">htmlBody</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #032f62;">`</span><span class="token" style="color: #032f62;">AUTOMATIC RESPONSE: Invoice &lt;b&gt; </span><span class="token" style="color: #032f62;">${</span><span class="token" style="color: #24292e;">copySubject</span><span class="token" style="color: #032f62;">}</span><span class="token" style="color: #032f62;"> &lt;/b&gt; is processed successfully.</span><span class="token" style="color: #032f62;">`</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">)</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-249"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-247">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-248'
	>
	Finally, you’ll want to mark the emails that have been processed:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-251"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-javascript github-light" data-language="javascript" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">const</span><span class="token"> </span><span class="token" style="color: #005cc5;">LABEL_DONE</span><span class="token"> </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;">done</span><span class="token" style="color: #032f62;">&quot;</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: #24292e;">thread</span><span class="token">.</span><span class="token" style="color: #6f42c1;">addLabel</span><span class="token">(</span><span class="token" style="color: #24292e;">GmailApp</span><span class="token">.</span><span class="token" style="color: #6f42c1;">getUserLabelByName</span><span class="token">(</span><span class="token" style="color: #005cc5;">LABEL_DONE</span><span class="token">)</span><span class="token">)</span><span class="token">;</span><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-254"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-252">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-253'
	>
	The full script is available on <a href="https://gist.github.com/mladen1nf/ae565a9e12d9927b8ea86070906ac528" target="_blank" rel="noreferrer noopener">GitHub</a>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-257"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-255">
	<h3	class='typography typography--size-36-text js-typography block-typography__typography'
	data-id='es-256'
	>
	Setting the trigger</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-260"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-258">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-259'
	>
	You can easily set the interval for your script by configuring the trigger. After saving the script, go to the left-side menu in App Script, where you’ll find <strong>Triggers</strong>. Click the blue <strong>Add trigger</strong> action button, and choose the function you want to run by selecting <strong>forwardInvoices</strong>. Here, you set your time-based trigger, for example, for running the script every hour. Once you save the trigger, you&#8217;re all set.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-263"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-261">
	<h2	class='typography typography--size-52-default js-typography block-typography__typography'
	data-id='es-262'
	>
	AI invoice processing in action</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-266"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-264">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-265'
	>
	Let’s demonstrate the process in a real-world accounts payable scenario.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-269"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-267">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-268'
	>
	This is an invoice we received:</p></div>	</div>
</div>
</div>		</div>
	</div>

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

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-273">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2024/09/Invoice.webp"
					class="image__img block-media__image-img"
					alt=""
										height="557"
															width="1244"
										loading="lazy"
					 />
					</picture>

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

<div
	class="wrapper"
	data-id="es-280"
	 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-275">
	

</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-278"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-276">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-277'
	>
	This is the email containing the invoice sent by our colleague in HR:</p></div>	</div>
</div>
</div>		</div>
	</div>

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

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-282">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2024/09/Mail.webp"
					class="image__img block-media__image-img"
					alt=""
										height="563"
															width="1244"
										loading="lazy"
					 />
					</picture>

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

<div
	class="wrapper"
	data-id="es-289"
	 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-284">
	

</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-287"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-285">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-286'
	>
	When the script runs based on the trigger we set up earlier, it finds and processes the invoice, and we get an automatic response confirming the action.</p></div>	</div>
</div>
</div>		</div>
	</div>

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

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-291">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2024/09/Mail_3.webp"
					class="image__img block-media__image-img"
					alt=""
										height="490"
															width="1244"
										loading="lazy"
					 />
					</picture>

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

<div
	class="wrapper"
	data-id="es-301"
	 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-293">
	

</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-296"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-294">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-295'
	>
	This way, the person who forwarded the email to the invoices email list knows that the invoice has been processed successfully.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-299"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-297">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-298'
	>
	In addition, we also used the script to forward the invoice to our accountant:</p></div>	</div>
</div>
</div>		</div>
	</div>

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

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-303">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2024/09/Mail_2.webp"
					class="image__img block-media__image-img"
					alt=""
										height="470"
															width="1244"
										loading="lazy"
					 />
					</picture>

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

<div
	class="wrapper"
	data-id="es-328"
	 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-305">
	

</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-308"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-306">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-307'
	>
	As you can see, the email subject contains the invoice details that the AI model extracted for us. So, if we want to search for an invoice in the future, we can do so easily through Gmail or Google Drive.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-311"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-309">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-310'
	>
	And that’s it! All the boring work is done automatically, and we&#8217;ve successfully streamlined the accounts payable workflow.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-314"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-312">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-313'
	>
	So far, the process has worked well for us. There is one condition, though: the PDF files must be of good quality. Sometimes, our AI invoice processing script makes some mistakes, but so do humans. If that happens, we handle the invoice manually. In any case, the success rate is more than satisfactory, and we spend far less time thinking about processing invoices.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-317"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-315">
	<h2	class='typography typography--size-52-default js-typography block-typography__typography'
	data-id='es-316'
	>
	Want to do more? Use your creativity for advanced AI invoice processing</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-320"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-318">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-319'
	>
	The process described above is just one example of how artificial intelligence can simplify accounts payable tasks, relieving the team of repetitive, tedious work. You can also build up on it and customize the script to match your needs. For example, you can get it to recognize if the invoice is sent by a colleague who needs a travel expenses refund and store the information in a Google Sheet. With a little creativity, the possibilities are endless. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-323"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-321">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-322'
	>
	Most importantly, this is a low-effort solution that requires very little development time yet makes your life a lot easier. Not all AI solutions have to be revolutionary. If we’ve reduced our invoice processing time so we can focus on more strategic tasks, it’s an AI-powered win. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-326"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-typography" data-id="es-324">
	<p	class='typography typography--size-16-text-roman js-typography block-typography__typography'
	data-id='es-325'
	>
	<em>If you want to leverage AI to optimize processes within your organization, <a href="https://infinum.com/ai-business-solutions/" target="_blank" rel="noreferrer noopener">find out how we can help.</a></em></p></div>	</div>
</div>
</div>		</div>
	</div><p>The post <a href="https://infinum.com/blog/ai-invoice-processing/">Want an AI Invoice Processing Solution? Create It Yourself</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</content:encoded>
			</item>
					<item>
				<image>
					<url>7966https://infinum.com/uploads/2020/12/secrets-android-projects-0.webp</url>
				</image>
				<title>Keeping Secrets Safe in Android Projects</title>
				<link>https://infinum.com/blog/secrets-android-projects/</link>
				<pubDate>Mon, 21 Dec 2020 15:40:00 +0000</pubDate>
				<dc:creator>Mladen Rakonjac</dc:creator>
				<guid isPermaLink="false">https://infinum.com/the-capsized-eight/secrets-android-projects/</guid>
				<description>
					<![CDATA[<p>You can never be too cautious with your secrets.</p>
<p>The post <a href="https://infinum.com/blog/secrets-android-projects/">Keeping Secrets Safe in Android Projects</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</description>
				<content:encoded>
					<![CDATA[<div
	class="wrapper"
	data-id="es-453"
	 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-329">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-332"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-330">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-331'
	>
	My Cryptography course professor used to say: “Nothing is 100% secure. Even if you go to the farthest place to hide the key for a safe, there is still a small percentage of probability that somebody will find it”.</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-paragraph" data-id="es-333">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-334'
	>
	Making something more secure actually means making sure that percentage is as low as possible.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-338"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-336">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-337'
	>
	What can we do to make secrets more secure in Android projects?</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-341"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-339">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-340'
	>
	The secret in Android projects can be an API key, Keystore, credentials for publishing, credentials for some special access to the 3rd party SDK, etc. If we want to secure a secret in an Android project, we usually ignore it in Git. When somebody new jumps into the project, those ignored files should be added manually.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-344"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-342">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-343'
	>
	Usually, secrets, like credentials, are inside the <code>build.gradle</code> file because they are environment-specific. For simplicity, let’s say that we have an environment called <code>preproduction</code> in which we have credentials to log in automatically into the application tapping on the login button multiple times.</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'
	>
	This environment acts the same as <code>production</code> plus this log in automatically feature. This is good to have for fast internal testing in the <code>production</code> environment before production release. However, this secret should never be visible in the final production apk. For simplicity, let’s say that we have <code>CREDENTIALS</code> that we use only in the preproduction environment.</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-paragraph" data-id="es-348">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-349'
	>
	We can add it to the <code>build.gradle</code> file:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-352"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-groovy github-light" data-language="groovy" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">..</span><span class="token">.
</span></span><span class="line"><span class="token">defaultConfig </span><span class="token">{</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">    resValue </span><span class="token" style="color: #005cc5;">RES_STRING</span><span class="token">, ‘</span><span class="token" style="color: #005cc5;">CREDENTIALS_USERNAME</span><span class="token">’,’’
</span></span><span class="line"><span class="token">    resValue </span><span class="token" style="color: #005cc5;">RES_STRING</span><span class="token">, ‘</span><span class="token" style="color: #005cc5;">CREDENTIALS_PASSWORD</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">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">productFlavors </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">    preproduction </span><span class="token">{</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">            resValue </span><span class="token" style="color: #005cc5;">RES_STRING</span><span class="token">, ‘</span><span class="token" style="color: #005cc5;">CREDENTIALS_USERNAME</span><span class="token">’,’mladen’
</span></span><span class="line"><span class="token">            resValue </span><span class="token" style="color: #005cc5;">RES_STRING</span><span class="token">, ‘</span><span class="token" style="color: #005cc5;">CREDENTIALS_PASSWORD</span><span class="token">’,’str0ngP4ssw0rd’
</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">}</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">..</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-355"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-353">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-354'
	>
	When we build the project, the credentials strings are located in <code>gradleResValues.xml</code> and we can access them using <code>@string/</code> in XML files or <code>getString</code> function in Kotlin/Java classes as we do for any other string from resources.</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-paragraph" data-id="es-356">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-357'
	>
	The<code>build.gradle</code> file should not be ignored in the version-control system cause all developers in the team should have the same version. With the current implementation, <code>‘CREDENTIALS_USERNAME’</code> and <code>‘CREDENTIALS_PASSWORD’</code> as part of the <code>build.gradle</code> file, will be included in git. This is not safe.</p></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'
	>
	We want to make it more secure, so if somebody gets access to the repository that person should not get access to our production credentials.</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-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-362"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-363">
	<picture class="image__picture block-media__image-picture">
								
			<source
				srcset=https://infinum.com/uploads/2020/12/secrets-android-projects-1-1400x349.webp				media='(max-width: 699px)'
				type=image/webp								height="349"
												width="1400"
				 />
												<img
					src="https://infinum.com/uploads/2020/12/secrets-android-projects-1.webp"
					class="image__img block-media__image-img"
					alt=""
										height="390"
															width="1566"
										loading="lazy"
					 />
					</picture>

	</figure></div></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 avoid that, we can store this key in the local.properties file:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-369"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-groovy github-light" data-language="groovy" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #005cc5;">CREDENTIALS_USERNAME</span><span class="token" style="color: #d73a49;">=</span><span class="token">mladen
</span></span><span class="line"><span class="token" style="color: #005cc5;">CREDENTIALS_PASSWORD</span><span class="token" style="color: #d73a49;">=</span><span class="token">str0ngP4ssw0rd
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-372"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-370">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-371'
	>
	The local.properties file should not be included in the version-control system.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-375"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-373">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-374'
	>
	To make keys from <code>local.properties</code> accessible in <code>build.gradle</code> we can add the following function:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-377"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-groovy github-light" data-language="groovy" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">def getLocalProperties() {
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">Properties</span><span class="token"> </span><span class="token">props</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: #d73a49;">Properties</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: #d73a49;">if</span><span class="token"> </span><span class="token">(</span><span class="token">file</span><span class="token">(</span><span class="token">’</span><span class="token" style="color: #d73a49;">..</span><span class="token" style="color: #d73a49;">/</span><span class="token">local</span><span class="token" style="color: #d73a49;">.</span><span class="token">properties’</span><span class="token">)</span><span class="token" style="color: #d73a49;">.</span><span class="token">ex</span><span class="token">i</span><span class="token">sts</span><span class="token">(</span><span class="token">)</span><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">        props</span><span class="token" style="color: #d73a49;">.</span><span class="token">load</span><span class="token">(</span><span class="token" style="color: #d73a49;">new</span><span class="token"> </span><span class="token" style="color: #d73a49;">FileInputStream</span><span class="token">(</span><span class="token">file</span><span class="token">(</span><span class="token">’</span><span class="token" style="color: #d73a49;">..</span><span class="token" style="color: #d73a49;">/</span><span class="token">local</span><span class="token" style="color: #d73a49;">.</span><span class="token">properties’</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">}</span><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">return</span><span class="token"> props
</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-380"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-378">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-379'
	>
	Next, we should create a build config field this way:</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-code">
	<pre class="phiki language-groovy github-light" data-language="groovy" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">preproduction </span><span class="token">{</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;">Properties</span><span class="token"> localProperties </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token">getLocalProperties</span><span class="token">(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">    resValue </span><span class="token" style="color: #005cc5;">RES_STRING</span><span class="token">, ‘</span><span class="token" style="color: #005cc5;">CREDENTIALS_USERNAME</span><span class="token">’, localProperties</span><span class="token">[</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">CREDENTIALS_USERNAME</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">]</span><span class="token">
</span></span><span class="line"><span class="token">    resValue </span><span class="token" style="color: #005cc5;">RES_STRING</span><span class="token">, ‘</span><span class="token" style="color: #005cc5;">CREDENTIALS_PASSWORD</span><span class="token">’, localProperties</span><span class="token">[</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">CREDENTIALS_PASSWORD</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">]</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">
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-385"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-383">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-384'
	>
	How will CI know about this secret?</h2></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'
	>
	At Infinum, we switched from <a href="https://infinum.com/blog/bitrise-vs-circleci-for-android-in-a-head-to-head-battle/">CircleCI to Bitrise</a>, so in this blog post, I’ll show you how to do this on Bitrise, but it’s similar to any other CI.</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'
	>
	We should copy the key from our local.properties file and add it inside Bitrise Secret ( Project -&gt; Workflow -&gt; Secrets -&gt; Add New). Let’s name it <code>CREDENTIALS_USERNAME</code>.</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-paragraph" data-id="es-392">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-393'
	>
	Next, we should change build config field:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-396"
	 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">resValue RES_STRING, ‘CREDENTIALS_USERNAME’, System</span><span class="token">.getenv</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">CREDENTIALS_USERNAME</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">) ?: project.properties[</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">CREDENTIALS_USERNAME</span><span class="token" style="color: #032f62;">&quot;</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-399"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-397">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-398'
	>
	If we run the build on CI, it will get <code>CREDENTIALS_USERNAME</code> from Secrets, if we run it locally it will get <code>API_KEY</code> from <code>local.properties</code> file.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-402"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-400">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-401'
	>
	What if we want to have a special credentials.properties file with all API keys?</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-405"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-403">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-404'
	>
	If we want to separate API keys from other properties in the <code>local.properties</code> file, we can create a new <code>credentials.properties</code> file. Moreover, instead of storing key by key in Bitrise Secrets, we can store the whole content of the<code>credentials.properties</code> file in one Bitrise secret called <code>CREDENTIALS_PROPERTIES</code>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-408"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-406">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-407'
	>
	Then, we can make a special workflow step that will create credentials.properties file and fill it with data stored in Bitrise Secret. That step should be called before any other step.</p></div>	</div>

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

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-410">
	<picture class="image__picture block-media__image-picture">
								
			<source
				srcset=https://infinum.com/uploads/2020/12/secrets-android-projects-2-1400x261.webp				media='(max-width: 699px)'
				type=image/webp								height="261"
												width="1400"
				 />
												<img
					src="https://infinum.com/uploads/2020/12/secrets-android-projects-2.webp"
					class="image__img block-media__image-img"
					alt=""
										height="298"
															width="1600"
										loading="lazy"
					 />
					</picture>

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

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-414"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-412">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-413'
	>
	A script could be made as follows:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-416"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-shellscript github-light" data-language="shellscript" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #005cc5;">set</span><span class="token"> </span><span class="token" style="color: #005cc5;">-</span><span class="token" style="color: #005cc5;">e</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #005cc5;">echo</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;">{</span><span class="token" style="color: #24292e;">CREDENTIALS_PROPERTIES</span><span class="token" style="color: #032f62;">}</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token" style="color: #d73a49;">&gt;</span><span class="token"> </span><span class="token" style="color: #032f62;">credentials.properties</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-419"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-417">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-418'
	>
	In that case, we should have only the following, without fetching the key from the system envs.</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-code">
	<pre class="phiki language-groovy github-light" data-language="groovy" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">resValue </span><span class="token" style="color: #005cc5;">RES_STRING</span><span class="token">, ‘</span><span class="token" style="color: #005cc5;">CREDENTIALS_USERNAME</span><span class="token">’, project</span><span class="token" style="color: #d73a49;">.</span><span class="token">properties</span><span class="token">[</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">CREDENTIALS_USERNAME</span><span class="token" style="color: #032f62;">&quot;</span><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-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'
	>
	You can choose one of the two approaches depending on what works for you.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-427"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-425">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-426'
	>
	Sharing secrets between team members with Vault</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-430"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-428">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-429'
	>
	Adding secrets to the CI is a one-time thing. You add them and you won’t change them ever, but what if you need to share them with your colleagues? It’s possible to use a password manager, but when someone joins the project, they have to add a new file and copy-paste the credentials. If you have a lot of them, this can be tedious.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-433"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-431">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-432'
	>
	A better way to share secrets is by using <a href="https://www.vaultproject.io/">Vault</a>. This tool enables you to have secrets for all your projects in one place. It has a command-line tool, so you can integrate it with your continuous integration script. That way, you do not expose secrets on your CI as they will be fetched only when you need them.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-436"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-434">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-435'
	>
	Any colleague who joins the project can have access to the Vault and will be able to fetch all or only one secret.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-439"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-437">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-438'
	>
	There are other tools that you can use instead of Vault, but the idea is the same. You have one tool which will help you share those secrets with your team.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-442"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-440">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-441'
	>
	Is this secure enough?</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-445"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-443">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-444'
	>
	If someone wants to, they can still do reverse engineering of the apk file and find keys, so it is really important what you have in a final production apk. If somebody gets access to the repository, credentials that are not included in the final production apk but are used during the development should not be exposed to that person.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-448"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-446">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-447'
	>
	Also, it is really important that those apks for preproduction environment will be stored in some internal host.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-451"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-449">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-450'
	>
	This was just one example of how we make Android projects more secure. In the end, everything is vulnerable, but we should try to make it harder for the bad guys. By introducing more layers of security, we reduce the vulnerability.</p></div>	</div>
</div>
</div>		</div>
	</div><p>The post <a href="https://infinum.com/blog/secrets-android-projects/">Keeping Secrets Safe in Android Projects</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</content:encoded>
			</item>
					<item>
				<image>
					<url>8026https://infinum.com/uploads/2019/12/how-to-support-themes-in-custom-views-for-android-apps-0.webp</url>
				</image>
				<title>How to Support Themes in Custom Views for Android Apps</title>
				<link>https://infinum.com/blog/how-to-support-themes-in-custom-views-for-android-apps/</link>
				<pubDate>Thu, 05 Dec 2019 14:45:00 +0000</pubDate>
				<dc:creator>Mladen Rakonjac</dc:creator>
				<guid isPermaLink="false">https://infinum.com/the-capsized-eight/how-to-support-themes-in-custom-views-for-android-apps/</guid>
				<description>
					<![CDATA[<p>Have you ever supported multiple themes in your application? Learn how to prepare a custom view that won't fall apart.</p>
<p>The post <a href="https://infinum.com/blog/how-to-support-themes-in-custom-views-for-android-apps/">How to Support Themes in Custom Views for Android Apps</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</description>
				<content:encoded>
					<![CDATA[<div
	class="wrapper"
	data-id="es-666"
	 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-454">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-457"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-455">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-456'
	>
	Have you ever supported multiple themes in your application? You made a new theme, added new colors, made a new build and everything looked shiny – except your custom view? Learn how to prepare a custom view that won’t fall apart.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-460"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-458">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-459'
	>
	The glue that keeps it all in one place</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-463"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-461">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-462'
	>
	Your custom view should support theming, so that it does not break the theme once you change it. Let’s go over a simple example.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-466"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-464">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-465'
	>
	Let’s say you want to make a custom view that will have an icon, title, and a description. Here’s how to create a layout for that custom view:</p></div>	</div>

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

	<figure class="image block-media__image-figure image--size-auto" data-id="es-468">
	<picture class="image__picture block-media__image-picture">
								
			<source
				srcset=https://infinum.com/uploads/2019/12/how-to-support-themes-in-custom-views-for-android-apps-1-1400x354.webp				media='(max-width: 699px)'
				type=image/webp								height="354"
												width="1400"
				 />
												<img
					src="https://infinum.com/uploads/2019/12/how-to-support-themes-in-custom-views-for-android-apps-1.webp"
					class="image__img block-media__image-img"
					alt=""
										height="368"
															width="1454"
										loading="lazy"
					 />
					</picture>

			<figcaption class="image__figcaption block-media__image-figcaption">
			Layout for the custom view		</figcaption>
	</figure></div></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-471"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-xml github-light" data-language="xml" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token">&lt;?</span><span class="token" style="color: #22863a;">xml</span><span class="token" style="color: #6f42c1;"> version</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">1.0</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #6f42c1;"> encoding</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">utf-8</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">?&gt;</span><span class="token">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">merge</span><span class="token"> </span><span class="token" style="color: #6f42c1;">tools</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">parentTag</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">androidx.constraintlayout.widget.ConstraintLayout</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;">ImageView</span><span class="token"> </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token">id=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">@+id/image</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;">TextView</span><span class="token"> </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token">id=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">@+id/title</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;">TextView</span><span class="token"> </span><span class="token" style="color: #6f42c1;">android</span><span class="token" style="color: #6f42c1;">:</span><span class="token">id=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">@+id/description</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">&lt;/</span><span class="token" style="color: #22863a;">merge</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-474"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-472">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-473'
	>
	The next step is to create a class for that custom view:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-476"
	 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: #d73a49;">&lt;</span><span class="token">span </span><span class="token" style="color: #d73a49;">data</span><span class="token" style="color: #d73a49;">-</span><span class="token">es</span><span class="token" style="color: #d73a49;">-</span><span class="token">language</span><span class="token" style="color: #d73a49;">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">c</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #d73a49;">&gt;</span><span class="token" style="color: #d73a49;">&lt;</span><span class="token" style="color: #d73a49;">/</span><span class="token">span</span><span class="token" style="color: #d73a49;">&gt;</span><span class="token">lass AwesomeCustomView </span><span class="token">constructor</span><span class="token">(
</span></span><span class="line"><span class="token">   context</span><span class="token">: </span><span class="token" style="color: #6f42c1;">Context</span><span class="token">,
</span></span><span class="line"><span class="token">   attrs</span><span class="token">: </span><span class="token" style="color: #6f42c1;">AttributeSet</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><span class="line"><span class="token">   defStyleAttr</span><span class="token">: </span><span class="token" style="color: #6f42c1;">Int</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">0</span><span class="token">
</span></span><span class="line"><span class="token">) </span><span class="token">: </span><span class="token" style="color: #6f42c1;">ConstraintLayout</span><span class="token">(</span><span class="token" style="color: #6f42c1;">context</span><span class="token">, </span><span class="token" style="color: #6f42c1;">attrs</span><span class="token">, </span><span class="token" style="color: #6f42c1;">defStyleAttr</span><span class="token">) </span><span class="token">{
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">init</span><span class="token"> {
</span></span><span class="line"><span class="token">       View</span><span class="token">.inflate</span><span class="token">(context, R.layout.layout_awesome_custom_view, </span><span class="token" style="color: #005cc5;">this</span><span class="token">)
</span></span><span class="line"><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-479"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-477">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-478'
	>
	You shouldn’t set drawable for ImageView nor the text size, line height, and the color for your TextViews directly in the layout of the custom view.</p></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">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-481'
	>
	Stylable</h3></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'
	>
	Let’s make the custom view styleable, so that different themes can have different style values. To do that, add the following code in <code>attrs.xml</code> file:</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-xml github-light" data-language="xml" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">resources</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;">declare-styleable</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;">AwesomeCustomView</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #6a737d;">&lt;!--</span><span class="token" style="color: #6a737d;">  todo add custom attributes </span><span class="token" style="color: #6a737d;">--&gt;</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">declare-styleable</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;">resources</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-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'
	>
	Now, add <code>image</code> and <code>imageTintColor</code> attributes, which will be used to set image drawable and tint color:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-492"
	 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">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">resources</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;">declare-styleable</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;">AwesomeCustomView</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">attr</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;">awesomeImage</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token" style="color: #6f42c1;">format</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">reference</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">/&gt;</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">attr</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;">awesomeImageTintColor</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token" style="color: #6f42c1;">format</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">color</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">/&gt;</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">declare-styleable</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;">resources</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-495"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-493">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-494'
	>
	You can use those attributes when adding <code>AwesomeCustomView</code> in fragment or activity layout. For example, if you want to add it in <code>fragment_example.xml</code>:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-497"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-xml github-light" data-language="xml" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">androidx.constraintlayout.widget.ConstraintLayout</span><span class="token"> ... </span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">co.infinum.styles.AwesomeCustomView</span><span class="token">
</span></span><span class="line"><span class="token">       ...
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #6f42c1;">app</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">awesomeImage</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">@drawable/ic_info</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;">app</span><span class="token" style="color: #6f42c1;">:</span><span class="token" style="color: #6f42c1;">awesomeImageTintColor</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">@color/green</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token">/&gt;</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">&lt;/</span><span class="token" style="color: #22863a;">androidx.constraintlayout.widget.ConstraintLayout</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-502"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="blockquote block-blockquote__blockquote" data-id="es-498">
	
	<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-499'>
	<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-500'
	>
	When you use custom attribute, you should use app: prefix instead of android: prefix.</p>
		<div class="blockquote__caption-wrap">
					</div>
	</div>
</div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-505"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-503">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-504'
	>
	To support those attributes, you should get values for those attributes in <code>AwesomeCustomView</code> class. You can do that from <code>attrs</code>, using <code>obtainStyledAttributes()</code> function:</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-code">
	<pre class="phiki language-c github-light" data-language="c" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">&lt;</span><span class="token">span data</span><span class="token" style="color: #d73a49;">-</span><span class="token">es</span><span class="token" style="color: #d73a49;">-</span><span class="token">language</span><span class="token" style="color: #d73a49;">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">c</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #d73a49;">&gt;</span><span class="token" style="color: #d73a49;">&lt;</span><span class="token" style="color: #d73a49;">/</span><span class="token">span</span><span class="token" style="color: #d73a49;">&gt;</span><span class="token">lass AwesomeCustomView @JvmOverloads </span><span class="token" style="color: #6f42c1;">constructor</span><span class="token">(</span><span class="token">
</span></span><span class="line"><span class="token">   context: Context</span><span class="token">,</span><span class="token">
</span></span><span class="line"><span class="token">   attrs: AttributeSet</span><span class="token" style="color: #d73a49;">?</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> null</span><span class="token">,</span><span class="token">
</span></span><span class="line"><span class="token">   defStyleAttr</span><span class="token" style="color: #d73a49;">:</span><span class="token"> Int </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token">0</span><span class="token">
</span></span><span class="line"><span class="token">)</span><span class="token"> : </span><span class="token" style="color: #6f42c1;">ConstraintLayout</span><span class="token">(</span><span class="token">context</span><span class="token">,</span><span class="token"> attrs</span><span class="token">,</span><span class="token"> defStyleAttr</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><span class="line"><span class="token">   init </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #24292e;">View</span><span class="token">.</span><span class="token" style="color: #6f42c1;">inflate</span><span class="token">(</span><span class="token">context</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #24292e;">R</span><span class="token">.</span><span class="token" style="color: #24292e;">layout</span><span class="token">.</span><span class="token" style="color: #24292e;">layout_awesome_custom_view</span><span class="token">,</span><span class="token"> this</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">       attrs</span><span class="token" style="color: #d73a49;">?</span><span class="token">.let </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">           val typedArray </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #24292e;">context</span><span class="token">.</span><span class="token" style="color: #6f42c1;">obtainStyledAttributes</span><span class="token">(</span><span class="token">it</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #24292e;">R</span><span class="token">.</span><span class="token" style="color: #24292e;">styleable</span><span class="token">.</span><span class="token" style="color: #24292e;">AwesomeCustomView</span><span class="token">)</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;">todo get value for attrs                            </span><span class="token">
</span></span><span class="line"><span class="token">           </span><span class="token" style="color: #24292e;">typedArray</span><span class="token">.</span><span class="token" style="color: #6f42c1;">recycle</span><span class="token">(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token">}</span><span class="token">
</span></span><span class="line"><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-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'
	>
	Once you’re finished with <code>typedArray</code>, you will need to recycle it.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-513"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-511">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-512'
	>
	To set tint, do the following:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-515"
	 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">val </span><span class="token">imageTintColorResource </span><span class="token" style="color: #d73a49;">=</span><span class="token"> typedArray</span><span class="token">.getResourceId</span><span class="token">(R.styleable.AwesomeCustomView_awesomeImageTintColor,
</span></span><span class="line"><span class="token">   android.R.color.white)
</span></span><span class="line"><span class="token">val </span><span class="token">imageTintColor </span><span class="token" style="color: #d73a49;">=</span><span class="token"> ContextCompat</span><span class="token">.getColor</span><span class="token">(context, imageTintColorResource)
</span></span><span class="line"><span class="token">ImageViewCompat</span><span class="token">.setImageTintList</span><span class="token">(image, ColorStateList</span><span class="token">.valueOf</span><span class="token">(imageTintColor))
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-518"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-516">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-517'
	>
	Unless you specify tint color, it will fall back to white color.<br>You can do a similar thing for <code>awesomeImage</code>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-521"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-519">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-520'
	>
	Style attribute</h3></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'
	>
	Should you set the <code>awesomeImageTintColor</code> and <code>awesomeImage</code> attributes wherever you use <code>AwesomeCustomView</code> with the desired tint color? <code>No</code>.</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'
	>
	There is a better way to handle it. In the <code>styles.xml</code>, let’s make a new style :</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-529"
	 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">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">resources</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;">style</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;">Widget.AppTheme.AwesomeCustomView</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token" style="color: #6f42c1;">parent</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">item</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;">awesomeImageTintColor</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">@color/green</span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">item</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;">style</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;">resources</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-534"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="blockquote block-blockquote__blockquote" data-id="es-530">
	
	<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-531'>
	<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-532'
	>
	Note: When you use a custom attribute in style, don’t use the android or app prefix.</p>
		<div class="blockquote__caption-wrap">
					</div>
	</div>
</div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-537"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-535">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-536'
	>
	For example, instead of having <code>android:awesomeImageTintColor</code> you should just have <code>awesomeImageTintColor</code>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-540"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-538">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-539'
	>
	Apply it in <code>activity_acv_example.xml</code>:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-542"
	 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">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">androidx.constraintlayout.widget.ConstraintLayout</span><span class="token"> ... </span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">co.infinum.styles.AwesomeCustomView</span><span class="token">
</span></span><span class="line"><span class="token">       ...
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #6f42c1;">style</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">@style/Widget.AppTheme.AwesomeCustomView</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token">/&gt;</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">&lt;/</span><span class="token" style="color: #22863a;">androidx.constraintlayout.widget.ConstraintLayout</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-545"
	 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-543"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-544">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2019/12/how-to-support-themes-in-custom-views-for-android-apps-2.webp"
					class="image__img block-media__image-img"
					alt=""
										height="365"
															width="1080"
										loading="lazy"
					 />
					</picture>

			<figcaption class="image__figcaption block-media__image-figcaption">
			Custom view with applied style		</figcaption>
	</figure></div></div>	</div>

<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'
	>
	At this point, you should only use one line in all the places where you want this style for your view.<br>If you want to have a different style, you can do it with ease:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-550"
	 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">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">resources</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;">style</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;">Widget.AppTheme.AwesomeCustomView.Warning</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token" style="color: #6f42c1;">parent</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">item</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;">awesomeImageTintColor</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">@color/sunset</span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">item</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;">style</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;">resources</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-553"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-551">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-552'
	>
	To use this in layout, just change one line:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-555"
	 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">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">androidx.constraintlayout.widget.ConstraintLayout</span><span class="token"> ... </span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">co.infinum.styles.AwesomeCustomView</span><span class="token">
</span></span><span class="line"><span class="token">       ...
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #6f42c1;">style</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">@style/Widget.AppTheme.AwesomeCustomView.Warning</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token">/&gt;</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">&lt;/</span><span class="token" style="color: #22863a;">androidx.constraintlayout.widget.ConstraintLayout</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-558"
	 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-556"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-557">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2019/12/how-to-support-themes-in-custom-views-for-android-apps-3.webp"
					class="image__img block-media__image-img"
					alt=""
										height="381"
															width="1080"
										loading="lazy"
					 />
					</picture>

			<figcaption class="image__figcaption block-media__image-figcaption">
			Custom view when Warning Theme is applied		</figcaption>
	</figure></div></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-561"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-559">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-560'
	>
	How did you end up with the name <code>Widget.AppTheme.AwesomeCustomView</code>?</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-564"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-562">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-563'
	>
	Let’s break it down to parts:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-567"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-565">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-566'
	>
	<li><code>Widget</code> —  describes that there is a defined style for a view</li><li><code>AppTheme</code> —  describes that this style is for the base AppTheme base theme</li><li><code>AwesomeCustomView</code> —  describes which view the style is for</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-570"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-568">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-569'
	>
	Instead of having lines of code for each custom attribute, now you can just use the style attribute.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-573"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-571">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-572'
	>
	TextAppearance attributes</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-576"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-574">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-575'
	>
	You should do a similar thing for our <code>TextViews</code>. Instead of making attributes for text size, text line height, text color, font, and other characteristics for each <code>TextView</code>, you should make a custom <code>textAppearance</code> attr.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-579"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-577">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-578'
	>
	What is the <code>textAppearance</code> attr? It is the same as style attribute but for the text. Instead of supporting all the attributes, as style does, <code>textAppearance</code> supports only some attributes, like <code>textColor</code>,<code>textSize</code>, <code>typeface</code>, <code>fontFamily</code> etc. However, it does not support ellipsize or background for example.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-582"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-580">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-581'
	>
	In the <code>attr.xml</code> file, you should add the new attribute:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-584"
	 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">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">resources</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;">declare-styleable</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;">AwesomeCustomView</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;">attr</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;">awesomeTitleTextAppearance</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token" style="color: #6f42c1;">format</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">reference</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">/&gt;</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">declare-styleable</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;">resources</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-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'
	>
	To support it, you will need to make the following changes in the AwesomeCustomView class:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-589"
	 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">val </span><span class="token">titleStyleRes </span><span class="token" style="color: #d73a49;">=</span><span class="token"> typedArray</span><span class="token">.getResourceId</span><span class="token">(R.styleable.AwesomeCustomView_awesomeTitleTextAppearance,
</span></span><span class="line"><span class="token">   R.style.TextAppearance_AppCompat_Title)
</span></span><span class="line"><span class="token">TextViewCompat</span><span class="token">.setTextAppearance</span><span class="token">(title, titleStyleRes)
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-592"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-590">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-591'
	>
	If you don’t specify text appearance, it will fallback to AppCompat’s Title TextAppearance.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-595"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-593">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-594'
	>
	Now, you can get down to adding the special <code>TextAppearance</code> style for the title in the <code>styles.xml</code> file.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-598"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-596">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-597'
	>
	Let’s call that style Header :</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-600"
	 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">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">style</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;">TextAppearance.AppTheme.Header</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token" style="color: #6f42c1;">parent</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">android:TextAppearance</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">item</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;">android:lineSpacingMultiplier</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">0</span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">item</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;">item</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;">android:textSize</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">32sp</span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">item</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;">item</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;">android:lineSpacingExtra</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">40sp</span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">item</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;">item</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;">android:textStyle</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">bold</span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">item</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;">style</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-603"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-601">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-602'
	>
	Let’s explain the naming for <code>TextAppearance.AppTheme.Header</code> :</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-606"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-604">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-605'
	>
	<li><code>TextAppearance</code>  —  describes that we have a style for textAppearance attr</li><li><code>AppTheme</code>  — describes that this style is for our AppTheme base theme</li><li><code>Header</code> — describes our text style</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-609"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-607">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-608'
	>
	You’ll do a similar code for description.<br>To use them in the <code>Widget.AppTheme.AwesomeCustomView</code> style, add the following:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-611"
	 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">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">style</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;">Widget.AppTheme.AwesomeCustomView</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token" style="color: #6f42c1;">parent</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</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;">item</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;">awesomeTitleTextAppearance</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">@style/TextAppearance.AppTheme.Header</span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">item</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;">item</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;">awesomeDescriptionTextAppearance</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">@style/TextAppearance.AppTheme.Body</span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">item</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;">style</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-614"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-612">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-613'
	>
	Theme</h3></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'
	>
	Let’s say you want to use the same style for your <code>AwesomeCustomView</code> everywhere where the base <code>AppTheme</code> is used. In that case, you should declare <code>AppTheme</code> styleable and add a special attribute in <code>attrs.xml</code>:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-619"
	 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">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">declare-styleable</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;">AppTheme</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">attr</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;">awesomeCustomViewStyle</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token" style="color: #6f42c1;">format</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">reference</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">/&gt;</span><span class="token">
</span></span><span class="line"><span class="token">&lt;/</span><span class="token" style="color: #22863a;">declare-styleable</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-622"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-620">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-621'
	>
	Next, you should set value for <code>awesomeCustomViewStyle</code> in your <code>AppTheme</code> :</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-624"
	 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">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">style</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;">AppTheme</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token" style="color: #6f42c1;">parent</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Theme.AppCompat.Light.NoActionBar</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">   
</span></span><span class="line"><span class="token">     </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">item</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;">awesomeCustomViewStyle</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">@style/Widget.AppTheme.AwesomeCustomView</span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">item</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;">style</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-627"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-625">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-626'
	>
	Now, you should end up with different parameters for <code>obtainStyledAttributes</code> function:</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-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">val </span><span class="token">typedArray </span><span class="token" style="color: #d73a49;">=</span><span class="token"> context</span><span class="token">.obtainStyledAttributes</span><span class="token">(
</span></span><span class="line"><span class="token">   it,
</span></span><span class="line"><span class="token">   R.styleable.AwesomeCustomView,
</span></span><span class="line"><span class="token">   R.attr.awesomeCustomViewStyle,                                   
</span></span><span class="line"><span class="token">   R.style.Widget_AppTheme_AwesomeCustomView
</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-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'
	>
	During the initialization of the <code>AwesomeCustomView</code> , Android will get the style of your view from <code>R.attr.awesomeCustomViewStyle</code>. If you use another theme, in which there is no set value for awesomeCustomViewStyle attribute, it will fall back to <code>R.style.Widget_AppTheme_AwesomeCustomView</code>.<br><br>That means you don’t have to use this line anymore:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-634"
	 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</span><span class="token" style="color: #d73a49;">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">@style/Widget.AppTheme.AwesomeCustomView</span><span class="token" style="color: #032f62;">&quot;</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-637"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-635">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-636'
	>
	Having done all that, you should now support themes for your custom view. If you want to use it in another theme, you can easily do it:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-639"
	 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">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">style</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;">AnotherTheme</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token" style="color: #6f42c1;">parent</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">AppTheme</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">
</span></span><span class="line"><span class="token">  </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">item</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;">awesomeCustomViewStyle</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">@style/Widget.AnotherTheme.AwesomeCustomView</span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">item</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;">style</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-642"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-640">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-641'
	>
	Dark theme</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-645"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-643">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-644'
	>
	Once you have a default custom view style per theme, it is really easy to support dark theme for your custom view. Your <code>AppTheme</code> should extend <code>Theme.AppCompat.DayNight</code> in <code>res/values/themes.xml</code> :</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-647"
	 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">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">style</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;">AppTheme</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token" style="color: #6f42c1;">parent</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Theme.AppCompat.DayNight</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">   
</span></span><span class="line"><span class="token">     </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">item</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;">awesomeCustomViewStyle</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">@style/Widget.AppTheme.AwesomeCustomView</span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">item</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;">style</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-650"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-648">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-649'
	>
	And in <code>res/values-night/themes.xml</code>, just define the dark resources:</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-code">
	<pre class="phiki language-xml github-light" data-language="xml" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token">&lt;</span><span class="token" style="color: #22863a;">style</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;">AppTheme</span><span class="token" style="color: #032f62;">&quot;</span><span class="token"> </span><span class="token" style="color: #6f42c1;">parent</span><span class="token">=</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Theme.AppCompat.DayNight</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">   
</span></span><span class="line"><span class="token">     </span><span class="token">&lt;</span><span class="token" style="color: #22863a;">item</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;">awesomeCustomViewStyle</span><span class="token" style="color: #032f62;">&quot;</span><span class="token">&gt;</span><span class="token">@style/Widget.AppTheme.Dark.AwesomeCustomView</span><span class="token">&lt;/</span><span class="token" style="color: #22863a;">item</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;">style</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-655"
	 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-653"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-654">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2019/12/how-to-support-themes-in-custom-views-for-android-apps-4.webp"
					class="image__img block-media__image-img"
					alt=""
										height="372"
															width="1080"
										loading="lazy"
					 />
					</picture>

			<figcaption class="image__figcaption block-media__image-figcaption">
			Custom view when dark theme is applied		</figcaption>
	</figure></div></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-658"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-656">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-657'
	>
	The checklist</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-661"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-659">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-660'
	>
	To conclude, if you want to avoid problems with custom views when you theme your app, your custom views should support custom <code>style</code> attributes for each child view style, and custom <code>textAppearance</code> attributes for each child TextView.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-664"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-662">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-663'
	>
	Furthermore, it should have defined styles for each different usage in each theme, as well as a default style for each theme.</p></div>	</div>
</div>
</div>		</div>
	</div><p>The post <a href="https://infinum.com/blog/how-to-support-themes-in-custom-views-for-android-apps/">How to Support Themes in Custom Views for Android Apps</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</content:encoded>
			</item>
		
	</channel>
</rss>