<?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>Camera Integration in the HAK Driving Exam App | Infinum</title>
		<atom:link href="https://infinum.com/blog/hak-camera-integration/feed/" rel="self" type="application/rss+xml" />
		<link>https://infinum.com/blog/hak-camera-integration/</link>
		<description>Building digital products</description>
		<lastBuildDate>Fri, 03 Apr 2026 12:58:20 +0000</lastBuildDate>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>

					<item>
				<image>
					<url>8025https://infinum.com/uploads/2021/08/hak-camera-integration-0.webp</url>
				</image>
				<title>Camera Integration in the HAK Driving Exam App</title>
				<link>https://infinum.com/blog/hak-camera-integration/</link>
				<pubDate>Tue, 03 Aug 2021 10:45:00 +0000</pubDate>
				<dc:creator>Stjepan Banek</dc:creator>
				<guid isPermaLink="false">https://infinum.com/the-capsized-eight/hak-camera-integration/</guid>
				<description>
					<![CDATA[<p>Another pair of eyes keeping an eye on the road for new drivers.</p>
<p>The post <a href="https://infinum.com/blog/hak-camera-integration/">Camera Integration in the HAK Driving Exam App</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</description>
				<content:encoded>
					<![CDATA[<div
	class="wrapper"
	data-id="es-387"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-92">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-95"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-93">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-94'
	>
	The Croatian Automobile Club (HAK) oversees and carries out all driving exams in Croatia. To introduce a digital dimension to the whole process, <a href="https://infinum.com/news/hak-app/">Infinum built an application that helps the examiners track the course of an exam</a>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-98"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-96">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-97'
	>
	The app notes the applicant’s mistakes, exam duration, driving path and records the exam using a third-party WiFi camera connected to the tablet while the app is used.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-101"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-99">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-100'
	>
	In this article, we will focus on the camera implementation aspect.</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-heading" data-id="es-102">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-103'
	>
	Camera implementation challenges</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-paragraph" data-id="es-105">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-106'
	>
	The development process wasn’t free of hurdles. The first significant one was WiFi discovery and connection. We realized that there was no cohesive and understandable API before Android 10 to perform these tasks. Eventually we came up with a solution, but in all honesty, we had a complete mess on our hands.</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-paragraph" data-id="es-108">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-109'
	>
	We had to use dozens of deprecated methods and properties as well as massive methods with a lot of try/catch clauses just to get our app to scan for a camera and connect with one. We were also worried about a deprecation notice saying that the app-initiated WiFi scans are going to be completely removed in future versions. However, there is no need to dive into pre-Android 10 network API in this article.</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-paragraph" data-id="es-111">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-112'
	>
	Somewhere along the road we decided to move the minimum version of the app to Android 10. We could afford to do so, because HAK examiners use only one type of device, and that device was updated to Android 10. This also allowed us to obtain access to <a href="https://developer.android.com/reference/android/net/wifi/WifiNetworkSpecifier">WifiNetworkSpecifier API</a>, which helped a great deal.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-116"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-114">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-115'
	>
	We discovered some more challenges in the recording feature:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-119"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-117">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-118'
	>
	<li>Connecting the camera to the application over TCP</li><li>Implementing a client to send commands to the camera and receive responses</li><li>Streaming the live camera feed to the app</li><li>Maintaining and restoring the connection if something goes wrong</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-122"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-120">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-121'
	>
	The dashcam</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-125"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-123">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-124'
	>
	For the recording feature we decided to use a car digital video recorder or a dashcam. The camera is placed on the car’s dashboard or windshield and connected to the app over WiFi.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-128"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-126">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-127'
	>
	These are the camera’s features:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-131"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-129">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-130'
	>
	<li>WiFi connectable</li><li>Recording to an SD card</li><li>Streaming the feed over WiFi</li><li>Removable SD card to store the videos</li><li>Command interface to communicate with the client app</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-134"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-132">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-133'
	>
	The camera connects to the application over WiFi. The user can control the recording through that connection and send some additional commands. Two connections are made with the camera:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-137"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-135">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-136'
	>
	<li>TCP socket connection for sending commands and receiving responses. For example, requesting and receiving info about the available storage data is done this way, as well as setting the exam ID for the video and starting or stopping the recording.</li><li>HTTP connection for streaming the camera feed to the application.</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-140"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-138">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-139'
	>
	When the recording is initiated, the camera records videos and stores them on the SD card. Each recording session is stored in a separate folder for easier navigation and the videos are recorded in multiple 1-minute recordings. Each video has a timestamp and the exam’s ID so that they can be sorted for each exam.</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-heading" data-id="es-141">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-142'
	>
	WiFi Network Specifier API</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-146"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-144">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-145'
	>
	Android 10 introduced a new API to handle in-app <a href="https://developer.android.com/guide/topics/connectivity/wifi-bootstrap">WiFi connections</a>. In-app connection here means literaly that. The connection is established while the application is used and then automatically disconnected when the application is closed.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-149"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-147">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-148'
	>
	Thanks to the new API, the whole process became much easier to implement.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-152"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-150">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-151'
	>
	Let’s dive into it step by step:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-155"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-153">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-154'
	>
	Building a specifier</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-157"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-php github-light" data-language="php" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #005cc5;">val</span><span class="token"> </span><span class="token" style="color: #005cc5;">specifier</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">WifiNetworkSpecifier</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">Builder</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;">.</span><span class="token" style="color: #6f42c1;">setSsidPattern</span><span class="token">(</span><span class="token" style="color: #6f42c1;">PatternMatcher</span><span class="token">(</span><span class="token" style="color: #005cc5;">ssid</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #005cc5;">PatternMatcher</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">PATTERN_PREFIX</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;">.</span><span class="token" style="color: #6f42c1;">setWpa2Passphrase</span><span class="token">(</span><span class="token" style="color: #005cc5;">password</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" style="color: #6f42c1;">build</span><span class="token">(</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-160"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-158">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-159'
	>
	<em>WifiNetworkSpecifier</em> is a kind of a query object for available network connections. We specified the SSID (name) of the network we wanted to connect to in its builder.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-163"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-161">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-162'
	>
	In this case we had predefined SSID patterns for our dashcams so that’s what we were passing in the variable SSID.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-166"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-164">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-165'
	>
	<em>PatternMatcher.PATTERN_PREFIX</em> is a constant that says we will use the value passed in SSID and use it as a prefix for possible access points. Access points that do not start with the SSID value are ignored.</p></div>	</div>

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

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-172"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-170">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-171'
	>
	<code>val ssid = "Dashcam_"</code></p></div>	</div>

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

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-178"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-176">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-177'
	>
	<li>Dashcam_123</li><li>My Home Wifi</li><li>Office</li><li>Dashcam_234</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-181"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-179">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-180'
	>
	Networks shown by the WiFi connection dialog:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-184"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-182">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-183'
	>
	<li>Dashcam_123</li><li>Dashcam_234</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-187"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-185">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-186'
	>
	We filtered out all access points that are not a dashcam. Optionally, we could have added a password so the connection is established faster. Otherwise, the dialog asks for password input. In our case the passwords were also predefined, so we used:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-190"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-188">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-189'
	>
	<code>setWpa2Passphrase(password)</code></p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-193"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-191">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-192'
	>
	Building a NetworkRequest</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-196"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-194">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-195'
	>
	After specifying the access point, we needed to build a <em>NetworkRequest</em> object with some additional configuration for our connection. First, we set the transport type to <em>TRANSPORT_WIFI</em>, by which we said that the connection would be established over WiFi. We then removed the connection’s internet capability, as our camera and application were communicating peer-to-peer. We finally added the specifier that we had built earlier to <em>NetworkRequest</em>.</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-php github-light" data-language="php" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #005cc5;">val</span><span class="token"> </span><span class="token" style="color: #005cc5;">request</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">NetworkRequest</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">Builder</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;">.</span><span class="token" style="color: #6f42c1;">addTransportType</span><span class="token">(</span><span class="token" style="color: #005cc5;">NetworkCapabilities</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">TRANSPORT_WIFI</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" style="color: #6f42c1;">removeCapability</span><span class="token">(</span><span class="token" style="color: #005cc5;">NetworkCapabilities</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">NET_CAPABILITY_INTERNET</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" style="color: #6f42c1;">setNetworkSpecifier</span><span class="token">(</span><span class="token" style="color: #005cc5;">specifier</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" style="color: #6f42c1;">build</span><span class="token">(</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-201"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-199">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-200'
	>
	With everything set up, let’s move on to requesting a connection using <em>ConnectivityManager</em>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-204"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-202">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-203'
	>
	Requesting a network connection</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-207"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-205">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-206'
	>
	Since it is a fairly simple procedure, we decided to use <em>ConnectivityManager</em> and define a callback to handle all the outcomes.</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-code">
	<pre class="phiki language-php github-light" data-language="php" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #005cc5;">val</span><span class="token"> </span><span class="token" style="color: #005cc5;">networkCallback</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #d73a49;">object</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">ConnectivityManager</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">NetworkCallback</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: #005cc5;">override</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">onAvailable</span><span class="token">(</span><span class="token" style="color: #6f42c1;">network</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">Network</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: #6a737d;">//</span><span class="token" style="color: #6a737d;"> handle success</span><span class="token" style="color: #6a737d;">
</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 class="token" style="color: #005cc5;">override</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">onUnavailable</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: #6a737d;">//</span><span class="token" style="color: #6a737d;"> handle failure</span><span class="token" style="color: #6a737d;">
</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 class="token" style="color: #005cc5;">override</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">onLosing</span><span class="token">(</span><span class="token" style="color: #6f42c1;">network</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">Network</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #6f42c1;">maxMsToLive</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #d73a49;">Int</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: #6a737d;">//</span><span class="token" style="color: #6a737d;"> handle losing connection</span><span class="token" style="color: #6a737d;">
</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 class="token" style="color: #005cc5;">override</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">onLost</span><span class="token">(</span><span class="token" style="color: #6f42c1;">network</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">Network</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: #6a737d;">//</span><span class="token" style="color: #6a737d;"> handle lost connection</span><span class="token" style="color: #6a737d;">
</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 class="token" style="color: #005cc5;">override</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">onLinkPropertiesChanged</span><span class="token">(</span><span class="token" style="color: #6f42c1;">network</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">Network</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #6f42c1;">linkProperties</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">LinkProperties</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: #6a737d;">//</span><span class="token" style="color: #6a737d;"> handle network changing LinkProperties</span><span class="token" style="color: #6a737d;">
</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 class="token" style="color: #005cc5;">override</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">onBlockedStatusChanged</span><span class="token">(</span><span class="token" style="color: #6f42c1;">network</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">Network</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #6f42c1;">blocked</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #d73a49;">Boolean</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: #6a737d;">//</span><span class="token" style="color: #6a737d;"> handle blocked status of a network change</span><span class="token" style="color: #6a737d;">
</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 class="token" style="color: #005cc5;">override</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">onCapabilitiesChanged</span><span class="token">(</span><span class="token" style="color: #6f42c1;">network</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">Network</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #6f42c1;">networkCapabilities</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">NetworkCapabilities</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: #6a737d;">//</span><span class="token" style="color: #6a737d;"> handle network capabilities change</span><span class="token" style="color: #6a737d;">
</span></span><span class="line"><span class="token">   </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-212"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-210">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-211'
	>
	After defining a callback we just needed to call <em>connectivityManager.requestNetwork()</em>, passing in the request and the previously defined callback.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-215"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-213">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-214'
	>
	A native dialog would pop up showing the available access points that meet the parameters passed to the specifier object. In the following picture there is a dialog showing one of our cameras named “BlackVue HAK 1613”.</p></div>	</div>

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

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-217">
	<picture class="image__picture block-media__image-picture">
												<img
					src="https://infinum.com/uploads/2021/07/hak-camera-integration-1.webp"
					class="image__img block-media__image-img"
					alt=""
										height="248"
															width="716"
										loading="lazy"
					 />
					</picture>

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

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-221"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-219">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-220'
	>
	Clean up the request</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-224"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-222">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-223'
	>
	In case the connection is lost or not found we needed to unregister the request by calling:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-227"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-225">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-226'
	>
	<code>connectivityManager.unregisterNetworkCallback(networkCallback)</code></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-heading" data-id="es-228">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-229'
	>
	Camera – Application communication</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-233"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-231">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-232'
	>
	With the connection successfully established, let’s dive deeper into the implementation of camera communication.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-236"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-234">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-235'
	>
	We have two separate clients communicating with the camera in the application. The first one is a TCP socket used for commands and simple data correspondence. The second one is an HTTP connection client that transfers the camera’s live feed to the application.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-239"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-237">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-238'
	>
	Callback and propagating to the caller</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-242"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-240">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-241'
	>
	Before moving on to the network request callback, we needed a utility interface to propagate connection events to the corresponding view components. Every view component requiring a connection with the camera would be implementing this interface.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-244"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-php github-light" data-language="php" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">interface</span><span class="token"> </span><span class="token" style="color: #6f42c1;">WifiConnectionListener</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 class="token" style="color: #6a737d;">/**</span><span class="token" style="color: #6a737d;">
</span></span><span class="line"><span class="token" style="color: #6a737d;">    * Called after a connection has been established successfully.
</span></span><span class="line"><span class="token" style="color: #6a737d;">    </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">onConnectionEstablished</span><span class="token">(</span><span class="token" style="color: #6f42c1;">ssid</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #d73a49;">String</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: #6a737d;">/**</span><span class="token" style="color: #6a737d;">
</span></span><span class="line"><span class="token" style="color: #6a737d;">    * Called if a connection attempt failed.
</span></span><span class="line"><span class="token" style="color: #6a737d;">    </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">onConnectionFailed</span><span class="token">(</span><span class="token" style="color: #6f42c1;">ssid</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #d73a49;">String</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: #6a737d;">/**</span><span class="token" style="color: #6a737d;">
</span></span><span class="line"><span class="token" style="color: #6a737d;">    * Called when disconnected from a Wi-Fi network at a point in time after being connected to it.
</span></span><span class="line"><span class="token" style="color: #6a737d;">    </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">onDisconnected</span><span class="token">(</span><span class="token" style="color: #6f42c1;">ssid</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #d73a49;">String</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: #6a737d;">/**</span><span class="token" style="color: #6a737d;">
</span></span><span class="line"><span class="token" style="color: #6a737d;">    * Called when the connection attempt is cancelled using WifiConnectionController.
</span></span><span class="line"><span class="token" style="color: #6a737d;">    </span><span class="token" style="color: #6a737d;">*/</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">onCancelled</span><span class="token">(</span><span class="token" style="color: #6f42c1;">ssid</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #d73a49;">String</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-247"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-245">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-246'
	>
	In the network callback for requesting a connection, we handled the outcomes of that request:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-249"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-php github-light" data-language="php" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #005cc5;">cancelled</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">false</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #005cc5;">networkWithTargetSsidFound</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">false</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #005cc5;">override</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">onAvailable</span><span class="token">(</span><span class="token" style="color: #6f42c1;">network</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">Network</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: #005cc5;">super</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">onAvailable</span><span class="token">(</span><span class="token" style="color: #005cc5;">network</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;">if</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #005cc5;">cancelled</span><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #6f42c1;">unregisterNetworkCallback</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><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 class="token" style="color: #005cc5;">connectivityManager</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">bindProcessToNetwork</span><span class="token">(</span><span class="token" style="color: #005cc5;">network</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #005cc5;">networkWithTargetSsidFound</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">true</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #005cc5;">listener</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">onConnectionEstablished</span><span class="token">(</span><span class="token" style="color: #005cc5;">ssid</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #005cc5;">override</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">onLost</span><span class="token">(</span><span class="token" style="color: #6f42c1;">network</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">Network</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: #005cc5;">super</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">onLost</span><span class="token">(</span><span class="token" style="color: #005cc5;">network</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">     </span><span class="token" style="color: #6f42c1;">unregisterNetworkCallback</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 class="token" style="color: #005cc5;">connectivityManager</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">bindProcessToNetwork</span><span class="token">(</span><span class="token" style="color: #005cc5;">null</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">     </span><span class="token" style="color: #005cc5;">listener</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">onDisconnected</span><span class="token">(</span><span class="token" style="color: #005cc5;">ssid</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-252"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-250">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-251'
	>
	In addition to notifying the listener about the connection, we also bound the connection to the process. You might notice the canceled flag. We used it so that the user can cancel a connection attempt. The flag can be set from <em>WifiConnectionManager</em>, which is just a wrapper class for all the WiFi-related code. The manager is used on every screen that leverages the WiFi connection.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-255"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-253">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-254'
	>
	To account for connection time-out and other methods from <em>WifiConnectionListener</em>, here are other methods from <em>WifiConnectionManager</em>:</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-code">
	<pre class="phiki language-php github-light" data-language="php" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">startResponseTimeoutCountdown</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;">if</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #005cc5;">timeoutCountdownStarted</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">not</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">       </span><span class="token" style="color: #005cc5;">timeoutCountdownStarted</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">true</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #6f42c1;">Handler</span><span class="token">(</span><span class="token" style="color: #005cc5;">Looper</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">getMainLooper</span><span class="token">(</span><span class="token">)</span><span class="token">)</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">postDelayed</span><span class="token">(</span><span class="token" style="color: #d73a49;">::</span><span class="token" style="color: #005cc5;">onTimedOut</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #005cc5;">NETWORK_CALLBACK_TIMEOUT_MS</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><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">onTimedOut</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: #005cc5;">timeoutCountdownStarted</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">false</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: #005cc5;">networkWithTargetSsidFound</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">not</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">       </span><span class="token" style="color: #6f42c1;">unregisterNetworkCallback</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: #005cc5;">cancelled</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">not</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">           </span><span class="token" style="color: #005cc5;">listener</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">onConnectionFailed</span><span class="token">(</span><span class="token" style="color: #005cc5;">ssid</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><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #005cc5;">override</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">cancel</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: #005cc5;">cancelled</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">true</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #005cc5;">listener</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">onCancelled</span><span class="token">(</span><span class="token" style="color: #005cc5;">ssid</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-260"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-258">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-259'
	>
	This way we have successfully propagated the callback to the screen. The listener covers all the bad cases so we can show the appropriate message or try to connect again if we want to. Maybe we’ll want to try and reconnect if the camera disconnects in the middle of an exam. In that case, we just start the whole process again until we get that <em>onConnectionEstablished</em> callback again.</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-paragraph" data-id="es-261">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-262'
	>
	After establishing the connection and propagating the callback to the caller, there was still some work to do. We had to set up the clients to communicate with the camera over WiFi.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-266"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-264">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-265'
	>
	Setting up the clients</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-269"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-267">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-268'
	>
	Dashcam client</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-272"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-270">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-271'
	>
	The first client to set up was the dashcam command interface client. We decided to use a third-party library that provides a TCP socket implementation for Android called <a href="https://github.com/xuuhaoo/OkSocket"><em>OkSocket</em></a>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-275"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-273">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-274'
	>
	The socket is set up in the following block of code:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-277"
	 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">connectionManager </span><span class="token" style="color: #d73a49;">=</span><span class="token"> OkSocket.</span><span class="token" style="color: #6f42c1;">open</span><span class="token">(</span><span class="token" style="color: #6f42c1;">ConnectionInfo</span><span class="token">(</span><span class="token">DashcamApiService.DASHCAM_IP</span><span class="token">,</span><span class="token"> DashcamApiService.DASHCAM_PORT</span><span class="token">)</span><span class="token">)</span><span class="token">.apply </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">  </span><span class="token" style="color: #6f42c1;">option</span><span class="token">(</span><span class="token" style="color: #24292e;">OkSocketOptions</span><span class="token">.</span><span class="token" style="color: #6f42c1;">Builder</span><span class="token">(</span><span class="token">option</span><span class="token">)</span><span class="token">.</span><span class="token" style="color: #6f42c1;">setReaderProtocol</span><span class="token">(</span><span class="token">readerProtocol</span><span class="token">)</span><span class="token">.</span><span class="token" style="color: #6f42c1;">build</span><span class="token">(</span><span class="token">)</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #6f42c1;">registerReceiver</span><span class="token">(</span><span class="token">socketActionAdapter</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #6f42c1;">connect</span><span class="token">(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-280"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-278">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-279'
	>
	It creates the socket that connects to a given IP address and port. In our case, these were predefined by the camera manufacturer. The method returns a so-called <em>IConnectionManager</em> object that we could use to check the connection status.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-283"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-281">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-282'
	>
	The command’s requests and responses come as a predefined set of bytes. Not to go into details about what each of the bytes represents, those interested can take a look at the <em>IReader</em> and <em>DashcamCommand</em> implementations that serve as a custom mapper for headers and payload in each of our requests and responses.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-285"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-php github-light" data-language="php" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #d73a49;">const</span><span class="token"> </span><span class="token" style="color: #005cc5;">val</span><span class="token"> </span><span class="token" style="color: #005cc5;">DATA_SIZE_START</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">8</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #d73a49;">const</span><span class="token"> </span><span class="token" style="color: #005cc5;">val</span><span class="token"> </span><span class="token" style="color: #005cc5;">DATA_SIZE_END</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">12</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #d73a49;">const</span><span class="token"> </span><span class="token" style="color: #005cc5;">val</span><span class="token"> </span><span class="token" style="color: #005cc5;">HEADER_LENGTH</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">12</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #005cc5;">val</span><span class="token"> </span><span class="token" style="color: #005cc5;">readerProtocol</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #d73a49;">object</span><span class="token"> </span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">IReaderProtocol</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 class="token" style="color: #005cc5;">override</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">getBodyLength</span><span class="token">(</span><span class="token" style="color: #6f42c1;">header</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">ByteArray</span><span class="token" style="color: #d73a49;">?</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #005cc5;">byteOrder</span><span class="token" style="color: #d73a49;">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">ByteOrder</span><span class="token" style="color: #d73a49;">?</span><span class="token">)</span><span class="token" style="color: #d73a49;">:</span><span class="token"> </span><span class="token" style="color: #d73a49;">Int</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: #005cc5;">header</span><span class="token" style="color: #d73a49;">?</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">let</span><span class="token"> </span><span class="token">{</span><span class="token"> </span><span class="token" style="color: #6f42c1;">BigInteger</span><span class="token">(</span><span class="token" style="color: #005cc5;">header</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">copyOfRange</span><span class="token">(</span><span class="token" style="color: #005cc5;">DATA_SIZE_START</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #005cc5;">DATA_SIZE_END</span><span class="token">)</span><span class="token">)</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">toInt</span><span class="token">(</span><span class="token">)</span><span class="token"> </span><span class="token">}</span><span class="token"> </span><span class="token" style="color: #d73a49;">?:</span><span class="token"> </span><span class="token" style="color: #005cc5;">0</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: #005cc5;">override</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">getHeaderLength</span><span class="token">(</span><span class="token">)</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">HEADER_LENGTH</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><span class="line"><span class="token" style="color: #005cc5;">sealed</span><span class="token"> </span><span class="token" style="color: #d73a49;">class</span><span class="token"> </span><span class="token" style="color: #6f42c1;">DashcamCommand</span><span class="token">(
</span></span><span class="line"><span class="token">   val code: String
</span></span><span class="line"><span class="token">) : ISendable </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: #005cc5;">override</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">parse</span><span class="token">(</span><span class="token">)</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">ByteArray</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">Hex</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">stringToBytes</span><span class="token">(</span><span class="token" style="color: #005cc5;">code</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;">class</span><span class="token"> </span><span class="token" style="color: #6f42c1;">GetSdCardInfo</span><span class="token">(val listener: (SdCardInfoResponse) -&gt; Unit) : DashcamCommand(&quot;00000001&quot; + &quot;07D0&quot; + &quot;000F&quot; + &quot;00000000&quot;)
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">   class StartRecording(val listener: () -&gt; Unit) : DashcamCommand(&quot;00000001&quot; + &quot;07D0&quot; + &quot;0010&quot; + &quot;00000004&quot; + &quot;00000001&quot;)
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">   object StopRecording : DashcamCommand(&quot;00000001&quot; + &quot;07D0&quot; + &quot;0010&quot; + &quot;00000004&quot; + &quot;00000000&quot;)
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">   class UpdateUserString(userString: String, val listener: () -&gt; Unit) :
</span></span><span class="line"><span class="token">       DashcamCommand(
</span></span><span class="line"><span class="token">           &quot;00000001&quot; +
</span></span><span class="line"><span class="token">               &quot;07D0&quot; +
</span></span><span class="line"><span class="token">               &quot;0012&quot; +
</span></span><span class="line"><span class="token">               String.format(&quot;%08x&quot;, userString.length + 1) +
</span></span><span class="line"><span class="token">               Hex.bytesToStringUppercase(&quot; $userString&quot;.toByteArray())
</span></span><span class="line"><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-288"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-286">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-287'
	>
	With the socket and protocol set up, all we needed to do is make a client that would use these implementations to communicate with the camera. In the following section, we are going to go over the most important methods from <em>DashcamClient</em>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-290"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-php github-light" data-language="php" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #6a737d;">//</span><span class="token" style="color: #6a737d;"> DashcamClient.kt</span><span class="token" style="color: #6a737d;">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #005cc5;">val</span><span class="token"> </span><span class="token" style="color: #005cc5;">commandQueue</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">mutableSetOf</span><span class="token" style="color: #d73a49;">&lt;</span><span class="token" style="color: #005cc5;">DashcamCommand</span><span class="token" style="color: #d73a49;">&gt;</span><span class="token">(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #005cc5;">currentlyExecutingCommand</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">DashcamCommand</span><span class="token" style="color: #d73a49;">?</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">
</span></span><span class="line"><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">enqueueCommand</span><span class="token">(</span><span class="token" style="color: #6f42c1;">command</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">DashcamCommand</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: #005cc5;">commandQueue</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">add</span><span class="token">(</span><span class="token" style="color: #005cc5;">command</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: #005cc5;">connectionManager</span><span class="token" style="color: #d73a49;">?</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">isConnect</span><span class="token"> </span><span class="token" style="color: #d73a49;">==</span><span class="token"> </span><span class="token" style="color: #005cc5;">true</span><span class="token">) </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #6f42c1;">executeNextCommand</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><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">executeNextCommand</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;">if</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #005cc5;">connectionManager</span><span class="token" style="color: #d73a49;">?</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">isConnect</span><span class="token"> </span><span class="token" style="color: #d73a49;">==</span><span class="token"> </span><span class="token" style="color: #005cc5;">false</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: #005cc5;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Socket disconnected. Aborting.</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;">return</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 class="token" style="color: #d73a49;">if</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #005cc5;">currentlyExecutingCommand</span><span class="token"> </span><span class="token" style="color: #d73a49;">!=</span><span class="token"> </span><span class="token" style="color: #005cc5;">null</span><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #005cc5;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Another command is currently executing. Aborting.</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;">return</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 class="token" style="color: #d73a49;">if</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #005cc5;">commandQueue</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">isEmpty</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">       </span><span class="token" style="color: #005cc5;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Command queue is empty. Aborting.</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;">return</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 class="token" style="color: #005cc5;">commandQueue</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">first</span><span class="token">(</span><span class="token">)</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">let</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: #005cc5;">currentlyExecutingCommand</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">it</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #6f42c1;">sendCommand</span><span class="token">(</span><span class="token" style="color: #005cc5;">it</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></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-293"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-291">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-292'
	>
	The commands need to be executed one at a time. The client uses a command queue so that all the commands can be kept and executed sequentially. Before executing a command we have to check that the connection is alive and there is no other command executing at the time. After executing a command we use the following method to make sure everything is ready for the next one.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-295"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-php github-light" data-language="php" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">dequeueCommand</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;">if</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #005cc5;">commandQueue</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">isEmpty</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">       </span><span class="token" style="color: #005cc5;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Command queue is empty. Aborting.</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;">return</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 class="token" style="color: #d73a49;">if</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #005cc5;">currentlyExecutingCommand</span><span class="token"> </span><span class="token" style="color: #d73a49;">!=</span><span class="token"> </span><span class="token" style="color: #005cc5;">null</span><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #005cc5;">commandQueue</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">remove</span><span class="token">(</span><span class="token" style="color: #005cc5;">currentlyExecutingCommand</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #005cc5;">currentlyExecutingCommand</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">   </span><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-298"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-296">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-297'
	>
	Finally, the following block of code shows how we send commands and receive responses from the camera.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-300"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-php github-light" data-language="php" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">sendCommand</span><span class="token">(</span><span class="token" style="color: #6f42c1;">command</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">ISendable</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: #005cc5;">connectionManager</span><span class="token" style="color: #d73a49;">?</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">send</span><span class="token">(</span><span class="token" style="color: #005cc5;">command</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;">private</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">processResponse</span><span class="token">(</span><span class="token" style="color: #6f42c1;">data</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">OriginalData</span><span class="token">,</span><span class="token"> </span><span class="token" style="color: #6f42c1;">currentCommand</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">DashcamCommand</span><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #6f42c1;">when</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #005cc5;">currentCommand</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: #005cc5;">is</span><span class="token"> </span><span class="token" style="color: #005cc5;">DashcamCommand</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">GetSdCardInfo</span><span class="token"> </span><span class="token" style="color: #d73a49;">-&gt;</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: #005cc5;">val</span><span class="token"> </span><span class="token" style="color: #005cc5;">sdCardInfo</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #6f42c1;">parseSdCardInfo</span><span class="token">(</span><span class="token" style="color: #005cc5;">data</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">bodyBytes</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">           </span><span class="token" style="color: #005cc5;">currentCommand</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">listener</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">invoke</span><span class="token">(</span><span class="token" style="color: #005cc5;">sdCardInfo</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: #005cc5;">is</span><span class="token"> </span><span class="token" style="color: #005cc5;">DashcamCommand</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">StartRecording</span><span class="token"> </span><span class="token" style="color: #d73a49;">-&gt;</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: #005cc5;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Recording started.</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: #005cc5;">currentCommand</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">listener</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">invoke</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: #005cc5;">is</span><span class="token"> </span><span class="token" style="color: #005cc5;">DashcamCommand</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">StopRecording</span><span class="token"> </span><span class="token" style="color: #d73a49;">-&gt;</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: #005cc5;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Recording stopped.</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" style="color: #005cc5;">is</span><span class="token"> </span><span class="token" style="color: #005cc5;">DashcamCommand</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">UpdateUserString</span><span class="token"> </span><span class="token" style="color: #d73a49;">-&gt;</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: #005cc5;">currentCommand</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">listener</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">invoke</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><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-303"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-301">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-302'
	>
	The socket connection adapter handles sending and receiving data from the camera. When the response arrives, it calls the <em>processResponse</em> method shown above. Every command takes a callback action in the constructor, invoked when the response arrives.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-306"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-304">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-305'
	>
	The <em>GetSdCardInfo</em> command takes an additional parameter in the callback, showing the SD card storage information.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-309"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-307">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-308'
	>
	The client and commands just described are enough to cover all of the camera operations during an exam. They work well even if the connection is lost during the exam because of the command queue that keeps all of the unexecuted commands.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-312"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-310">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-311'
	>
	When the connection is lost, the <em>WifiConnectionManager</em> attempts to re-establish connection and the commands are executed afterwards.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-315"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-313">
	<h3	class='typography typography--size-36-text js-typography block-heading__heading'
	data-id='es-314'
	>
	Camera feed streaming client</h3></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-318"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-316">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-317'
	>
	The second client we made is called <em>LivestreamManager</em>. It transfers the stream from the camera to the app in real-time. There are 2 use-cases for this feature in our application:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-321"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-319">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-320'
	>
	<li>Camera preview screen – after connecting to a camera there is a screen with a live feed, so the examiner can make sure they are connected to the right camera. Since the exams usually start from the same place, there can be several cameras nearby and it’s possible to connect to the wrong one.</li></ul></div>	</div>

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

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-323">
	<picture class="image__picture block-media__image-picture">
								
			<source
				srcset=https://infinum.com/uploads/2021/07/hak-camera-integration-2-1400x840.webp				media='(max-width: 699px)'
				type=image/webp								height="840"
												width="1400"
				 />
												<img
					src="https://infinum.com/uploads/2021/07/hak-camera-integration-2.webp"
					class="image__img block-media__image-img"
					alt=""
										height="960"
															width="1600"
										loading="lazy"
					 />
					</picture>

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

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-327"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-325">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-326'
	>
	<li>Driving exam screen – we built in a small live feed window in the app drawer just in case the examiner wants to check the recording.</li></ul></div>	</div>

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

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-329">
	<picture class="image__picture block-media__image-picture">
								
			<source
				srcset=https://infinum.com/uploads/2021/01/hak-camera-integration-3-1400x840.webp				media='(max-width: 699px)'
				type=image/webp								height="840"
												width="1400"
				 />
												<img
					src="https://infinum.com/uploads/2021/01/hak-camera-integration-3.webp"
					class="image__img block-media__image-img"
					alt=""
										height="960"
															width="1600"
										loading="lazy"
					 />
					</picture>

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

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-333"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-331">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-332'
	>
	<em>LivestreamManager</em> features:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-336"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-334">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-335'
	>
	<li>Uses <em>WifiConnectionManager</em> to manage connection to the camera</li><li>IP Cam View – library to display stream from the camera endpoint</li><li>Observes the lifecycle of an activity or a fragment and resumes or pauses the streaming accordingly</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-339"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-337">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-338'
	>
	IP Cam View library offers a series of classes that help with subscribing to the stream and displaying it on screen. Streaming can be heavy on the device’s resources, so we added an observer on the screen lifecycle. That way we can stop streaming when the app is not in the foreground. <em>LivestreamManager</em> implements <em>LifecycleObserver</em>, which means it is tied to the lifecycle events of its host component and can react to them, given that the component implements <em>LifecycleOwner</em>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-342"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-340">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-341'
	>
	Similar to <em>DashcamClient</em>, <em>LivestreamManager</em> uses <em>WifiConnectionManager</em> to handle camera connection. If the camera disconnects, the app will try to re-establish the connection and notify <em>LivestreamManager</em> so it could resume streaming.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-345"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-343">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-344'
	>
	Here are some significant code blocks that show how <em>LivestreamManager</em> works.</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-code">
	<pre class="phiki language-php github-light" data-language="php" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #005cc5;">streamSubscription</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">Subscription</span><span class="token" style="color: #d73a49;">?</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" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #d73a49;">var</span><span class="token"> </span><span class="token" style="color: #005cc5;">stream</span><span class="token" style="color: #d73a49;">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">MjpegInputStream</span><span class="token" style="color: #d73a49;">?</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">
</span></span><span class="line"><span class="token" style="color: #005cc5;">init</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: #005cc5;">dashcamWifiConnectionManager</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">addListener</span><span class="token">(</span><span class="token" style="color: #005cc5;">dashcamWifiConnectionListener</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #005cc5;">lifecycleOwner</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">lifecycle</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">addObserver</span><span class="token">(</span><span class="token" style="color: #005cc5;">this</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">@</span><span class="token" style="color: #6f42c1;">OnLifecycleEvent</span><span class="token">(</span><span class="token" style="color: #005cc5;">Lifecycle</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">Event</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">ON_RESUME</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">openLivestream</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;">if</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #005cc5;">stream</span><span class="token"> </span><span class="token" style="color: #d73a49;">!=</span><span class="token"> </span><span class="token" style="color: #005cc5;">null</span><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #005cc5;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Stream already opened. Aborting.</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;">return</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 class="token" style="color: #d73a49;">if</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #005cc5;">streamSubscription</span><span class="token"> </span><span class="token" style="color: #d73a49;">!=</span><span class="token"> </span><span class="token" style="color: #005cc5;">null</span><span class="token">)</span><span class="token"> </span><span class="token">{</span><span class="token">
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #005cc5;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Livestream already requested. Aborting.</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;">return</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 class="token" style="color: #d73a49;">if</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #6f42c1;">isLifecycleOwnerResumed</span><span class="token">(</span><span class="token">)</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">not</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">       </span><span class="token" style="color: #005cc5;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Lifecycle owner not in resumed state. Aborting.</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;">return</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 class="token" style="color: #d73a49;">if</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #005cc5;">dashcamWifiConnectionManager</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">isConnectedToDashcam</span><span class="token">(</span><span class="token">)</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">not</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">       </span><span class="token" style="color: #005cc5;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Device not connected to dashcam. Aborting.</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;">return</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 class="token" style="color: #005cc5;">streamSubscription</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">mjpeg</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">open</span><span class="token">(</span><span class="token" style="color: #005cc5;">DashcamApiService</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">DASHCAM_LIVESTREAM_ENDPOINT</span><span class="token">)</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">subscribe</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" style="color: #005cc5;">stream</span><span class="token"> </span><span class="token" style="color: #d73a49;">-&gt;</span><span class="token">
</span></span><span class="line"><span class="token">           </span><span class="token" style="color: #005cc5;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Stream opened.</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: #005cc5;">streamSubscription</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">           </span><span class="token" style="color: #d73a49;">if</span><span class="token"> </span><span class="token">(</span><span class="token" style="color: #6f42c1;">isLifecycleOwnerResumed</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">               </span><span class="token" style="color: #005cc5;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Lifecycle owner in resumed state.</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: #005cc5;">this</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">stream</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">stream</span><span class="token">
</span></span><span class="line"><span class="token">               </span><span class="token" style="color: #005cc5;">livestreamListener</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">onLivestreamAvailable</span><span class="token">(</span><span class="token" style="color: #005cc5;">stream</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" 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: #005cc5;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">Lifecycle owner not in resumed state. Closing stream.</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;">closeLivestream</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 class="token">
</span></span><span class="line"><span class="token">       </span><span class="token">{</span><span class="token"> </span><span class="token" style="color: #005cc5;">throwable</span><span class="token"> </span><span class="token" style="color: #d73a49;">-&gt;</span><span class="token">
</span></span><span class="line"><span class="token">           </span><span class="token" style="color: #005cc5;">log</span><span class="token">(</span><span class="token" style="color: #032f62;">&quot;</span><span class="token" style="color: #032f62;">onError called in startLivestream. Retrying.</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: #005cc5;">streamSubscription</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">           </span><span class="token" style="color: #6f42c1;">openLivestream</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><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">@</span><span class="token" style="color: #6f42c1;">OnLifecycleEvent</span><span class="token">(</span><span class="token" style="color: #005cc5;">Lifecycle</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">Event</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">ON_PAUSE</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">closeLivestream</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: #005cc5;">stream</span><span class="token" style="color: #d73a49;">?</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">let</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: #005cc5;">livestreamListener</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">onLivestreamUnavailable</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: #005cc5;">it</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">close</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: #005cc5;">stream</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">   </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;">private</span><span class="token"> </span><span class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">isLifecycleOwnerResumed</span><span class="token">(</span><span class="token">)</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #005cc5;">lifecycleOwner</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">lifecycle</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">currentState</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #6f42c1;">isAtLeast</span><span class="token">(</span><span class="token" style="color: #005cc5;">Lifecycle</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">State</span><span class="token" style="color: #d73a49;">.</span><span class="token" style="color: #005cc5;">RESUMED</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-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'
	>
	Stream events are sent to the activity or fragment through the <em>LivestreamListener</em> interface since LivestreamManager is not a UI component. The listener interface handles two possible cases, as can be seen in the following code block.</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-php github-light" data-language="php" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">interface</span><span class="token"> </span><span class="token" style="color: #6f42c1;">LivestreamListener</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 class="token" style="color: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">onLivestreamAvailable</span><span class="token">(</span><span class="token" style="color: #6f42c1;">stream</span><span class="token">:</span><span class="token"> </span><span class="token" style="color: #005cc5;">MjpegInputStream</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: #005cc5;">fun</span><span class="token"> </span><span class="token" style="color: #6f42c1;">onLivestreamUnavailable</span><span class="token">(</span><span class="token">)</span><span class="token">
</span></span><span class="line"><span class="token">}</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-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 the stream is connected and passed to the screen, it is passed as a source to <em>MjpegSurfaceView</em>, which is just a custom view for displaying a video stream. It is also a part of the library that we used to subscribe to the stream.</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-heading" data-id="es-356">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-357'
	>
	Key takeaways for technical implementation</h2></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'
	>
	The sections above explain the technical side of what is needed for successfully recording a driving exam.</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-paragraph" data-id="es-362">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-363'
	>
	These are the key takeaways:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-367"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-365">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-366'
	>
	<li>WiFi Network Specifier API helps us to find the camera and connect.</li><li>Our app now handles WiFi connection like a pro, disconnects, timeouts, you name it.</li><li>We created a client to send commands to the camera and receive responses.</li><li>Displaying the camera’s live feed from over WiFi is wrapped nicely in the <em>LivestreamManager</em></li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-370"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-368">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-369'
	>
	It was a challenging but very educational journey. We implemented a feature that an average Android developer doesn’t see every day. Connecting an application to a camera was not a simple task, considering all the features, commands, streaming, etc. WiFi Network Specifier really saved us some work, because the old network scanning API was very difficult to work with.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-373"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-371">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-372'
	>
	We did have to move the minimum Android version to API level 29, but luckily for us this specific client uses only one tablet model and that one has an Android 10 update.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-376"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-374">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-375'
	>
	OkSocket and IP Cam View did their part in transferring the data between the application and camera and we took all the measures to keep the connection alive or reconnect if needed.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-379"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-377">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-378'
	>
	Ensuring greater transparency</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-382"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-380">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-381'
	>
	Thanks to camera integration, the driving examination app now has an extra feature. Exams are recorded and documented through the app, so if there happens to be a complaint about an exam’s result, the examiner, the candidate, and the Croatian Automobile Club now have solid evidence for everything that happened during the exam.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-385"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-383">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-384'
	>
	Digitizing the driving evaluation process means less paperwork and more transparency – and hopefully many more great drivers on the road!</p></div>	</div>
</div>
</div>		</div>
	</div><p>The post <a href="https://infinum.com/blog/hak-camera-integration/">Camera Integration in the HAK Driving Exam App</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</content:encoded>
			</item>
		
	</channel>
</rss>