<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/mobile/</id>
  <link href="https://www.endpointdev.com/blog/tags/mobile/"/>
  <link href="https://www.endpointdev.com/blog/tags/mobile/" rel="self"/>
  <updated>2023-08-29T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>Ubuntu Touch on a Galaxy S7 &amp; a Pixel 3a</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2023/08/ubuntu-touch-on-galaxy-s7-a-pixel-3a/"/>
      <id>https://www.endpointdev.com/blog/2023/08/ubuntu-touch-on-galaxy-s7-a-pixel-3a/</id>
      <published>2023-08-29T00:00:00+00:00</published>
      <author>
        <name>Seth Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2023/08/ubuntu-touch-on-galaxy-s7-a-pixel-3a/arabesque_with_knight-crop.webp&#34; alt=&#34;A dark print on very old paper shows an armored knight on a horse, surrounded completely by swirling floral patterns. In a few places the pattern evolves into a flower or what appears to be a curved, long bird head.&#34;&gt;&lt;/p&gt;
&lt;!-- Image: Arabesque with Knight, Italian, niello print, unknown date. Retrieved from https://www.nga.gov/collection/art-object-page.4541.html --&gt;
&lt;p&gt;I have a shoebox in my closet I call my &amp;ldquo;phone graveyard.&amp;rdquo; At times I&amp;rsquo;ve had five or more old phones in there, in various states of decay—some have fully shattered screens, some broken USB ports, etc. But some still have quite usable hardware, their main drawback being how slowly they run modern versions of Android or iOS, and the lack of support for modern features.&lt;/p&gt;
&lt;p&gt;It has always troubled me to have such incredible devices, with way more computing power than, say, a Raspberry Pi, gather dust because of software limitations. Even if I don&amp;rsquo;t use an old phone for daily use any more, what if I could use it as a DNS server (like a Pi-hole), or as a camera or media player?&lt;/p&gt;
&lt;p&gt;When I heard about Ubuntu Touch, it seemed like the perfect OS to bring back some long-term functionality to these old devices. Originally created by Canonical, it was soon abandoned but revived by UBports, who started community development in 2015. They actively maintain the OS for around 80 devices, including two I have in my phone graveyard: a Samsung Galaxy S7 and a Google Pixel 3a.&lt;/p&gt;
&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;
&lt;p&gt;Installing Ubuntu Touch is quite straightforward; the UBports installer does most of the heavy lifting. See the &lt;a href=&#34;https://devices.ubuntu-touch.io/installer/&#34;&gt;UBports website&lt;/a&gt; for instructions.&lt;/p&gt;
&lt;p&gt;For the Pixel 3a, I had to flash an older version of the stock OS before running the UBports installer. The Ubuntu Touch &lt;a href=&#34;https://devices.ubuntu-touch.io/device/sargo&#34;&gt;device page&lt;/a&gt; for the Pixel 3a specifies which version to flash under &amp;ldquo;Preparatory Step,&amp;rdquo; but this seems to be in a different place for each device. Read the device-specific instructions carefully before installing.&lt;/p&gt;
&lt;p&gt;See also:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This useful &lt;a href=&#34;https://android.gadgethacks.com/how-to/complete-guide-flashing-factory-images-android-using-fastboot-0175277/&#34;&gt;guide&lt;/a&gt; from Gadget Hacks on flashing a factory image&lt;/li&gt;
&lt;li&gt;Android&amp;rsquo;s &lt;a href=&#34;https://developer.android.com/studio/releases/platform-tools&#34;&gt;SDK platform tools&lt;/a&gt;, including fastboot&lt;/li&gt;
&lt;li&gt;Android&amp;rsquo;s docs for &lt;a href=&#34;https://source.android.com/docs/core/architecture/bootloader/locking_unlocking&#34;&gt;locking/unlocking the bootloader&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;usability&#34;&gt;Usability&lt;/h3&gt;
&lt;p&gt;The native app landscape for Ubuntu Touch is, as you would expect, pretty limited. You won&amp;rsquo;t be able to play most DRM content, and while there are web apps for Uber, Discord, and others, they tend to be various levels of buggy. You can see the apps, including helpful usability reports, at &lt;a href=&#34;https://open-store.io/&#34;&gt;open-store.io&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are plenty of bugs I encountered while briefly testing, like having no volume indicator when using volume keys and not seeing the Libertine installation which should&amp;rsquo;ve been there, making it hard to install programs with the terminal emulator.&lt;/p&gt;
&lt;p&gt;Audio playback had fairly frequent interruptions, at least on my Pixel 3a, making it not viable as a music player. However, for audiobooks or podcasts this probably wouldn&amp;rsquo;t be a big deal.&lt;/p&gt;
&lt;p&gt;However, most of the base functionality for web browsing, photos, and media playback seemed to work well.&lt;/p&gt;
&lt;p&gt;The inclusion of a fully-featured terminal emulator expands the possibilities greatly, allowing your old phone to act in place of a Raspberry Pi for projects like a &lt;a href=&#34;/blog/2020/12/pihole-great-holiday-gift/&#34;&gt;Pi-hole&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The system is, by default, in read-only mode, requiring containers (the intent is to run the custom container system, &lt;a href=&#34;https://docs.ubports.com/en/latest/userguide/dailyuse/libertine.html&#34;&gt;Libertine&lt;/a&gt;) to run most apps. However, you can change it to read-write for a more traditional Linux system.
For more info on terminal capability, see &lt;a href=&#34;https://ubports.com/en/blog/ubports-news-1/post/terminal-chapter-1-3082&#34;&gt;the UBports blog&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;While I wouldn&amp;rsquo;t want to run Ubuntu Touch as my only smartphone OS, it is an exciting option for secondary or old devices, especially if you want to contribute to open-source projects and help out the community!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Appium: Automated Mobile Testing</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2021/06/appium-automated-mobile-testing/"/>
      <id>https://www.endpointdev.com/blog/2021/06/appium-automated-mobile-testing/</id>
      <published>2021-06-30T00:00:00+00:00</published>
      <author>
        <name>Couragyn Chretien</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2021/06/appium-automated-mobile-testing/clouds.jpg&#34; alt=&#34;Clouds and a river&#34;&gt;&lt;/p&gt;
&lt;!-- Image by Seth Jensen --&gt;
&lt;p&gt;First impressions are everything. You can have the best, most robust application in the world, but if it looks like it’s from 2004 most users won’t give it a second look. Automated testing can help ensure that the app the user sees is consistent and fully functional no matter the iteration.&lt;/p&gt;
&lt;p&gt;Selenium, Cypress, and other automated testing suites have become more and more popular for webapps. This trend has not carried over to mobile native app testing. This may be a bit surprising, as a fully functional frontend can be the difference between a professional-feeling app and a hacky one.&lt;/p&gt;
&lt;p&gt;There are many frameworks that can be used to test mobile applications (Appium, UI Automator, Robotium, XCUITest, SeeTest, and TestComplete to name a few), but today we’ll be focusing on &lt;a href=&#34;https://appium.io/&#34;&gt;Appium&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Appium is an open source framework that’s easy to use out of the box. It can be used to test many versions of many different mobile OSes. It’s a one-stop shop to ensure your users are on the same page, no matter the platform.&lt;/p&gt;
&lt;h3 id=&#34;installation-and-setup-for-linux&#34;&gt;Installation and setup for Linux&lt;/h3&gt;
&lt;h4 id=&#34;general&#34;&gt;General&lt;/h4&gt;
&lt;p&gt;We will be testing the To-Do List app from the Google Play Store. What we’re testing and for which platform isn’t important, since we’re here to learn about the Appium methodology that can be applied to any mobile app.&lt;/p&gt;
&lt;h4 id=&#34;avd-android-emulator&#34;&gt;AVD Android emulator&lt;/h4&gt;
&lt;p&gt;We will be using an AVD (Android Virtual Device) to test our app. There are many out there but I recommend &lt;a href=&#34;https://developer.android.com/studio&#34;&gt;Android Studio’s&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once it’s running, just click &lt;code&gt;Configure &amp;gt; AVD Manager&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2021/06/appium-automated-mobile-testing/avd-manager.jpg&#34; alt=&#34;AVD Manager&#34;&gt;&lt;/p&gt;
&lt;p&gt;Either create a new AVD or use the default one and start it up.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2021/06/appium-automated-mobile-testing/avd-view.jpg&#34; alt=&#34;AVD View&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;appium&#34;&gt;Appium&lt;/h4&gt;
&lt;p&gt;Download Appium from &lt;a href=&#34;https://github.com/appium/appium-desktop/releases/tag/v1.21.0&#34;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Appium can be run through the command line but we’re going to use the desktop app here. This gives you the ability to record a session and capture elements for automated tests.&lt;/p&gt;
&lt;p&gt;Once it’s running, all we have to do is click &lt;code&gt;Start Server&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2021/06/appium-automated-mobile-testing/start-appium.png&#34; alt=&#34;Start Appium&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;ruby-and-rspec&#34;&gt;Ruby and RSpec&lt;/h4&gt;
&lt;p&gt;We will be using Ruby and &lt;a href=&#34;https://rspec.info/&#34;&gt;RSpec&lt;/a&gt; to run our tests. At the top level of our project we need to add these lines to our Gemfile:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/Gemfile&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;source &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;https://www.rubygems.org&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gem &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;appium_lib&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;~&amp;gt; 11.2&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gem &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;appium_lib_core&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;~&amp;gt; 4.2&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Make sure Ruby is installed, then run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ gem install rspec
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ bundle install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We also need to configure RSpec to find the correct AVD and APK to test.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/spec/spec_settings.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ANDROID_PACKAGE&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;io.appium.android.apis&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;android_caps&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;caps&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;# These settings match the settings for your AVD. They can be found in the AVD Manager.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;platformName&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Android&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;platformVersion&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;11&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;deviceName&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Pixel_3a_API_30_x86&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;#  This points to the apk you are testing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;app&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;./To-do_list.apk&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;automationName&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;UIAutomator2&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;appium_lib&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;wait&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;60&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally we need to create our test. Since we don’t know the elements yet we can start with a blank template.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/spec/TaskCRUD&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;spec_settings&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;appium_lib&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;describe &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Tests basic CRUD functions of Task application&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  before(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:all&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@driver&lt;/span&gt; = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Appium&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Driver&lt;/span&gt;.new(android_caps, &lt;span style=&#34;color:#080&#34;&gt;true&lt;/span&gt;).start_driver
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  after(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:all&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@driver&lt;/span&gt;&amp;amp;.quit
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  it &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;...&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;putting-it-all-together&#34;&gt;Putting it all together&lt;/h4&gt;
&lt;p&gt;Let’s boot it up and make sure everything is running as expected.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run an AVD from Android Studio&lt;/li&gt;
&lt;li&gt;Start the Appium Server&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;rspec&lt;/code&gt; in the project root&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Even though there is no test written it should still run and pass the template test. Running it the first time will save the APK into the AVD virtual memory. We can now access the app on the AVD emulator and record tests.&lt;/p&gt;
&lt;h3 id=&#34;recording-a-test-with-inspector&#34;&gt;Recording a test with Inspector&lt;/h3&gt;
&lt;p&gt;Open the Appium window that has the server running and click Start Inspector Session:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2021/06/appium-automated-mobile-testing/start-inspector.png&#34; alt=&#34;Start Inspector&#34;&gt;&lt;/p&gt;
&lt;p&gt;Under &lt;code&gt;Attach to Session...&lt;/code&gt; you will see a dropdown containing the RSpec test we just tried to run. If it’s not here try running &lt;code&gt;rspec&lt;/code&gt; again. Select this and click the &lt;code&gt;Attach to Session&lt;/code&gt; button.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2021/06/appium-automated-mobile-testing/attach-to-session.png&#34; alt=&#34;Attach to Session&#34;&gt;&lt;/p&gt;
&lt;p&gt;We are now connected to the Android Emulator via the Inspector. We can click on elements to see their properties and interact with them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2021/06/appium-automated-mobile-testing/view-element.jpg&#34; alt=&#34;View Element&#34;&gt;&lt;/p&gt;
&lt;p&gt;If we click the top record button it will save our actions and allow us to import them into a test.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2021/06/appium-automated-mobile-testing/session-recording.jpg&#34; alt=&#34;Session Recording&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;running-a-test&#34;&gt;Running a test&lt;/h3&gt;
&lt;p&gt;Let’s take what we got from the Inspector and populate our template test.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/spec/TaskCRUD&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;spec_settings&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;appium_lib&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;describe &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Tests basic CRUD functions of Task application&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  before(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:all&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@driver&lt;/span&gt; = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Appium&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Driver&lt;/span&gt;.new(android_caps, &lt;span style=&#34;color:#080&#34;&gt;true&lt;/span&gt;).start_driver
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  after(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:all&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@driver&lt;/span&gt;&amp;amp;.quit
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  it &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;creates a task and verifies its name&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   el1 = &lt;span style=&#34;color:#33b&#34;&gt;@driver&lt;/span&gt;.find_elements(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:id&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;todolist.scheduleplanner.dailyplanner.todo.reminders:id/zk&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    el1.click
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    el2 = &lt;span style=&#34;color:#33b&#34;&gt;@driver&lt;/span&gt;.find_elements(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:id&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;todolist.scheduleplanner.dailyplanner.todo.reminders:id/xd&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    el2.click
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    el3 = &lt;span style=&#34;color:#33b&#34;&gt;@driver&lt;/span&gt;.find_elements(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:id&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;todolist.scheduleplanner.dailyplanner.todo.reminders:id/wt&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    el3.send_keys &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;task_1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    el4 = &lt;span style=&#34;color:#33b&#34;&gt;@driver&lt;/span&gt;.find_elements(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:id&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;todolist.scheduleplanner.dailyplanner.todo.reminders:id/wr&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    el4.click
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    el5 = &lt;span style=&#34;color:#33b&#34;&gt;@driver&lt;/span&gt;.find_elements(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:xpath&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/androidx.recyclerview.widget.RecyclerView/android.widget.LinearLayout[3]/android.widget.TextView&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    el5.click
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    el6 = &lt;span style=&#34;color:#33b&#34;&gt;@driver&lt;/span&gt;.find_elements(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:id&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;todolist.scheduleplanner.dailyplanner.todo.reminders:id/wo&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    el6.click
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    el7 = &lt;span style=&#34;color:#33b&#34;&gt;@driver&lt;/span&gt;.find_elements(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:xpath&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout/androidx.recyclerview.widget.RecyclerView&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    el7.click
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    el8 = &lt;span style=&#34;color:#33b&#34;&gt;@driver&lt;/span&gt;.find_elements(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:id&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;todolist.scheduleplanner.dailyplanner.todo.reminders:id/xl&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    expect(el8.text).to eql &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Test-text-1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we run it with the &lt;code&gt;rspec&lt;/code&gt; command we can watch the test in progress on the AVD and then we receive a success message!&lt;/p&gt;
&lt;h3 id=&#34;whats-next&#34;&gt;What’s next?&lt;/h3&gt;
&lt;h4 id=&#34;additional-tests&#34;&gt;Additional tests&lt;/h4&gt;
&lt;p&gt;Tests should be written to cover as many aspects of the app as possible. The main focus should be on parts of the app that don’t change very often. Tests written for something in progress or that is due for a facelift will need to be rewritten when the time comes.&lt;/p&gt;
&lt;h4 id=&#34;test-with-other-devices&#34;&gt;Test with other devices&lt;/h4&gt;
&lt;p&gt;An app can look perfect on a Galaxy S20 but look awful on a Pixel 5. That’s why it’s important to try many different AVD versions for your app. Android allows you to create many phone types that run many different firmware versions.&lt;/p&gt;
&lt;h4 id=&#34;cicd-integration&#34;&gt;CI/​CD integration&lt;/h4&gt;
&lt;p&gt;Manual testing is great but nothing beats automation. These tests should ideally be integrated into a continuous integration and continuous delivery (CI/​CD) platform. This way each build will be automatically vetted for bugs.&lt;/p&gt;
&lt;p&gt;For more info, check out this &lt;a href=&#34;https://circleci.com/blog/ci-for-mobile-app-development/&#34;&gt;guide for integrations with CircleCI&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Mobile Device and Application Management (MAM vs. MDM)</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/12/mam-vs-mdm/"/>
      <id>https://www.endpointdev.com/blog/2017/12/mam-vs-mdm/</id>
      <published>2017-12-20T00:00:00+00:00</published>
      <author>
        <name>Joe Marrero</name>
      </author>
      <content type="html">
        &lt;p&gt;Businesses of all sizes have been increasingly using mobile devices for all kinds of activities. Some of these activities are pretty common, like being able to read and respond to work email and manage work calendars. On the other hand, some companies are using mobile devices for more specific niche activities like checking in customers, using device cameras to read barcodes and UPC stickers, or even temporarily storing sensitive business related data.&lt;/p&gt;
&lt;p&gt;As a result of the proliferation of mobile devices for work use, corporations and smaller businesses are finding the need the exhibit finer control over how their employees and customers use their devices. This is where mobile device management (MDM) and mobile application management (MAM) really shine and help solve many of these types of problems.&lt;/p&gt;
&lt;h3 id=&#34;what-is-mdm&#34;&gt;What is MDM?&lt;/h3&gt;
&lt;p&gt;Mobile device management (MDM) is a software solution that allows organizations to manage the maintenance, deployment, and configuration of mobile devices that are issued to members of the organization. All of this magic tends to be done via a &lt;em&gt;store&lt;/em&gt; or &lt;em&gt;portal&lt;/em&gt; application that users download onto their devices.&lt;/p&gt;
&lt;p&gt;The app begins an enrollment process when the user enters their credentials that the organization previously issued to the user (usually the user’s email and password). After the device is enrolled, the user gets prompted to accept if the device should be managed as a &lt;em&gt;work&lt;/em&gt; device. After the enrollment completes, the store app can push configuration, some device level policies for enforcement, and any required applications.&lt;/p&gt;
&lt;p&gt;Some of the useful things you can do with MDM:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Install apps autonomously:&lt;/strong&gt; Some corporations have their own suite of mobile applications that they require all employees to use. MDM allows organizations to push down required apps to a device. When a newer app is available, the new app gets pushed down to devices automatically. This makes it easier to deploy apps to all employees and ensure users have the latest apps on their devices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Configuration:&lt;/strong&gt; Push down sensitive or complex network configuration. Organizations can push down WiFi and VPN configurations. This is especially useful for allowing users to connect to work networks that have hidden SSIDs. You can also enforce use of PIN, and other security features.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Policy Enforcement:&lt;/strong&gt; Set global policies that can affect all or a portion of devices; Prevent third party apps from being installed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auditing:&lt;/strong&gt; See what devices have done, what versions of apps are deployed, and how a device has used your network.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scale up:&lt;/strong&gt; MDM scales up nicely for thousands of devices. If you find yourself updating devices one at time then you definitely need an MDM solution.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;what-is-mam&#34;&gt;What is MAM?&lt;/h3&gt;
&lt;p&gt;Mobile application management (MAM) is a technology that primarily focuses on enforcing policies on mobile applications. To be able to have this finer level control over applications, an organization needs to &lt;em&gt;wrap&lt;/em&gt; the application.&lt;/p&gt;
&lt;p&gt;Wrapping is the process of injecting code into an application despite not even having access to the application’s source code. The injected code can then listen for events that are related to certain policies. For example, if an application is not allowed to open the camera application, then the injected code will listen for the corresponding events that will bring up the camera.&lt;/p&gt;
&lt;p&gt;How this is done is different on Android and iOS because the platforms handle these kinds things quite differently.&lt;/p&gt;
&lt;p&gt;Some of the useful things you can do with MDM:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Block certain applications from being able to handle certain types of data.&lt;/li&gt;
&lt;li&gt;Enforce filesystem level encryption even if the application was never designed to use encryption.&lt;/li&gt;
&lt;li&gt;Detect if an application is running on a rooted device and kill the app if desired.&lt;/li&gt;
&lt;li&gt;Force an application to use a VPN all the time or when outside of the allowed networks.&lt;/li&gt;
&lt;li&gt;Block devices from accessing the Internet or the local loop-back addresses (i.e. 127.0.0.1 or ::1).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;what-are-some-mam-and-mdm-vendors&#34;&gt;What are some MAM and MDM vendors?&lt;/h3&gt;
&lt;p&gt;Some of the major vendors of MAM and MDM solutions are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.citrix.com/products/xenmobile/&#34;&gt;XenMobile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.air-watch.com&#34;&gt;AirWatch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.mobileiron.com&#34;&gt;MobileIron&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;XenMobile and AirWatch are robust and mature MAM and MDM solutions. With respect to features, both offer single-sign on (SSO), a suite of productivity apps (email, browser, etc.), and mobile content management.&lt;/p&gt;
&lt;p&gt;XenMobile’s email client is similar in capabilities to AirWatch’s email client. However, AirWatch also allows management of the native email clients on Android and iOS which tends to be buggier. Furthermore, AirWatch’s productivity apps may require additional VMware product licenses to unlock some of these capabilities.&lt;/p&gt;
&lt;p&gt;In terms of the administration web app, XenMobile is a bit more complicated than AirWatch and may require a few tech support calls to get configured just right. Both products require mobile apps to be &lt;em&gt;wrapped&lt;/em&gt; with a &lt;em&gt;wrapping utility&lt;/em&gt; before they can be uploaded to the management console. And both offer SDKs that allow organizations to build apps that have MAM capabilities without needing to be wrapped.&lt;/p&gt;
&lt;p&gt;Also, both products have great VPN capabilities. If your organization is already using Netscaler than it might make more sense to use XenMobile. Netscaler appears to be more feature-rich compared to VMware Tunnel, but Netscaler is also very hard to configure. With respect to price, AirWatch tends to be cheaper for smaller organizations than XenMobile because XenMobile offers fixed price tiers where as AirWatch has a per-device pricing model.&lt;/p&gt;
&lt;p&gt;MobileIron is a bit less developed than XenMobile and AirWatch. However, this also makes it a bit more simpler and easier to administrate. In addition, MobileIron lacks a managed email application compared to XenMobile and AirWatch.&lt;/p&gt;
&lt;h3 id=&#34;need-help&#34;&gt;Need Help?&lt;/h3&gt;
&lt;p&gt;End Point has tons of experience with MAM and MDM. If your organization needs expert-level consulting then we are available to help you!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Case Study: Responsive Design Return on Investment</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/07/case-study-responsive-design-return-on/"/>
      <id>https://www.endpointdev.com/blog/2016/07/case-study-responsive-design-return-on/</id>
      <published>2016-07-21T00:00:00+00:00</published>
      <author>
        <name>Elizabeth Garrett Christensen</name>
      </author>
      <content type="html">
        &lt;p&gt;Responsive design has been a hot topic in the e-commerce world for several years now. End Point has worked on many sites over the last few years to transition clients to a responsive design website model. While many large sized retailers have already transitioned to a responsive design, there are many smaller e-commerce sites that are still on an older design model and I would like to show that the return on investment for those stores is still noteworthy.&lt;/p&gt;
&lt;p&gt;The lead of our Interchange team, Greg Hanson, led a responsive design project and I’d like to summarize that work on our blog. For confidentiality, I am leaving out the client’s name in this case.&lt;/p&gt;
&lt;h3 id=&#34;why-go-responsive&#34;&gt;Why Go Responsive?&lt;/h3&gt;
&lt;p&gt;There are two main reasons every e-commerce website, even a small one, needs to become responsive:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your customers&lt;/li&gt;
&lt;li&gt;Google&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The march toward mobile sales at this point is undeniable and unstoppable. As more and more people become comfortable using their phones and tablets to purchase things, the bigger this market share will become. Also, Google has begun de-prioritizing websites that do not cater to mobile users. If you are waiting to go responsive because of budget, you might surprised to learn how dramatically the mobile revenue increased for the customer in this case.&lt;/p&gt;
&lt;h3 id=&#34;goals&#34;&gt;Goals&lt;/h3&gt;
&lt;p&gt;This client is a small e-commerce business with a heavy ‘on’ season and ‘off’ season where the business owners could focus on this project. They wanted:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To accommodate the increasing mobile user base; they knew it was there from looking at their Google Analytics.&lt;/li&gt;
&lt;li&gt;To increase revenue from mobile users. They could see that they had a 10% mobile user base, but they were converting only a small percentage.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;end-points-strategy&#34;&gt;End Point’s strategy&lt;/h3&gt;
&lt;p&gt;Our strategy with this small client, to minimize impact to the business and cost was to:&lt;/p&gt;
&lt;h4 id=&#34;use-bootstrap&#34;&gt;Use Bootstrap&lt;/h4&gt;
&lt;p&gt;Bootstrap is one of many front end frameworks that allows you to create a design template and roll that out to all the pages within a website without having to re-code each and every page in a website. This dramatically increases the speed and decreases cost.&lt;/p&gt;
&lt;h4 id=&#34;break-up-the-work-into-segments&#34;&gt;Break up the work into segments&lt;/h4&gt;
&lt;p&gt;In this case, we had a three phase approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Phase I, re­design the site using Bootstrap but still at fixed width&lt;/li&gt;
&lt;li&gt;Phase II, site checkout sequence changed to responsive&lt;/li&gt;
&lt;li&gt;Phase III, entire site responsive&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;the-project&#34;&gt;The Project&lt;/h3&gt;
&lt;p&gt;Converting the site to Bootstrap was the biggest chunk of the time and money spent on this project. Since we knew this would be the foundation for the changes to come, we took our time getting it right, keeping the client involved every step of the way. We didn’t want to get in the way of their busy season either, so we completed that project and waited a half a year to begin the next piece.&lt;/p&gt;
&lt;p&gt;The second step was updating checkout sequence to be responsive since this was arguably the hardest part about using the non-responsive site on a mobile device. The client considered this piece top priority. Plus, since it was only a few pages, it allowed us all to better understand the scope of coding the rest of the site and give the client an accurate picture of the budget.&lt;/p&gt;
&lt;p&gt;Last, once we had a responsive checkout sequence, we changed the rest of the internal pages to be responsive and rolled out the entire site as responsive and mobile friendly.&lt;/p&gt;
&lt;h3 id=&#34;results&#34;&gt;Results&lt;/h3&gt;
&lt;p&gt;The first time we looked at the analytics following the responsive conversion, we were &lt;strong&gt;shocked&lt;/strong&gt;! Using a small sample period, from just a year prior, we saw a &lt;strong&gt;280%&lt;/strong&gt; increase in mobile purchases.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2016/07/case-study-responsive-design-return-on/image-0.png&#34; /&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;The timeframe for these comparison numbers was August 2014 before the responsive transition and August of 2015, after the transition.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To sanity check those numbers, we just re-ran some of the analytics recently and in May of 2016, revenue from mobile users is still up over 90% and &lt;strong&gt;the clients total revenue year-over-year is up 12%&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2016/07/case-study-responsive-design-return-on/image-1.png&#34; /&gt;&lt;/div&gt;
&lt;p&gt;We also ran some numbers on the growth of mobile revenue as a percentage of overall revenue through this process. As you can see, two years ago mobile revenue was less than 1% and is now near 12%.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;img src=&#34;/blog/2016/07/case-study-responsive-design-return-on/image-2.jpeg&#34; /&gt;&lt;/div&gt;
&lt;p&gt;In this case, I didn’t go into all the ways to slice and dice the numbers in Google Analytics. However I&amp;rsquo;m happy to speak with you if you want to know more about mobile users, this client’s data, or how responsive design might help grow your e-commerce business.&lt;/p&gt;
&lt;h3 id=&#34;the-lesson&#34;&gt;The Lesson&lt;/h3&gt;
&lt;p&gt;The lesson here is that any store will benefit from going responsive. Having an experienced partner like End Point that can work with your budget and timeline can make this is a doable project for any size store and budget.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Zurb Foundation 5: Applying Styles Conditionally Based on Viewport Size</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/02/zurb-foundation-5-applying-styles/"/>
      <id>https://www.endpointdev.com/blog/2016/02/zurb-foundation-5-applying-styles/</id>
      <published>2016-02-08T00:00:00+00:00</published>
      <author>
        <name>Patrick Lewis</name>
      </author>
      <content type="html">
        &lt;p&gt;The &lt;a href=&#34;https://get.foundation/sites/docs-v5/&#34;&gt;Zurb Foundation 5&lt;/a&gt; front-end framework provides many convenient features such as the ability to control the visibility of HTML elements for different browser window sizes using CSS classes. Foundation CSS classes like “show-for-small-only” and “hide-for-large-up” (full list at &lt;a href=&#34;https://get.foundation/sites/docs-v5/components/visibility.html&#34;&gt;https://get.foundation/sites/docs-v5/components/visibility.html&lt;/a&gt;) make it easy to add mobile-specific content to your page or prevent certain page elements from being displayed on mobile devices.&lt;/p&gt;
&lt;p&gt;Having an easy way to show/hide elements based on viewport size is nice, but what if you want to style an element differently based on the size of the browser that’s viewing the page? Foundation has you covered there, too, though the method is less obvious. It’s possible to use Foundation’s media query SCSS variables when writing your own custom styles in order to apply different styling rules for different viewport sizes.&lt;/p&gt;
&lt;p&gt;For example, if you have an element that you want to offset with a margin in a large window but be flush with the left edge of a small window, you can use the Foundation media query variables to apply a styling override that’s specific to small windows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scss&#34; data-lang=&#34;scss&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;#element&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;margin-left&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;@media&lt;/span&gt; &lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$small-only&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;margin-left&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will apply a specific set of styling to the element for small viewports and a different set for medium and larger viewports (with the definitions for “small”, “medium”, etc. corresponding to the same values used by Foundation’s visibility classes like “show-for-small-only” which were mentioned at the start of this post).&lt;/p&gt;
&lt;p&gt;It wasn’t immediately obvious to me how to apply conditional styling using Foundation’s own definitions of small, medium, etc. viewport sizes but luckily the variable definitions provided by the SCSS framework make it easy to do so.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Mobile-friendly sites or bust!</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/03/mobile-friendly-sites-or-bust/"/>
      <id>https://www.endpointdev.com/blog/2015/03/mobile-friendly-sites-or-bust/</id>
      <published>2015-03-23T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;A few weeks ago, &lt;a href=&#34;http://googlewebmastercentral.blogspot.com/2015/02/finding-more-mobile-friendly-search.html&#34;&gt;Google announced that&lt;/a&gt; starting on April 21 it will expand its “use of mobile-friendliness as a ranking signal” which “will have a significant impact in our search results”.&lt;/p&gt;
&lt;p&gt;The world of search engine optimization and online marketing is aflutter about this announcement, given that even subtle changes in Google’s ranking algorithm can have major effects to improve or worsen any particular site’s ranking. And the announcement was made less than two months in advance of the announced date of the change, so there is not much time to dawdle.&lt;/p&gt;
&lt;p&gt;Google has lately been increasing its pressure on webmasters (is that still a real term‽) such as with its announcement last fall of an accelerated timetable for &lt;a href=&#34;https://security.googleblog.com/2014/09/gradually-sunsetting-sha-1.html&#34;&gt;sunsetting SSL certificates with SHA-1 signatures&lt;/a&gt;. So far these accelerated changes have been a good thing for most people on the Internet.&lt;/p&gt;
&lt;p&gt;In this case, Google provides an easy &lt;a href=&#34;https://www.google.com/webmasters/tools/mobile-friendly/&#34;&gt;Mobile-Friendly Site Test&lt;/a&gt; that you can run on your sites to see if you need to make changes or not:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;https://www.google.com/webmasters/tools/mobile-friendly/&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2015/03/mobile-friendly-sites-or-bust/image-0.png&#34;/&gt;&lt;/a&gt;&lt;br/&gt; &lt;/div&gt;
&lt;p&gt;So get on it and check those sites! I know we have a few that we can do some work on.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Simplifying mobile development with Ionic Framework</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/11/simplifying-mobile-development-with/"/>
      <id>https://www.endpointdev.com/blog/2014/11/simplifying-mobile-development-with/</id>
      <published>2014-11-06T00:00:00+00:00</published>
      <author>
        <name>Kamil Ciemniewski</name>
      </author>
      <content type="html">
        &lt;p&gt;My high school math teacher used to say that mathematicians are the laziest people on Earth. Why? Because they always look for clever ways to simplify their work.&lt;/p&gt;
&lt;p&gt;If you stop and think about it, all that technology is, is just simplification. It’s taking the infinitely complex world and turning it into something sterile and simple. It’s all about producing simple models with a limited number of elements and processes.&lt;/p&gt;
&lt;p&gt;Today I’d like to walk you through creation of a mobile app that could be used on iOS, Android or Windows Phone. We’ll use a very cool set of technologies that allow us to switch from using multiple languages and frameworks (Objective-C for iOS, Java for Android and C# for Windows Phone) to just using HTML, CSS and JavaScript.&lt;/p&gt;
&lt;p&gt;Let’s start turning complex into simple!&lt;/p&gt;
&lt;h3 id=&#34;phonegap-and-ionic-framework&#34;&gt;PhoneGap and Ionic Framework&lt;/h3&gt;
&lt;h4 id=&#34;creating-the-project&#34;&gt;Creating the project&lt;/h4&gt;
&lt;p&gt;In order to be able to start playing along, you need to get yourself a set of toys. Assuming that you’ve got NodeJS and Npm installed already, all you have to do is:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ npm install -g cordova ionic&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now you should be able to create the project’s scaffold. We’ll be creating a simple app that will list all the latest cartoons from &lt;a href=&#34;https://xkcd.com&#34;&gt;the xkcd blog&lt;/a&gt;. Let’s call it Cartoonic.&lt;/p&gt;
&lt;p&gt;Ionic comes with a handy tool called ‘ionic’. It allows you to create a new project as well as perform some automated project-structure-management tasks. The project creation task accepts a ‘skeleton’ name that drives an initial layout of the app. Possible options are: ‘blank’, ‘tabs’ and ‘sidemenu’.&lt;/p&gt;
&lt;p&gt;We’ll be creating an app from scratch so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ ionic start Cartoonic blank&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The framework gives you an option of whether you want to use Sass or just plain old Css. To turn Sass on for the project run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ &lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt; Cartoonic &amp;amp;&amp;amp; ionic setup sass&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;All went well, but now let’s see if it &lt;em&gt;works&lt;/em&gt; well. For this, Ionic gives you an ability to test your app in the browser, as if it were a screen of your mobile device. To run the app in the browser now:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ ionic serve&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2014/11/simplifying-mobile-development-with/image-0.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34; target=&#34;_blank&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2014/11/simplifying-mobile-development-with/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h4 id=&#34;working-with-the-app-layout&#34;&gt;Working with the app layout&lt;/h4&gt;
&lt;p&gt;We need to let all users know what a cool name we’ve chosen for our app. The default one provided by the scaffold wouldn’t work well. Also we’d like the color of the header to be blue instead of the default white.&lt;/p&gt;
&lt;p&gt;In order to do so you can take a look at the CSS documentation for different aspects of the UI:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ionicframework.com/docs/components/#header&#34;&gt;https://ionicframework.com/docs/components/#header&lt;/a&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;--- a/www/index.html
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+++ b/www/index.html
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;@@ -21,8 +21,8 @@
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666&#34;&gt;&lt;/span&gt;     &amp;lt;ion-pane&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-      &amp;lt;ion-header-bar class=&amp;#34;bar-stable&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-        &amp;lt;h1 class=&amp;#34;title&amp;#34;&amp;gt;Ionic Blank Starter&amp;lt;/h1&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+      &amp;lt;ion-header-bar class=&amp;#34;bar-positive&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+        &amp;lt;h1 class=&amp;#34;title&amp;#34;&amp;gt;Cartoonic&amp;lt;/h1&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;       &amp;lt;/ion-header-bar&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &amp;lt;ion-content&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &amp;lt;/ion-content&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2014/11/simplifying-mobile-development-with/image-1.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34; target=&#34;_blank&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2014/11/simplifying-mobile-development-with/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;So far so good, now let’s play with the list of cartoons:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;--- a/scss/ionic.app.scss
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+++ b/scss/ionic.app.scss
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+.cartoon {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  text-align: center;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  width: 96%;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  margin-left: 2%;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  margin-top: 2%;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  margin-bottom: 2%;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  img {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    width: 90%;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;--- a/www/index.html
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+++ b/www/index.html
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+        &amp;lt;ion-list&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+          &amp;lt;ion-item class=&amp;#34;item-divider&amp;#34;&amp;gt;Where Do Birds Go&amp;lt;/ion-item&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+          &amp;lt;ion-item class=&amp;#34;cartoon&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+            &amp;lt;img src=&amp;#34;http://imgs.xkcd.com/comics/where_do_birds_go.png&amp;#34; alt=&amp;#34;&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+          &amp;lt;/ion-item&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+          &amp;lt;ion-item class=&amp;#34;item-divider&amp;#34;&amp;gt;Lightsaber&amp;lt;/ion-item&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+          &amp;lt;ion-item class=&amp;#34;cartoon&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+            &amp;lt;img src=&amp;#34;http://imgs.xkcd.com/comics/lightsaber.png&amp;#34; alt=&amp;#34;&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+          &amp;lt;/ion-item&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+        &amp;lt;/ion-list&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;       &amp;lt;/ion-content&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2014/11/simplifying-mobile-development-with/image-2.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34; target=&#34;_blank&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2014/11/simplifying-mobile-development-with/image-2.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Alright, we’ve got the list that’s looking quite nice. The data behind is static, but for now we just wanted to make sure the look&amp;amp;feel is good.&lt;/p&gt;
&lt;h4 id=&#34;using-angularjs-to-manage-the-app&#34;&gt;Using AngularJS to manage the app&lt;/h4&gt;
&lt;p&gt;Ionic is built around the fantastic AngularJS framework. That’s our means of developing the logic behind. We need to make the list of cartoons use real data from the Xckd blog RSS feed. We also need to enable tapping on images to see the picture in the browser (so it can be zoomed in).&lt;/p&gt;
&lt;p&gt;Let’s start with making the UI use dynamically bound data that we can operate on with JavaScript. In order to do so, we need to add a controller for our view. We also need to specify the data binding between the markup we’ve created previously and the variable in the controller that we intend to use as our data store.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;--- a/www/index.html
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+++ b/www/index.html
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;     &amp;lt;script src=&amp;#34;js/app.js&amp;#34;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    &amp;lt;script src=&amp;#34;js/controllers.js&amp;#34;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;   &amp;lt;/head&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-  &amp;lt;body ng-app=&amp;#34;starter&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  &amp;lt;body ng-app=&amp;#34;starter&amp;#34; ng-controller=&amp;#34;CartoonsCtrl&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(...)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &amp;lt;ion-content&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &amp;lt;ion-list&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-          &amp;lt;ion-item class=&amp;#34;item-divider&amp;#34;&amp;gt;Where Do Birds Go&amp;lt;/ion-item&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-          &amp;lt;ion-item class=&amp;#34;cartoon&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-            &amp;lt;img src=&amp;#34;http://imgs.xkcd.com/comics/where_do_birds_go.png&amp;#34; alt=&amp;#34;&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-          &amp;lt;/ion-item&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-          &amp;lt;ion-item class=&amp;#34;item-divider&amp;#34;&amp;gt;Lightsaber&amp;lt;/ion-item&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-          &amp;lt;ion-item class=&amp;#34;cartoon&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-            &amp;lt;img src=&amp;#34;http://imgs.xkcd.com/comics/lightsaber.png&amp;#34; alt=&amp;#34;&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+          &amp;lt;ion-item class=&amp;#34;item-divider&amp;#34; ng-repeat-start=&amp;#34;cartoon in cartoons&amp;#34;&amp;gt;{{ cartoon.title }}&amp;lt;/ion-item&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+          &amp;lt;ion-item class=&amp;#34;cartoon&amp;#34; ng-repeat-end&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+            &amp;lt;img ng-src=&amp;#34;{{ cartoon.href }}&amp;#34; alt=&amp;#34;&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;           &amp;lt;/ion-item&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &amp;lt;/ion-list&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &amp;lt;/ion-content&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;--- a/www/js/app.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+++ b/www/js/app.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-angular.module(&amp;#39;starter&amp;#39;, [&amp;#39;ionic&amp;#39;])
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+angular.module(&amp;#39;starter&amp;#39;, [&amp;#39;ionic&amp;#39;, &amp;#39;starter.controllers&amp;#39;])
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;--- /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+++ b/www/js/controllers.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+angular.module(&amp;#39;starter.controllers&amp;#39;, [])
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+.controller(&amp;#39;CartoonsCtrl&amp;#39;, function($scope) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  $scope.cartoons = [
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+      href: &amp;#34;http://imgs.xkcd.com/comics/where_do_birds_go.png&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+      id: 1434,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+      title: &amp;#34;Where Do Birds Go&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+      href: &amp;#34;http://imgs.xkcd.com/comics/lightsaber.png&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+      id: 1433,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+      title: &amp;#34;Lightsaber&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  ];
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+});
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can notice that “ng-controller” directive has been added to the body element. It points at the newly created controller, which we’re loading with a script tag and making available to the rest of the app by including its module (starter.controllers) in the ‘starter’ module’s dependencies list.&lt;/p&gt;
&lt;p&gt;Let’s implement opening the picture upon the tap:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;--- a/www/index.html
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+++ b/www/index.html
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;       &amp;lt;ion-content&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &amp;lt;ion-list&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &amp;lt;ion-item class=&amp;#34;item-divider&amp;#34; ng-repeat-start=&amp;#34;cartoon in cartoons&amp;#34;&amp;gt;{{ cartoon.title }}&amp;lt;/ion-item&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-          &amp;lt;ion-item class=&amp;#34;cartoon&amp;#34; ng-repeat-end&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+          &amp;lt;ion-item class=&amp;#34;cartoon&amp;#34; ng-repeat-end ng-click=&amp;#34;openCartoon(cartoon)&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;             &amp;lt;img ng-src=&amp;#34;{{ cartoon.href }}&amp;#34; alt=&amp;#34;&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &amp;lt;/ion-item&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &amp;lt;/ion-list&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;--- a/www/js/controllers.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+++ b/www/js/controllers.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;@@ -13,4 +13,8 @@ angular.module(&amp;#39;starter.controllers&amp;#39;, [])
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666&#34;&gt;&lt;/span&gt;       title: &amp;#34;Lightsaber&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  $scope.openCartoon = function(cartoon) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    window.open(cartoon.href, &amp;#39;_blank&amp;#39;, &amp;#39;location=no&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  };
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That was simple wasn’t it? We’ve just added the ng-click directive making the click/tap event bound to the openCartoon function from the scope. This function in turn is using window.open passing ‘_blank’ as target. Et voilà!&lt;/p&gt;
&lt;p&gt;Now, let’s implement loading images from the real feed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;--- a/www/index.html
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+++ b/www/index.html
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;     &amp;lt;script src=&amp;#34;cordova.js&amp;#34;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    &amp;lt;script type=&amp;#34;text/javascript&amp;#34; src=&amp;#34;https://www.google.com/jsapi&amp;#34;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    &amp;lt;script type=&amp;#34;text/javascript&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+      google.load(&amp;#34;feeds&amp;#34;, &amp;#34;1&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    &amp;lt;/script&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &amp;lt;!-- your app&amp;#39;s js --&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &amp;lt;script src=&amp;#34;js/app.js&amp;#34;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &amp;lt;script src=&amp;#34;js/controllers.js&amp;#34;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    &amp;lt;script src=&amp;#34;js/services.js&amp;#34;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;   &amp;lt;/head&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &amp;lt;body ng-app=&amp;#34;starter&amp;#34; ng-controller=&amp;#34;CartoonsCtrl&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;--- a/www/js/app.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+++ b/www/js/app.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-angular.module(&amp;#39;starter&amp;#39;, [&amp;#39;ionic&amp;#39;, &amp;#39;starter.controllers&amp;#39;])
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+angular.module(&amp;#39;starter&amp;#39;, [&amp;#39;ionic&amp;#39;, &amp;#39;starter.controllers&amp;#39;, &amp;#39;starter.services&amp;#39;])
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;--- a/www/js/controllers.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+++ b/www/js/controllers.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-angular.module(&amp;#39;starter.controllers&amp;#39;, [])
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+angular.module(&amp;#39;starter.controllers&amp;#39;, [&amp;#39;starter.services&amp;#39;])
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-.controller(&amp;#39;CartoonsCtrl&amp;#39;, function($scope) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-  $scope.cartoons = [
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-    {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-      href: &amp;#34;http://imgs.xkcd.com/comics/where_do_birds_go.png&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-      id: 1434,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-      title: &amp;#34;Where Do Birds Go&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-    },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-    {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-      href: &amp;#34;http://imgs.xkcd.com/comics/lightsaber.png&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-      id: 1433,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-      title: &amp;#34;Lightsaber&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-    }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-  ];
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+.controller(&amp;#39;CartoonsCtrl&amp;#39;, function($scope, cartoons) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  $scope.cartoons = [];
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   $scope.openCartoon = function(cartoon) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     window.open(cartoon.href, &amp;#39;_blank&amp;#39;, &amp;#39;location=no&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  $scope.$watch(function() {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    return cartoons.list;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  }, function(list) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    $scope.cartoons = list;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  });
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;--- /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+++ b/www/js/services.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;@@ -0,0 +1,26 @@
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+angular.module(&amp;#39;starter.services&amp;#39;, [])
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+.factory(&amp;#39;cartoons&amp;#39;, function($rootScope) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  var self = {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    list: [],
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    url: &amp;#34;http://xkcd.com/rss.xml&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    fetch: function() {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+      var feed = new google.feeds.Feed(self.url);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+      feed.load(function(result) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+        $rootScope.$apply(function() {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+          if(result.status.code == 200) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+            self.list = result.feed.entries.map(function(entry) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+              return {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+                href: entry.content.match(/src=&amp;#34;[^&amp;#34;]*/g)[0].substring(5, 100),
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+                title: entry.title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+              }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+            });
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+          }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+        });
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+      });
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+    }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  };
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  self.fetch();
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+  return self;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+});
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Okay, a couple of comments here. You may wonder why have we loaded Google APIs? That’s because if we were to try to load the xml that comes from the blog’s feed, we would inevitably fail because of the “&lt;a href=&#34;https://en.wikipedia.org/wiki/Same-origin_policy&#34;&gt;Same Origin Policy&lt;/a&gt;”. Basically, the Ajax request would not complete successfully and there’s nothing we can do locally about it.&lt;/p&gt;
&lt;p&gt;Luckily, Google has created a service we can use as a middleman between our in-browser JavaScript code and blog’s web server. Long story made short: when you load the feed with Google’s Feed API - the data’s there and it’s also already parsed.&lt;/p&gt;
&lt;p&gt;We’re also adding a custom service here. The service fetches the entries upon its initialization. And because the controller’s depending on this service - we’re guaranteed to get the data as soon as the controller is initialized. The controller is also using the $watch function to make sure it has the most recent copy of the entries list.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2014/11/simplifying-mobile-development-with/image-3.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34; target=&#34;_blank&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2014/11/simplifying-mobile-development-with/image-3.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://ionicframework.com/docs/&#34;&gt;Ionic docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developers.google.com/feed/v1/&#34;&gt;Google Feed API docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Same-origin_policy&#34;&gt;Same Origin Policy on Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://xkcd.com&#34;&gt;The xkcd blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>Mooving to the Mobile Web</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/11/mooving-to-mobile-web/"/>
      <id>https://www.endpointdev.com/blog/2013/11/mooving-to-mobile-web/</id>
      <published>2013-11-21T00:00:00+00:00</published>
      <author>
        <name>Kirk Harr</name>
      </author>
      <content type="html">
        &lt;p&gt;With the rise of the myriad of mobile phones, tablets and other devices that are connected to the internet, the potential users for a given website have both increased in number and morphed in their needs in terms of a user experience. As anyone who has attempted to use a website not designed for a mobile phone browser with frantic pinch-zooms to find a tiny page control, right next to four other controls that do something totally different in a menu, can attest this is really not ideal.&lt;/p&gt;
&lt;p&gt;And meanwhile from the other perspective, for web developers, the notion of a fragmented user base over everything from a desktop PC with a modern browser to an embedded PC built into my new LCD TV needing to view your pages gracefully can be a scary prospect. The thought of maintaining independent versions of your web infrastructure that fit each of these major use cases would likely scare everyone in your company, especially the finance people cutting all the checks.&lt;/p&gt;
&lt;p&gt;So what is a company facing this new reality of the modern web to do? One particular solution can help to alleviate one of the more troublesome issues with new devices browsing the Internet, mobile phone display. While the phones themselves are starting to come with resolutions comparable to a modern desktop or laptop PC, the screen size relative to your finger that is selecting inputs, leaves a far less precise pointer than a mouse. This is precisely the problem described earlier, as those page control designs with multiple options in a menu, comes from the design era when a precise mouse click as the input method was the norm. Additionally, with the smaller screen size, it is necessary to more prominently highlight certain aspects of the page design, like images of featured products, to keep the same level of emphasis of those images on the user who will view them.&lt;/p&gt;
&lt;p&gt;One firm that is implementing a version of this solution that I recently helped implement for a client is &lt;a href=&#34;https://www.moovweb.com/&#34;&gt;Moovweb&lt;/a&gt;. For web developers, this technology will allow them to accomplish those goals of making page controls more usable on mobile phones, make featured elements of page layout stand out more effectively on a mobile phone browser, without actually maintaining a separate version of their site, optimized for mobile users. Moovweb will make a request to your site, optimize the content for mobile (behind the scenes) and the send that optimized response back to the user’s device. In this manner, the page contents will always be updated automatically for the mobile sites, based on what is present on your current live site. Which page elements are selected to be displayed on the mobile page, and how they are displayed are all configurable options within Moovweb’s controls, and you are also able to use a number of pre-built templates based on web software packages that are common.&lt;/p&gt;
&lt;h3 id=&#34;technical-details&#34;&gt;Technical Details&lt;/h3&gt;
&lt;p&gt;How does Moovweb accomplish this sleight of hand—​tailoring the response based on the device requesting the information? The secret lies both in JavaScript and DNS. Firstly, in order to setup your domain for Moovweb, you need to create a sub-domain that requests from mobile devices would be forwarded to, which would actually point to Moovweb with a CNAME record. Here is an example from the &lt;a href=&#34;https://developer.moovweb.com/docs/cloud/production_domain&#34;&gt;setup documentation&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;If your domain was example.com, and the mobile sub-domain you had selected was m.example.com you would create a CNAME record for:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;m.example.com.     IN     CNAME     m.example.com.moovdns.net.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;This would point any request to m.example.com over to the Moovweb servers, which will then carry out the forwarding of the requests back to the mobile browser of the user once the template had been applied to the page design and crafted the mobile version of the site.&lt;/p&gt;
&lt;p&gt;For the JavaScript setup, a &amp;lt;script&amp;gt; tag must be added to the design of each page’s &lt;head&gt; tag in order to perform redirection of requests for mobile browsers. This script that is added is created for each customer by Moovweb, and is used to match the User-Agent setting of each request against a list of known mobile browsers. Conceivably, with this in place on every page, whether the user is attempting to load the main page, or perhaps a deeper link to something like a product page or category page, every request from the mobile browsers should be automatically redirected to the mobile domain that we setup the CNAME record for.&lt;/p&gt;
&lt;h3 id=&#34;how-it-looks&#34;&gt;How it looks&lt;/h3&gt;
&lt;p&gt;When working on deploying Moovweb for a client, tigerdistrict.com, I was introduced to the technology for the first time, and I was impressed with the mobile site experience. The page controls that were modified to make them easier to tap with your finger, and also making the page layout more portrait which fits the mobile phone form factor. Here are some examples of the mobile and non mobile site:&lt;/p&gt;
&lt;table align=&#34;center&#34; cellpadding=&#34;0&#34; cellspacing=&#34;0&#34; class=&#34;tr-caption-container&#34; style=&#34;margin-left: auto; margin-right: auto; text-align: center;&#34;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&#34;text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2013/11/mooving-to-mobile-web/image-0-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: auto; margin-right: auto;&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;232&#34; src=&#34;/blog/2013/11/mooving-to-mobile-web/image-0.png&#34; width=&#34;320&#34;/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#34;tr-caption&#34; style=&#34;text-align: center;&#34;&gt;Desktop Version&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;table align=&#34;center&#34; cellpadding=&#34;0&#34; cellspacing=&#34;0&#34; class=&#34;tr-caption-container&#34; style=&#34;margin-left: auto; margin-right: auto; text-align: center;&#34;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&#34;text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2013/11/mooving-to-mobile-web/image-1-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: auto; margin-right: auto;&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;320&#34; src=&#34;/blog/2013/11/mooving-to-mobile-web/image-1.png&#34; width=&#34;180&#34;/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#34;tr-caption&#34; style=&#34;text-align: center;&#34;&gt;Mobile Version&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;One of my favorite features was how Moovweb could handle page navigation menus, like the one you see on the left margin of the page in the desktop version. On a mobile device, attempting to get a precise enough point to select only the correct one of those options, and not mistakenly clicking others, would be painful to say the least. However after the site has been converted to the mobile version, two new page elements are added to the bar at the top of the page. There is a cart icon representing the all important shopping cart, and one of the legendary “Hamburger button” controls that opens up the page navigation menu. Here is what it looks like on the mobile browser:&lt;/p&gt;
&lt;table align=&#34;center&#34; cellpadding=&#34;0&#34; cellspacing=&#34;0&#34; class=&#34;tr-caption-container&#34; style=&#34;margin-left: auto; margin-right: auto; text-align: center;&#34;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&#34;text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2013/11/mooving-to-mobile-web/image-2-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: auto; margin-right: auto;&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;320&#34; src=&#34;/blog/2013/11/mooving-to-mobile-web/image-2.png&#34; width=&#34;180&#34;/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#34;tr-caption&#34; style=&#34;text-align: center;&#34;&gt;No Dialing Wand Needed&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;As you can see, it replicates the same menu tree, with the same ability to expand into any of the available categories, or use the text search, all within an interface that is easy to use within a mobile browser.&lt;/p&gt;
&lt;h3 id=&#34;the-future-of-the-web--devices-devices-devices&#34;&gt;The Future of the Web — Devices, Devices, Devices&lt;/h3&gt;
&lt;p&gt;One thing is clear from the rise of mobile devices and tablets on the web over the past few years, is that these devices are here to stay, and if anything will continue to grow and dominate the marketplace. For developers seeking to harness, or even to just stay ahead of this trend, will need to address the problems of mobile browsers and the limitations of the devices themselves. &lt;/p&gt;
&lt;p&gt;Creating websites that provide a desirable user experience to these many flavors of devices can be a daunting challenge, but in a way this shows us that the true issue is not the fragmentation itself. The real benefits from this type of mobile web development take advantage of your existing infrastructure, and gives you a method to tailor it as best you can to fit each of these new device’s abilities. Duplicating effort is wasteful and maintaining multiple versions the the same content with slightly different presentation is an example of this wasted effort. Reusing the current web infrastructure already available and already being invested in the maintenance of, allows the presentation of these multiple user experiences in a cost effective way. &lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Dynamically adding custom radio buttons in Android</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/05/dynamically-adding-custom-radio-buttons/"/>
      <id>https://www.endpointdev.com/blog/2013/05/dynamically-adding-custom-radio-buttons/</id>
      <published>2013-05-09T00:00:00+00:00</published>
      <author>
        <name>Zed Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;I’ve been writing a timesheet tracking app for End Point. In working on various features of this app, I’ve had more than a few problems to work through, since this project is one of my first on Android and I’ve never used many of the Android features that I’m now using. One particularly fun bit was setting up a scrollable list of radio buttons with numbers from 0 – 23 (no, we don’t often have people working 23 hours on a project in a day, but just in case!) when the user is creating an entry, as a prettier and more backward-compatible alternative to using a number picker to indicate how many hours were spent on this particular job.&lt;/p&gt;
&lt;p&gt;In Android, view layouts are usually defined in XML like this:&lt;/p&gt;
&lt;p&gt;layout/activity_hour_picker.xml&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;utf-8&amp;#34;?&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;RelativeLayout xmlns:android=&amp;#34;http://schemas.android.com/apk/res/android&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  android:layout_width=&amp;#34;match_parent&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  android:layout_height=&amp;#34;match_parent&amp;#34; &amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;Button android:id=&amp;#34;@+id/button&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    android:layout_height=&amp;#34;wrap_content&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    android:layout_width=&amp;#34;wrap_content&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    android:onClick=&amp;#34;doStuff&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    android:text=&amp;#34;Hello World!&amp;#34; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/RelativeLayout&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Simple. But, as you can imagine, not so fun when you’re adding a list of 24 buttons—​so, I decided to add them dynamically in the code. First, though, a ScrollView and RadioGroup (ScrollView only allows one child) need to be defined in the XML, no point in doing that programmatically. Let’s add those:&lt;/p&gt;
&lt;p&gt;layout/activity_hour_picker.xml&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;utf-8&amp;#34;?&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;RelativeLayout xmlns:android=&amp;#34;http://schemas.android.com/apk/res/android&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   android:layout_width=&amp;#34;match_parent&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   android:layout_height=&amp;#34;match_parent&amp;#34; &amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &amp;lt;HorizontalScrollView
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        android:id=&amp;#34;@+id/hour_scroll_view&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        android:layout_width=&amp;#34;match_parent&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        android:layout_height=&amp;#34;wrap_content&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        android:fillViewport=&amp;#34;true&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        android:scrollbars=&amp;#34;none&amp;#34; &amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;RadioGroup
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            android:id=&amp;#34;@+id/hour_radio_group&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            android:layout_width=&amp;#34;wrap_content&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            android:layout_height=&amp;#34;match_parent&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            android:orientation=&amp;#34;horizontal&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            // This is where our buttons will be
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;/RadioGroup&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/HorizontalScrollView&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/RelativeLayout&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Okay. So, now, in our Activity, we need to override onCreate if we haven’t already and add the following code:&lt;/p&gt;
&lt;p&gt;src/com/example/HourPickerActivity.java&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@Override
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;public void onCreate(Bundle icicle) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  super.onCreate(icicle);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  setContentView(R.layout.activity_hour_picker);  // This adds the views from the XML we wrote earlier
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ViewGroup hourButtonLayout = (ViewGroup) findViewById(R.id.hour_radio_group);  // This is the id of the RadioGroup we defined
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  for (int i = 0; i &amp;lt; RANGE_HOURS; i++) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    RadioButton button = new RadioButton(this);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    button.setId(i);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    button.setText(Integer.toString(i));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    button.setChecked(i == currentHours); // Only select button with same index as currently selected number of hours
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    button.setBackgroundResource(R.drawable.item_selector); // This is a custom button drawable, defined in XML
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    hourButtonLayout.addView(button);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And this is what we get:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2013/05/dynamically-adding-custom-radio-buttons/image-0-big.png&#34; imageanchor=&#34;1&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2013/05/dynamically-adding-custom-radio-buttons/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It scrolls horizontally like we want, but there’s a problem—​we don’t want the default radio button selector showing up, since we’ve already got custom button graphics showing. And we can’t forget that we’re still missing the code to make everything within the RadioGroup work properly—​In other words, the buttons won’t do anything when a user clicks them. So, let’s add a listener to each button as it’s created:&lt;/p&gt;
&lt;p&gt;src/com/example/HourPickerActivity.java&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;font color=&amp;#34;#969696&amp;#34;&amp;gt;button.setId(i);&amp;lt;/font&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;button.setBackgroundResource(R.drawable.item_selector); // This is a custom button drawable, defined in XML
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;button.setOnClickListener(new OnClickListener() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        @Override
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        public void onClick(View view) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ((RadioGroup) view.getParent()).check(view.getId());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            currentHours = view.getId();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;font color=&amp;#34;#969696&amp;#34;&amp;gt;button.setText(Integer.toString(i));&amp;lt;/font&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So now the currently selected value is ready in our static variable currentHours for whenever the user is finished. Now we need to get rid of the standard radio button graphics. The solution I found is to use selector XML, with just one item that points to a transparent drawable:&lt;/p&gt;
&lt;p&gt;drawable/null_selector.xml&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;utf-8&amp;#34;?&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;selector xmlns:android=&amp;#34;http://schemas.android.com/apk/res/android&amp;#34; &amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;item android:drawable=&amp;#34;@android:color/transparent&amp;#34; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/selector&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Set each button to use it (and to center the text) like this (R.drawable.null_selector is our selector XML):&lt;/p&gt;
&lt;p&gt;src/com/example/HourPickerActivity.java&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;font color=&amp;#34;#969696&amp;#34;&amp;gt;button.setText(Integer.toString(i));&amp;lt;/font&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;button.setGravity(Gravity.CENTER);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;button.setButtonDrawable(R.drawable.null_selector);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;font color=&amp;#34;#969696&amp;#34;&amp;gt;button.setChecked(i == currentHours); // Only select button with same index as currently selected number of hours  &amp;lt;/font&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, let’s see how this all has pulled together.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2013/05/dynamically-adding-custom-radio-buttons/image-1-big.png&#34; imageanchor=&#34;1&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2013/05/dynamically-adding-custom-radio-buttons/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There—​that looks much better! And it works great.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Code School: Journey into Mobile Review</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/07/code-school-journey-into-mobile-review/"/>
      <id>https://www.endpointdev.com/blog/2012/07/code-school-journey-into-mobile-review/</id>
      <published>2012-07-06T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;Yesterday, I took the &lt;a href=&#34;https://www.codeschool.com/courses/journey-into-mobile&#34;&gt;Journey into Mobile&lt;/a&gt; course over at &lt;a href=&#34;https://www.codeschool.com/&#34;&gt;Code School&lt;/a&gt;, an online training program with a handful of web technology courses. I just started a large mobile project for &lt;a href=&#34;https://www.papersource.com/&#34;&gt;Paper Source&lt;/a&gt;, so this was good timing. Because a paid membership to Code School is required to participating in the training, I won’t share too many of the course details here. But I’ll share a few tidbits that will hopefully interest you in taking the course or learning more about mobile development.&lt;/p&gt;
&lt;p&gt;The course was divided into 5 high level lessons (or levels):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Relative Font Size&lt;/li&gt;
&lt;li&gt;Fluid Layouts&lt;/li&gt;
&lt;li&gt;Adaptive Design&lt;/li&gt;
&lt;li&gt;Responsiveness Adventures&lt;/li&gt;
&lt;li&gt;Responsive Media&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first two topics covered working with proportional font sizes and content regions, and how to convert your existing layout to proportions (percentage or ems) to create a fluid layout which included proportional font sizes. In the Adaptive Design lesson, the CSS3 supported @media query was introduced. I’ve used the media query on the responsive design for &lt;a href=&#34;https://web.archive.org/web/20120719080212/http://www.thebestgameapps.com/&#34;&gt;The Best Game Apps&lt;/a&gt; and will be using it for Paper Source. Some examples of @media queries include:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;media&lt;/span&gt; (&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;min-width&lt;/span&gt;: &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;655px&lt;/span&gt;) &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;and&lt;/span&gt; (&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;max-width&lt;/span&gt;: &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;1006px&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;#&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;styles&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;specific&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;to&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;browser&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;width&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;655-1006&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;pixels&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;media&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;only&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;screen&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;and&lt;/span&gt; (&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;device-width&lt;/span&gt;: &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;768px&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;#&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;styles&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;specific&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;to&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;browser&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;width&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;768&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;pixels&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;media&lt;/span&gt; (&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;min-device-width&lt;/span&gt;: &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;320px&lt;/span&gt;) &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;and&lt;/span&gt; (&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;max-device-width&lt;/span&gt;: &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;655px&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;#&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;styles&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;specific&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;to&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;browser&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;width&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;320-655&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;pixels&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;media&lt;/span&gt; (&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;min-device-width&lt;/span&gt;: &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;450px&lt;/span&gt;) &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;and&lt;/span&gt; (&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;orientation&lt;/span&gt;:&lt;span style=&#34;color:#555&#34;&gt;landscape&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;#&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;styles&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;specific&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;to&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;browser&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;width&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;450&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;pixels&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;landscape&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;orientation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For each of the above @media queries, specific “break points” are determined to adjust styles as certain elements break as the browser width changes. For example, if elements begin to overlap as the screen narrows, the browser width at which this begins to happen is one break point, and new styles are defined for that width.&lt;/p&gt;
&lt;p&gt;The last two levels of the training course covered Responsiveness Adventures and Responsiveness Media. Responsive design also leverages the @media query to design responsively for changing browser widths. One interesting topic covered in the Responsive Media lesson was how &lt;a href=&#34;https://blog.cloudfour.com/how-apple-com-will-serve-retina-images-to-new-ipads/&#34;&gt;Retina Images&lt;/a&gt; are addressed on devices where the pixel density is 1.5-2 times regular pixel density. This was a topic I hadn’t come across in mobile development. The lesson presented a couple of options for dealing with Retina images, including use of the @media query and picture HTML tag.&lt;/p&gt;
&lt;p&gt;Overall, it was a decent course with a good overview. I would recommend it to anyone planning to get involved in mobile development.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Three Things: ImageMagick, RequestBin, Responsivness</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/06/three-things-imagemagick-requestbin/"/>
      <id>https://www.endpointdev.com/blog/2012/06/three-things-imagemagick-requestbin/</id>
      <published>2012-06-28T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;It’s been a while since I wrote a &lt;strong&gt;Three Things&lt;/strong&gt; blog article, where I share tidbits that I’ve learned that don’t quite make it into a full blog post, but are still worth sharing!&lt;/p&gt;
&lt;h3 id=&#34;image-stacking-via-command-line&#34;&gt;Image Stacking via Command Line&lt;/h3&gt;
&lt;p&gt;I recently needed to stack several images to CSS spritify them. I did some quick searching and found the following ImageMagick command line functionality:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;convert add.png add_ro.png -append test.png
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;convert add.png add_ro.png +append test.png&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The -append argument will stack the images vertically, and +append will stack the images horizontally.&lt;/p&gt;
&lt;h3 id=&#34;requestbin&#34;&gt;RequestBin&lt;/h3&gt;
&lt;p&gt;Recently, I needed to quickly examine the contents sent during a payment transaction. &lt;a href=&#34;/team/richard-templet/&#34;&gt;Richard&lt;/a&gt; pointed me to &lt;a href=&#34;https://github.com/Runscope/requestbin&#34;&gt;RequestBin&lt;/a&gt; a neat and quick tool for examining the contents of an external request. I was testing the Elavon payment gateway integrated in an application using &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt;. In my code, I made the following change:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ElavonGateway&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ViaklixGateway&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.test_url = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;https://demo.myvirtualmerchant.com/VirtualMerchantDemo/process.do&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.live_url = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;https://www.myvirtualmerchant.com/VirtualMerchant/process.do&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+ &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.test_url = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;http://requestb.in/abc1def2&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I ran through a single checkout, then visited the RequestBin inspect URL to examine the variables passed to the payment gateway, to see the following data (the text overwrites itself in Chrome):&lt;/p&gt;
&lt;p&gt;&lt;img border=&#34;0&#34; height=&#34;400&#34; src=&#34;/blog/2012/06/three-things-imagemagick-requestbin/image-0.png&#34; width=&#34;372&#34;/&gt;&lt;br&gt;
&lt;a href=&#34;https://github.com/Runscope/requestbin&#34;&gt;RequestBin&lt;/a&gt; example output.&lt;/p&gt;
&lt;h3 id=&#34;a-responsiveness-tool&#34;&gt;A Responsiveness Tool&lt;/h3&gt;
&lt;p&gt;I’ve been working on responsiveness for a couple of sites recently. One of my clients pointed out &lt;a href=&#34;http://responsivepx.com/&#34;&gt;responsivepx&lt;/a&gt;, which allows you to toggle the width of your browser to review various responsive layouts.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Popular Mobile Apps from Brian and Adam</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/06/popular-mobile-apps-from-brian-and-adam/"/>
      <id>https://www.endpointdev.com/blog/2012/06/popular-mobile-apps-from-brian-and-adam/</id>
      <published>2012-06-15T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;In our last lightning talk of the conference, Brian Dillon and &lt;a href=&#34;/blog/authors/adam-vollrath/&#34;&gt;Adam Vollrath&lt;/a&gt; shared some of their favorite mobile apps. Brian queried the room to see the phone type divide in the room. Here was our split: 33%—​iPhones, 33%—​Androids, 33%—​unsmart phones.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.flickr.com/photos/80083124@N08/7375164694/&#34;&gt;&lt;img alt=&#34;IMG_0881.JPG&#34; height=&#34;240&#34; src=&#34;/blog/2012/06/popular-mobile-apps-from-brian-and-adam/image-0.jpeg&#34; width=&#34;320&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Brian likes pure simplicity for iPhone apps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://web.archive.org/web/20120804061041/http://dropkickapp.com/&#34;&gt;Dropkick&lt;/a&gt;, categorization, syncing, require account creation&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://web.archive.org/web/20120731035429/http://www.simplenoteapp.com/&#34;&gt;Simplenote&lt;/a&gt;: same thing, but for notes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;http://www.flickr.com/photos/80083124@N08/7375140352/&#34;&gt;&lt;img alt=&#34;IMG_0883.JPG&#34; height=&#34;320&#34; src=&#34;/blog/2012/06/popular-mobile-apps-from-brian-and-adam/image-0.jpeg&#34; width=&#34;240&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Adam has an Android “because Google owns him”:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://support.google.com/websearch/answer/166331&#34;&gt;Google Goggles&lt;/a&gt;: Rumored Google technology is integrated into this. Google tries to ad-hoc identify logos, book covers, automatic translation of images, images, QR codes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://k9mail.github.io/&#34;&gt;K9 Mail&lt;/a&gt;: email client&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://web.archive.org/web/20120730123717/http://www.andchat.net/&#34;&gt;Andchat&lt;/a&gt;: IRC client&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://web.archive.org/web/20120826070800/http://www.flukenetworks.com/enterprise-network/wireless-network/AirMagnet-WiFi-Analyzer&#34;&gt;WiFi Analyzer&lt;/a&gt;: provides site survey on network traffic&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://web.archive.org/web/20120427232801/http://www.androidtapp.com/speedtest-net-speed-test/&#34;&gt;SpeedTest&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>Using Gmail at Work</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2011/12/using-gmail-at-work/"/>
      <id>https://www.endpointdev.com/blog/2011/12/using-gmail-at-work/</id>
      <published>2011-12-15T00:00:00+00:00</published>
      <author>
        <name>Brian Buchalter</name>
      </author>
      <content type="html">
        &lt;h3 id=&#34;the-short-story&#34;&gt;The Short Story&lt;/h3&gt;
&lt;p&gt;For those who don’t care about why, just how&amp;hellip;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a &lt;a href=&#34;https://accounts.google.com/NewAccount?service=mail&amp;amp;continue=http://mail.google.com/mail/e-11-63fb73ea731526b75ac5a66a770b0-ee45c8c140b7a1c66d569e7562acee65c08f1c21&amp;amp;type=2&#34;&gt;new Gmail account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Setup &lt;a href=&#34;https://support.google.com/mail/bin/answer.py?hl=en&amp;amp;answer=21289&#34;&gt;Mail Fetcher&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Setup &lt;a href=&#34;https://support.google.com/mail/bin/answer.py?hl=en&amp;amp;answer=22370&#34;&gt;send email from another account&lt;/a&gt;;and make it your default&lt;/li&gt;
&lt;li&gt;Verify you send and receive as your corporate address by default using the web client&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.google.com/gmail/about/&#34;&gt;Setup your mobile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;From your mobile go to &lt;a href=&#34;https://m.google.com/sync&#34;&gt;m.google.com/sync&lt;/a&gt; and Enable “Send Mail As” for this device (tested only on iOS)&lt;/li&gt;
&lt;li&gt;Verify you send and receive as your corporate address by default using your mobile client&lt;/li&gt;
&lt;li&gt;Setup Google Authorship with your domain’s email address&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;the-long-story&#34;&gt;The Long Story&lt;/h3&gt;
&lt;p&gt;Here at End Point there are a lot of opinions about email clients. Our hardcore folks like &lt;a href=&#34;https://en.wikipedia.org/wiki/Alpine_(email_client)&#34;&gt;Alpine&lt;/a&gt; while for most people Evolution, Thunderbird, or Outlook will do. As a Gmail user since September 2004, I found I needed to figure out how get our corporate email system to work with my preferred client.&lt;/p&gt;
&lt;p&gt;My first reaction was to have Gmail act as an IMAP client. I found (&lt;a href=&#34;https://productforums.google.com/forum/?hl=en#!category-topic/gmail/managing-settings-and-mail/b0v5HHlqbHs&#34;&gt;as many others had&lt;/a&gt;) that Gmail does not support IMAP integration with other accounts. However, Gmail does have a POP email client known as &lt;a href=&#34;https://support.google.com/mail/answer/21289?hl=en&#34;&gt;Mail Fetcher&lt;/a&gt;. I found that Gmail does support encrypted connections via POP, so use them if your email server supports them. When combined with the &lt;a href=&#34;https://gmail.googleblog.com/2010/01/default-https-access-for-gmail.html&#34;&gt;HTTPS by default&lt;/a&gt;, access to the Gmail web client seemed sufficiently secure.&lt;/p&gt;
&lt;p&gt;I now needed to send email not as my Gmail address, but as my End Point address. Google has well documented how to &lt;a href=&#34;https://support.google.com/mail/bin/answer.py?hl=en&amp;amp;answer=22370&#34;&gt;send email from another account&lt;/a&gt;. Again encrypted SMTP is supported and is strongly recommended. Also be sure to make your corporate email account the default account so you will always use your corporate email address and not the Gmail address.&lt;/p&gt;
&lt;p&gt;After verifying I was sending and receiving email properly, I needed to get my mobile setup. There are a &lt;a href=&#34;https://www.google.com/gmail/about/&#34;&gt;variety of options&lt;/a&gt; available for all the mobile platforms. On my iPhone, I had several other accounts already setup and found the native client to be acceptable. I decided I would configure the native iPhone email app to access Gmail, as well as Contacts and Calendar using Google’s support for Microsoft’s ActiveSync protocol, which Google has licensed and rebranded as &lt;a href=&#34;https://support.google.com/a/users/answer/138740?visit_id=1-636676970400046677-2511485164&amp;amp;hl=en&amp;amp;rd=1&#34;&gt;Google Sync&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I had used Google Sync for other Exchange accounts at my previous job and found it worked very well. However, there are some &lt;a href=&#34;https://support.google.com/a/users/answer/139635?visit_id=1-636676970400046677-2511485164&amp;amp;hl=en&amp;amp;rd=1&#34;&gt;known issues&lt;/a&gt;, like not being able to accept event invitations recieved via POP. It’s worth checking these issues out to see if there are any blockers for you.&lt;/p&gt;
&lt;p&gt;After setting up “Google Sync” on my iPhone, I tested again, and found that by default, it would use my Gmail account as my default outgoing email account, despite the setting in the Gmail web client. I needed to use my corporate address here at End Point for sending mail from mobile; I thought I was sunk!&lt;/p&gt;
&lt;p&gt;Fortunately, it seems I over looked a section in the Google Sync setup documentation, labeled “Enable Send Mail As feature”. This feature solved my problem by having me go to &lt;a href=&#34;https://get.google.com/apptips/apps/?utm_source=googlemobile&amp;amp;utm_campaign=redirect#!/all&#34;&gt;m.google.com/sync&lt;/a&gt; from my iOS device and check Enable “Send Mail As” for this device. This would tell Google Sync to use the default outgoing account I had specified in the web client.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2011/12/using-gmail-at-work/image-0.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2011/12/using-gmail-at-work/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;/div&gt;
&lt;p&gt;One requirement here at End Point which this configuration does not meet is support for PGP encryption/decryption of messages. There is a &lt;a href=&#34;https://web.archive.org/web/20111203170252/http://gpg4browsers.recurity.com/&#34;&gt;Chrome plugin&lt;/a&gt; that claims to offer support, but as the authors from &lt;a href=&#34;http://www.theregister.co.uk/2011/11/23/browser_crypto_plugin_debuts/&#34;&gt;this post&lt;/a&gt; highlight:&lt;/p&gt;
&lt;p&gt;*There may also be resistance from crypto users – who already are a security-conscious lot – to trusting private keys and confidential messages to a set of PGP functions folded inside some JavaScript running inside a browser. *&lt;/p&gt;
&lt;p&gt;I’d have to say I agree. After following the instructions to install the plugin, I balked when it asked for my private key; I just didn’t feel comfortable. Despite this shortfall, most End Point email isn’t encrypted end-to-end. However, I can feel good knowing that my “last mile” connection to End Point’s servers are encrypted, end-to-end using encrypted POP, SMTP, and HTTPS.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Christmas Tree Commerce in 2011</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2011/12/christmas-tree-commerce-in-2011/"/>
      <id>https://www.endpointdev.com/blog/2011/12/christmas-tree-commerce-in-2011/</id>
      <published>2011-12-09T00:00:00+00:00</published>
      <author>
        <name>Brian J. Miller</name>
      </author>
      <content type="html">
        &lt;p&gt;Took a bit of a break today to get one of those perennial activities out of the way, the great Christmas tree shop. Much hasn’t changed about this time honored tradition, don the hats and gloves (well, at least until global warming takes over), pile the family in the car, and hit the ATM to get a bundle of cash to pass “under the table.” Not so fast, this is 2011 and Christmas tree lots aren’t what they used to be.&lt;/p&gt;
&lt;p&gt;Rest assured much of the experience hasn’t changed, you still get to wade up and down aisles of freshly cut firs. Trying to select just the right balance of fat vs. thin, tall vs. short, density vs. ornament hanging potential, and there is still some haggling over price (if you are lucky) and the inevitable chainsawing, bundling, and twining to the top of the old station wagon (well, SUV). But today did have a big difference, and one that our e-commerce clients and more so our bricks and mortar clients should be particularly mindful, the “cash box” with the flip up lid and stacks of tens and twenties had been replaced by an iPad with a card reader. This Christmas tree lot has gone high tech, all the way. The iPad totaled the order, and with card reader attached, took my payment, allowed me to sign the screen with a finger, and e-mailed me my receipt.&lt;/p&gt;
&lt;p&gt;As much as I appreciated the simplicity and convenience of paying it is a tough argument to make that it is that much better from the consumer side, paying in cash was pretty simple before too, but the secret here is for the vendor. This particular vendor (not exactly Apple) has eight tree lots around town, but what the iPad has done for this little, short lived merchant is provide real time inventory tracking, supply management, resource management, and the underpinnings of customer relationship management. In an instant they are able to see which lots are having the most foot traffic, which lots trend at which times, which are running low on a particular sort of tree, and make adjustments to stock and resourcing accordingly. I will be quite surprised if I don’t receive an e-mail from them next year reminding me just where their lot is located, and that it is time to buy the centerpiece of holiday decorations.&lt;/p&gt;
&lt;p&gt;Of course next year it will be 2012, and credit card mag strips are really so 2011, so if I need to bring anything other than my cellphone I’ll be just a little disappointed. (Did I mention they provide delivery? I guess in case you drive a smart car.)&lt;/p&gt;
&lt;p&gt;@Happy_Holidays!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Google 2-factor authentication</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2011/03/google-2-factor-authentication/"/>
      <id>https://www.endpointdev.com/blog/2011/03/google-2-factor-authentication/</id>
      <published>2011-03-14T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;About a month ago, Google made available to all users their new 2-factor authentication, which they call &lt;a href=&#34;https://googleblog.blogspot.com/2011/02/advanced-sign-in-security-for-your.html&#34;&gt;2-step authentication&lt;/a&gt;. In addition to the customary username and password, this optional new feature requires that you enter a 6-digit number that changes every 30 seconds, generated by the Google Authenticator app on your Android, BlackBerry, or iPhone. The app looks like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2011/03/google-2-factor-authentication/image-0-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5584162442962701106&#34; src=&#34;/blog/2011/03/google-2-factor-authentication/image-0.png&#34; style=&#34;cursor:pointer; cursor:hand;width: 225px; height: 400px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This was straightforward to set up and has worked well for me in the past month. It would thwart bad guys who intercept your password in most cases. It would also lock you out of your Google account if you lose your phone and your emergency scratch codes. :)&lt;/p&gt;
&lt;p&gt;I was happy to see this is all based on some open standards under development, and Google has made this even more useful by releasing an open source &lt;a href=&#34;https://mirrors.edge.kernel.org/pub/linux/libs/pam/whatispam.html&#34;&gt;PAM&lt;/a&gt; module called &lt;a href=&#34;https://github.com/google/google-authenticator&#34;&gt;google-authenticator&lt;/a&gt;. With that PAM module, a Linux system administrator can require a Google Authenticator code in addition to password authentication for login.&lt;/p&gt;
&lt;p&gt;I tried this out on a CentOS x86_64 system and found it fairly straightforward to set up. I ran into two minor gotchas which were reported by others as well:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The Makefile calls sudo directly, which it shouldn’t—​I was running a minimal installation without sudo installed, and in any case the administrator should decide when to become root and how. &lt;a href=&#34;https://github.com/google/google-authenticator/issues/17&#34;&gt;(Issue 17)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The Makefile installs into /lib/security instead of /lib64/security. This has since been fixed. &lt;a href=&#34;https://github.com/google/google-authenticator/issues/6&#34;&gt;(Issue 6)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After build and installation it was easy to generate a secret key for each individual user account. The key is stored in the user’s home directory, which &lt;a href=&#34;https://github.com/google/google-authenticator/issues/4&#34;&gt;Issue 4&lt;/a&gt; notes has some downsides, and the resolution to &lt;a href=&#34;https://github.com/google/google-authenticator/issues/24&#34;&gt;Issue 24&lt;/a&gt; provides a partial workaround for this. The home directory seems like a nice default to me.&lt;/p&gt;
&lt;p&gt;In the end, I found the google-authenticator module isn’t suitable for my regular ssh use due to no fault of its developers. I normally use SSH public key authentication and that’s handled by OpenSSH natively, separately from PAM, and thus bypasses 2-factor authentication entirely. So I can have 2-factor authentication with password authentication, but not with public key authentication, which is really what I want.&lt;/p&gt;
&lt;p&gt;Does anyone know of a way to configure things that way? I wasn’t able to find a way, so I’m not planning on using this for shell logins right now. But it’s still a nice option for Google logins right now, and I expect the google-authenticator project will advance over time.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>API gaps: an Android MediaPlayer example</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2011/03/api-gaps-android-mediaplayer-example/"/>
      <id>https://www.endpointdev.com/blog/2011/03/api-gaps-android-mediaplayer-example/</id>
      <published>2011-03-02T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;Many programming library APIs come with several levels of functionality, including the low-level but flexible way, and the high-level and simpler but limited way. I recently came across a textbook case of this in Android’s Java audio API, in the &lt;a href=&#34;https://developer.android.com/reference/android/media/MediaPlayer.html&#34;&gt;MediaPlayer class&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We needed to play one of several custom Ogg Vorbis audio files in the &lt;a href=&#34;https://web.archive.org/web/20110902084453/http://www.locateexpress.com/&#34;&gt;Locate Express&lt;/a&gt; Android app to alert the user to various situations.&lt;/p&gt;
&lt;p&gt;Getting this going initially was fairly straightforward:&lt;/p&gt;
&lt;script src=&#34;https://gist.github.com/850599.js?file=PlaySound.java&#34;&gt;&lt;/script&gt;
&lt;p&gt;In this simplified version of our PlaySound class we pass in the app resource ID of the sound file, and using the &lt;a href=&#34;https://developer.android.com/reference/android/media/MediaPlayer#create(android.content.Context,%20int)&#34;&gt;MediaPlayer.create()&lt;/a&gt; method is about as simple as can be.&lt;/p&gt;
&lt;p&gt;We keep a map of playing sound files so that external events can stop all playing sounds at once in a single call.&lt;/p&gt;
&lt;p&gt;We set an OnCompletionListener to clean up after ourselves if the sound plays to its end without interruption.&lt;/p&gt;
&lt;p&gt;Everything worked fine. Except for a pesky volume problem in real-world use. MediaPlayer uses Android’s default audio stream, which seemed to be &lt;a href=&#34;https://developer.android.com/reference/android/media/AudioManager#STREAM_MUSIC&#34;&gt;STREAM_MUSIC&lt;/a&gt;. That plays the audio files fine, but has an interesting consequence during the actual playing: You can’t turn the volume down or up because the volume control outside of any specific media-playing contexts affects the &lt;a href=&#34;https://developer.android.com/reference/android/media/AudioManager#STREAM_RING&#34;&gt;STREAM_RING&lt;/a&gt; volume, not the one we’re playing on. Practically speaking, that’s a big problem because if the music stream was turned up all the way and the alert goes off at full volume in a public place, you have no way to turn it down! (Not a hypothetical situation, as you may guess &amp;hellip;)&lt;/p&gt;
&lt;p&gt;Switching to STREAM_RING would be the obvious and hopefully simple thing to do, but calling the &lt;a href=&#34;https://developer.android.com/reference/android/media/MediaPlayer.html#setAudioStreamType(int)&#34;&gt;MediaPlayer.setAudioStreamType() method&lt;/a&gt; must be done before the MediaPlayer state machine enters the prepared state, which the MediaPlayer.create() does automatically. The convenience is our undoing!&lt;/p&gt;
&lt;p&gt;Switching over to the low-level way of doing things turns out to be a bit of a pain because:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;There’s no interface to pass an Android resource ID to one of the &lt;a href=&#34;https://developer.android.com/reference/android/media/MediaPlayer.html#setDataSource(java.lang.String)&#34;&gt;setDataSource()&lt;/a&gt; methods. Instead, we have to use a file path, file descriptor, or URI.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The easiest of those options seemed to be a URI, and doing a little research, the format comes to light: android.resource://package.name/resource_id&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We have to handle IOException which wasn’t throwable using the higher-level MediaPlayer.create() invocation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Putting it all together, we end up with:&lt;/p&gt;
&lt;script src=&#34;https://gist.github.com/850599.js?file=PlaySound2.java&#34;&gt;&lt;/script&gt;
&lt;p&gt;It’s not so hard, but it’s not the one-line addition of MediaPlayer.setAudioStreamType() that I expected. This is an example of how the API lacking a MediaPlayer.setDataSource(Context, int) for the resource ID makes a simple change a lot more painful than it really needs to be—​especially since the URI variation could easily be handled behind the scenes by MediaPlayer.&lt;/p&gt;
&lt;p&gt;I later took a look at the &lt;a href=&#34;https://github.com/aosp-mirror/platform_frameworks_base/blob/gingerbread/media/java/android/media/MediaPlayer.java#L648&#34;&gt;Android MediaPlayer class source&lt;/a&gt; to see how the create() method does its work:&lt;/p&gt;
&lt;script src=&#34;https://gist.github.com/850599.js?file=MediaPlayer-excerpt.java&#34;&gt;&lt;/script&gt;
&lt;p&gt;Instead of creating a URI, the authors chose to go the file descriptor route, and they check for exceptions just like I had to. It seems more cumbersome to have to open the file, get the descriptor, and manually call getStartOffset() and getLength() in the call to setDataSource, but perhaps there’s some benefit there.&lt;/p&gt;
&lt;p&gt;This gap between low-level and high-level interfaces is another small lesson I’ll remember both when using and creating APIs.&lt;/p&gt;
&lt;p&gt;There’s one final unanswered question I had earlier: Was STREAM_MUSIC really the default output stream? Empirically that seemed to be the case, but I didn’t see it stated explicitly anywhere in the documentation. To find out for sure we have to delve into the native C++ code that backs MediaPlayer, in &lt;a href=&#34;https://github.com/aosp-mirror/platform_frameworks_base/blob/gingerbread/media/libmedia/mediaplayer.cpp#L48&#34;&gt;libmedia/mediaplayer.cpp&lt;/a&gt;, and sure enough, in the constructor the default is set:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mStreamType = AudioSystem::MUSIC;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;My experience with Android so far has been that it’s well documented, but it’s been very nice to be able to read the source and see how the core libraries are implemented when needed.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>DROID 2 review</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2011/01/droid-2-review/"/>
      <id>https://www.endpointdev.com/blog/2011/01/droid-2-review/</id>
      <published>2011-01-24T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;I got a &lt;a href=&#34;https://web.archive.org/web/20110221030520/http://www.motorola.com/Consumers/US-EN/Consumer-Product-and-Services/Mobile-Phones/Motorola-DROID-2-US-EN&#34;&gt;Motorola DROID 2&lt;/a&gt; phone a couple of months ago and have assembled here my notes about how it has worked out so far. First, some background.&lt;/p&gt;
&lt;p&gt;This is my second Android phone. My first was the &lt;a href=&#34;https://web.archive.org/web/20110221122921/http://developer.htc.com/google-io-device.html&#34;&gt;Google Ion&lt;/a&gt;, basically the same as the HTC Magic. That was running standard Android 1.5 (Cupcake), while the DROID 2 runs Android 2.2 (Froyo) tweaked somewhat by Motorola. I’ve used several other Android phones belonging to friends and relatives.&lt;/p&gt;
&lt;p&gt;Overall I like the Android operating system fairly well. Like everything, it can be improved. It’s been advancing at a fairly quick pace. It’s mostly free software. Too many phones are locked down and have to be broken into to change the operating system, but Android’s still a freer computing environment than most phones have and I hope the situation will improve over time.&lt;/p&gt;
&lt;p&gt;I take for granted much of Android’s feature set: The excellent system-wide notification bar that many apps hook into and which is always easy to get to. The solid multitasking. Automatic screen adjustment for using the phone in landscape vs. portrait mode. The ability to mount the normal filesystem on the SD card from another computer via USB or by removing the SD card. The freedom to run any applications I want, downloaded from wherever, not just the Android Market.&lt;/p&gt;
&lt;p&gt;For one of our clients, I’m currently developing an Android application in Java that uses geolocation, JSON web services, and Urban Airship’s &lt;a href=&#34;https://www.urbanairship.com/products/mobile-app-engagement&#34;&gt;AirMail Push&lt;/a&gt; for push notifications. So I’m using the phone both as my main mobile phone and as an app developer.&lt;/p&gt;
&lt;h3 id=&#34;keyboard&#34;&gt;Keyboard&lt;/h3&gt;
&lt;p&gt;This is the first phone I’ve had with a full slide-out QWERTY keyboard. I wasn’t sure if I’d use it often or just stick with the touchscreen keyboard. So far I use the real keyboard most of the time. But it’s nice to be able to use either the hard keyboard or touchscreen keyboard, whichever’s more convenient.&lt;/p&gt;
&lt;p&gt;I like the keyboard a lot, especially that its rows are offset like a normal keyboard and not a straight aligned grid:&lt;/p&gt;
&lt;img width=&#34;376&#34; height=&#34;369&#34; src=&#34;/blog/2011/01/droid-2-review/Droid2_FrontOpen_email_alt.png&#34;&gt;
&lt;p&gt;The real keyboard makes using ssh (via the ConnectBot app) on the phone much easier than with the touchscreen keyboard, because the real keyboard doesn’t use up any screen real estate.&lt;/p&gt;
&lt;p&gt;The biggest annoyance for me has been the keyboard’s microphone key which launches the voice commands function. I don’t use voice commands, so this has never been what I’ve wanted and is highly annoying when accidentally pressed.&lt;/p&gt;
&lt;p&gt;Third-party alternative touchscreen keyboards for European languages, Hebrew, Arabic, and others work pretty well too.&lt;/p&gt;
&lt;h3 id=&#34;touchscreen&#34;&gt;Touchscreen&lt;/h3&gt;
&lt;p&gt;I really miss having physical buttons for menu, home, back, and search, as I had on the HTC Magic:&lt;/p&gt;
&lt;img width=&#34;180&#34; height=&#34;360&#34; src=&#34;/blog/2011/01/droid-2-review/htc-magic-google-ion-vertical_180x360.png&#34;&gt;
&lt;p&gt;On the DROID 2 they’re touchscreen buttons and it’s annoying and error-prone to have to look at the phone and carefully press one. Much better to have real buttons for those core functions used by all apps.&lt;/p&gt;
&lt;p&gt;However, good riddance to the trackball.&lt;/p&gt;
&lt;p&gt;The DROID 2 screen itself is higher resolution, bright, and responds well to touch, swipe, and multitouch, as you’d expect.&lt;/p&gt;
&lt;h3 id=&#34;battery&#34;&gt;Battery&lt;/h3&gt;
&lt;p&gt;Battery life on the DROID 2 is not great, especially when using GPS heavily. But that’s a problem on most phones.&lt;/p&gt;
&lt;p&gt;I later got the optional extended-life battery. It isn’t much bigger or heavier, and does add a lot more life to the phone. It wouldn’t be necessary if I weren’t doing heavy GPS &amp;amp; web service interaction with my own Android app under development.&lt;/p&gt;
&lt;h3 id=&#34;audio&#34;&gt;Audio&lt;/h3&gt;
&lt;p&gt;The DROID 2 has a normal 1/8&amp;quot; stereo headphone jack, which is nice. The HTC Magic needed a special adapter to use normal headphones, which was a pain.&lt;/p&gt;
&lt;p&gt;Audio playback is usually fine, though on a couple of occasions it’s stuttered, presumably while contending with other apps for CPU. Audio playback when the screen is off doesn’t drain the battery much at all.&lt;/p&gt;
&lt;h3 id=&#34;calling-and-contacts&#34;&gt;Calling and Contacts&lt;/h3&gt;
&lt;p&gt;There have been a few isolated incidents, perhaps involving switching off Bluetooth, where I couldn’t turn down the call volume in the middle of the call. That could’ve been either a generic Android problem, or the Motorola Android build; I’m not sure.&lt;/p&gt;
&lt;p&gt;I didn’t much like the special Motorola apps that shipped on the phone, but simply not using them has made them no problem.&lt;/p&gt;
&lt;p&gt;An exception to that is the Contacts app that integrates with Facebook and Twitter. That’s a lot more helpful than I imagined it would be, though when you have many contacts it can sometime slow down on opening an individual contact, when I imagine it tries to fetch the latest details from social networks.&lt;/p&gt;
&lt;p&gt;The in-call screen’s “Mute” button is small and way too close to the “End call” button:&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2011/01/droid-2-review/image-0.png&#34; style=&#34;width: 225px; height: 400px;&#34;&gt;
&lt;p&gt;That’s led to me accidentally hanging up during conference calls a couple of times. The stock open source Android call screen is arguably less pretty, but equally-sized buttons makes it easier to use:&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2011/01/droid-2-review/image-1.png&#34; style=&#34;width: 270px; height: 400px;&#34;&gt;
&lt;p&gt;Because I’m using Verizon for the first time with this phone, I’ve been introduced to the sad problem of not being able to use voice and data at the same time on CDMA. I used to laugh about that when I was using AT&amp;amp;T because it seemed silly. Now I find it really does get in the way fairly often.&lt;/p&gt;
&lt;p&gt;The normal Skype app doesn’t allow using a Bluetooth headset or using the cell network for calls (in the US). The special Verizon Skype app burns voice minutes and won’t let you use wifi for calling (in the US). The combination of proprietary software plus controlling phone carriers is bad for users! Because of the limitations I use Skype mostly for text chat on my phone.&lt;/p&gt;
&lt;h3 id=&#34;gps&#34;&gt;GPS&lt;/h3&gt;
&lt;p&gt;As I mentioned, the GPS uses a lot of battery. But it works well. The GPS on my HTC Magic sometimes took a very long time to get a location fix, but on the DROID 2 it’s fast and works very well. Given the location-aware app I’m working on, I’ve had more opportunity to notice this than I otherwise might have.&lt;/p&gt;
&lt;p&gt;There was one funny problem with the GPS, though. Once when the battery got down to 5% or so, the GPS started to report hilariously wrong data to my app, and thus to the web service it reported to. For example, it reported every second that my phone, which was sitting on my nightstand, was traveling at 121 m/s (270 mph), and the point in latitude and longitude moved quickly. This continued even after I’d plugged the phone back in. I haven’t seen it happen since, but it was strange.&lt;/p&gt;
&lt;h3 id=&#34;useful-apps&#34;&gt;Useful apps&lt;/h3&gt;
&lt;p&gt;I don’t spend a lot of time trying out new apps, but I’ve found several to be very useful: K-9 Mail for multiple mail accounts (reviewed yesterday on &lt;a href=&#34;https://arstechnica.com/information-technology/2011/01/excellent-k-9-mail-app-for-android-keeps-your-messages-on-a-leash/&#34;&gt;Ars Technica&lt;/a&gt;), Yaaic (an IRC client), 3G Watchdog (bandwidth monitor), TweetsRide (Twitter client), Google Sky Map, and My Tracks. I haven’t found a great music player yet, but the built-in Android Music app and Winamp are both fine.&lt;/p&gt;
&lt;h3 id=&#34;security&#34;&gt;Security&lt;/h3&gt;
&lt;p&gt;I’ve long used crypto filesystems on desktop and laptop computers, and feel the lack on Android. A phone needs a solid cryptofs option more than normal computers even, and I hope that’ll be available soon.&lt;/p&gt;
&lt;h3 id=&#34;the-end-for-now&#34;&gt;The end (for now)&lt;/h3&gt;
&lt;p&gt;I’m probably forgetting lots of things, but this is already longer than I’d expected, so I’ll stop now and put any afterthoughts in the comments. In short, I like the phone, but it’s hard to avoid looking forward to a brighter future when it comes to mobile devices.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Google Voice first impressions</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2009/08/google-voice-first-impressions/"/>
      <id>https://www.endpointdev.com/blog/2009/08/google-voice-first-impressions/</id>
      <published>2009-08-13T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;I’ve been using &lt;a href=&#34;https://voice.google.com/&#34;&gt;Google Voice&lt;/a&gt; on and off for about a week now, and wanted to share my first impressions.&lt;/p&gt;
&lt;p&gt;Google Voice is still available only by invite, but I only had to wait about 3 days to get access. Signup was quick and easy, and doesn’t cost any money. The hardest part was selecting a phone number &amp;ndash; you can choose one from most anywhere in the United States, but as far as I know you can’t change it later, at least not easily. I finally got the Montana phone number I’ve always wanted!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Simultaneous multiple call forwarding:&lt;/strong&gt; The feature I was most aware of beforehand is having your Google Voice number forward calls to as many other numbers as you want. This is actually better than I expected, because it calls the other numbers simultaneously, not in series, so there’s very little delay to the caller compared with directly calling. Whichever phone you answer first and tell the robot lady “1”, is the one that takes the call.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Android integration (not VoIP):&lt;/strong&gt; I was most excited to use Google Voice as a VoIP client on my Android phone, but that is a feature I apparently imagined all by myself. The Android Google Voice client still uses cell phone minutes and goes through the cell network, but it routes the calls through Google’s infrastructure and shows caller ID as your Google Voice number. The point of this is to save money on international calls because you are only making a domestic call to your Google Voice number as far as your cell phone provider is concerned, and then Google routes the international call and charges you a few cents a minute. (This is the only part of Google Voice that costs money at the moment, as far as I know.) That’s a neat feature, but what I really wanted was a completely VoIP client that I can use on wifi or a 3G data network to make calls entirely outside the cell phone network. Any Google Voice developers reading: Here’s my enthusiastic vote for that! I would use it all the time if it existed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Google Contacts integration:&lt;/strong&gt; Because I’m using an Android phone and its native contact management, my contacts are synced with my Google account and thus appear in Google Voice automatically. That is convenient. It means the GOOG has all my contact info. That’s more or less the case with anyone routing phone calls for me anyway.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Free SMS:&lt;/strong&gt; Google Voice does free SMS text messages, both sending and receiving, even if you have no cell phone. If you do have a cell phone configured with it, it also forwards incoming SMS to your phone. Very handy. Every cell phone SMS someone has sent me has made it, but for some reason I haven’t been able to get Skype’s SMS to go to Google Voice at all. That keeps me for now from having SkypeOut calls show a Google Voice number as the caller ID, unfortunately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Free domestic long distance:&lt;/strong&gt; Another nice feature I hadn’t known about before is Google Voice’s ability to initiate two outgoing calls, one to one of my configured phones, and the other to anyone else, and connect us. This makes it possible to use no long distance but call whoever you want. A feature I haven’t tried yet is having others call the Google Voice number and conferencing them in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Volume attenuation problem:&lt;/strong&gt; The biggest problem I’ve noticed with Google Voice call quality so far is a serious attenuation of sound volume. When talking one-on-one with someone it’s tolerable, and many people may not notice it. But when I called into a conference call with Google Voice people could barely hear me at all, when I talked loudly. I could hear them ok, but they couldn’t hear me. I use that conference call facility all the time with regular phones, cell phones, and SkypeOut, and have no problems there. If Google could somehow boost the call volume to compensate for the extra connections, that might help. But for now I simply can’t use it for conference calls, where I spend a fair amount of time every week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Silent participants dropped:&lt;/strong&gt; The second-biggest problem is that Google Voice apparently intentionally drops the call without warning whenever one side is silent for more than a few minutes. I haven’t seen this documented anywhere, and Google doesn’t tell you in any way, but it’s pretty clear that’s what’s happening. As long as both sides are talking fairly frequently it’s fine, but on conference calls I often have to stay muted for many minutes while others are talking, and then I’m unceremoniously dropped. So that’s strike two against using Google Voice for conference calls right now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Voice mail transcription:&lt;/strong&gt; Voice mail left at Google Voice is available via the Android app, the web app, can be emailed to you, but most amazingly, if in English, is automatically transcribed and sent via SMS text message! It was hard to imagine this would work very well. I had Kiel test it for me. Compare &lt;a href=&#34;/blog/2009/08/google-voice-first-impressions/google-voice-kiel-transcription.mp3&#34;&gt;his voice mail recording&lt;/a&gt; to the transcription:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A little cutie of the corner told me to leave you not Grandmama call messages and so the core in based cheese card test, kittens From, trucking, and you know from getting to
fat until all the super caliper at July 6th and if that’s not ballistic. I don’t even know it’s realistic. Because let’s just face it. Corn is, where’s that. See you.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Note that it displays the transcription lighter or darker depending on its level of confidence in each word. The transcription is not perfect, and Kiel deliberately chose words and phrases to be difficult to decode, but it’s definitely close enough to give an idea of what the message is about without having to listen to the recording. Very useful if you’re somewhere you can only peek at the phone for a few moments and can decide if the message warrants excusing yourself from the room or not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Listen in:&lt;/strong&gt; I haven’t used this yet, but can imagine I would. You can let people start leaving voice mail but listen to it in real time and decide to take the call, just as with old answering machines.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Switch phones:&lt;/strong&gt; I haven’t used this yet either, but you can switch phones during a call from a land line to a cell phone or vice versa.&lt;/p&gt;
&lt;p&gt;In summary, it is not the be-all and end-all of phone services that I may have hoped for, but it’s a very useful addition to the world of phone services, is easy to get going with, and is mostly free of charge. It’ll be fun to see where Google takes this.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Look Ma, I made an iPhone Enterprise Application!</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2009/08/iphone-enterprise-application/"/>
      <id>https://www.endpointdev.com/blog/2009/08/iphone-enterprise-application/</id>
      <published>2009-08-03T00:00:00+00:00</published>
      <author>
        <name>David Christensen</name>
      </author>
      <content type="html">
        &lt;p&gt;One of our clients recently came to us for help with implementing an iPhone Enterprise application. This particular application involved deploying specific signed mail configurations for customers with iPhones. This was primarily a server-side application, although the front-end interface was created using the DashCode development tools from Apple. Although this was a web application, the DashCode integration enabled us to create the interface in a way that it appeared to be a native application on the iPhone. Client-side validation was performed in a way that for all intents and purposes appeared native.&lt;/p&gt;
&lt;p&gt;The backend was a traditional CGI script which generated the .mobileconfig configuration payloads to easily integrate the customer’s mail server information into the iPhone’s Mail application. The backend was written to support any number of accounts deployed per customer, and the resulting payload was signed and verified by the customer’s PEM key.&lt;/p&gt;
&lt;p&gt;We integrated openssl’s pkcs12 support to transparently sign the generated dynamic mobileconfig. This was keyed off of the client’s deployment key, so all of the generated keys were automatically indicated as trusted and registered by the client when installed on the iPhone.&lt;/p&gt;
&lt;p&gt;Action shots:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://4.bp.blogspot.com/_eLhk5Eevkf8/Snckmvx40MI/AAAAAAAAAAw/QD6fsoAIHSA/s1600-h/Picture+3.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5365797729114575042&#34; src=&#34;/blog/2009/08/iphone-enterprise-application/image-0.png&#34; style=&#34;margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 176px; height: 320px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://2.bp.blogspot.com/_eLhk5Eevkf8/Snck1wTNpWI/AAAAAAAAAA4/7AfXR4efyuQ/s1600-h/Picture+4.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5365797986952389986&#34; src=&#34;/blog/2009/08/iphone-enterprise-application/image-0.png&#34; style=&#34;margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 173px; height: 320px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Gmail Contacts Notes Converter</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2009/07/gmail-contacts-notes-converter/"/>
      <id>https://www.endpointdev.com/blog/2009/07/gmail-contacts-notes-converter/</id>
      <published>2009-07-21T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;As I &lt;a href=&#34;/blog/2009/05/google-io-2009-day-1/&#34;&gt;mentioned previously&lt;/a&gt;, I recently got a Google Ion phone running Android. I recently began using it as my main mobile phone, and thus needed to finally migrate the contacts from my Nokia 6126 phone to Android.&lt;/p&gt;
&lt;p&gt;This is apparently easy to do by first copying all the contacts from the Nokia 6126 internal memory to the SIM card, then moving the SIM card to the Ion and importing the contacts. But that only works if all your contacts fit on the SIM card. If not, they’re truncated, and you have to delete many contacts on the Nokia to fit more, which would be a nonreversable move.&lt;/p&gt;
&lt;p&gt;Several posts describe ways to do the export and import, such as &lt;a href=&#34;http://blog.lickmyear.org/2009/01/story-of-nokia-and-android-contact.html&#34;&gt;this one&lt;/a&gt; that didn’t really apply to my phone, and &lt;a href=&#34;https://www.javaworld.com/article/2072557/g1-contacts-import.html&#34;&gt;this one that involves VCF export &amp;amp; import&lt;/a&gt; which I didn’t see a way to do.&lt;/p&gt;
&lt;p&gt;Ultimately I found an article that described &lt;a href=&#34;https://web.archive.org/web/20090725154507/https://www.nokiausa.com/get-support-and-software/software/nokia-suites-for-your-pc&#34;&gt;Nokia’s PC Suite&lt;/a&gt; software that I’d never heard of before, which I downloaded on an old Windows machine and used to download the contacts from the phone via Bluetooth, then export to a CSV file and import into Gmail. So far, so good.&lt;/p&gt;
&lt;p&gt;Except as &lt;a href=&#34;https://web.archive.org/web/20100318041417/http://forums.t-mobile.com/t5/ARCHIVED-Help-How-To/Nokia-N73-Contacts-transfer-to-G1/m-p/43321&#34;&gt;this post&lt;/a&gt; and &lt;a href=&#34;https://www.theinquirer.net/inquirer/news/1049400/fiddling-android-brain&#34;&gt;another post&lt;/a&gt; describe, then all the contact data showed up in a single Notes field, useless for dialing or emailing.&lt;/p&gt;
&lt;p&gt;I decided it would be easiest to convert the Notes data into normal phone fields since I already had some contact information in the phone and couldn’t find any other reasonable way.&lt;/p&gt;
&lt;p&gt;I came up with this &lt;a href=&#34;https://gist.github.com/jonjensen/151139&#34;&gt;Gmail Contacts Notes Converter&lt;/a&gt; script to solve the problem. It takes Gmail-exported CSV as input, converts any Notes field data into standard Work or Personal contacts, and outputs CSV that can be re-imported into Gmail. It requires Perl 5.10.0 and I’ve only tested it on Linux. (It could be modified to work with earlier Perl versions fairly easily.)&lt;/p&gt;
&lt;p&gt;Perhaps it will be useful to someone else as well.&lt;/p&gt;

      </content>
    </entry>
  
</feed>
