tag:chrisguzman.svbtle.com,2014:/feedChris Guzman2018-03-15T18:53:10-07:00Chris Guzmanhttps://chrisguzman.svbtle.comSvbtle.comtag:chrisguzman.svbtle.com,2014:Post/i-hacked-the-pocket-app-to-cut-down-on-tabs2018-03-15T18:53:10-07:002018-03-15T18:53:10-07:00How I hacked around the Pocket app to cut down on my browser tabs.<p>I’ve been an app developer for a bit now and I had never published a side project app on my own. So I figured I’d change that! I wanted to solve a problem I had of opening too many tabs in my browser on my phone. Recently I was on a plane and had over 50 browser tabs that wouldn’t load in airplane mode and was frustrated that I hadn’t remember to save any of them to <a href="https://play.google.com/store/apps/details?id=com.ideashower.readitlater.pro" rel="nofollow">Pocket</a>. I had a bad habit of opening links to read later but not saving them. How could I make it easier to save links to Pocket?</p>
<p>What I wanted was an app that could behave like a browser. I could open links from the Twitter, Facebook and Hacker News apps and then immediately share to the Pocket app. I disliked the current flow of saving links to Pocket:</p>
<ol>
<li>Click on a link in the Twitter app</li>
<li>Wait for the Chrome app to pop up</li>
<li>Click the option menu</li>
<li>Hit the share button</li>
<li>Wait for the share sheet to finally finish loading</li>
<li>Look for “Pocket”, but remember that the text is “Add to Pocket”, not “Pocket”</li>
<li>Scroll back up the share sheet</li>
<li>Go to click on the Pocket icon</li>
<li>Mysteriously and (seemingly at random) the share sheet loads a top part and I almost click the wrong icon</li>
<li>Finally scroll back down and click the Pocket icon</li>
</ol>
<p><img src="http://i.imgur.com/KyTy8LV.gif" alt="Gif of app loading and then another button popping up in place of another button"></p>
<p><em>Actual gif of the share sheet loading in Android</em></p>
<p>I was also inspired to build the app after using <a href="https://support.mozilla.org/en-US/kb/open-links-background-later-viewing-firefox-android" rel="nofollow">Firefox Mobile’s feature to launch tabs in the background</a> when clicking on links. The Pocket & Firefox apps show an Activity without a layout file, instead flashing some kind of modified Toast, so I figured I could make an app do the same to solve my problem.</p>
<p>First thing I did was try to find the activity in Pocket that saved an Article without a View. I used the <a href="https://developer.android.com/studio/build/apk-analyzer.html#view_the_androidmanifestxml" rel="nofollow">APK Analyzer in Android Studio</a> to look at Pocket’s AndroidManifest and see the list of activities. <code class="prettyprint">AddActivity</code> stood out since it had a bunch of intent filters that would receive another app’s share intents. A quick google search confirmed that this was the Activity I wanted.</p>
<p>Now I could have used ADB to fire an intent to Pocket’s <code class="prettyprint">AddActivity</code> to test it out, but I was itching to just build the app.</p>
<p>I knew the first activity had to do a few things:</p>
<ol>
<li>Receive intents that browser would usually open</li>
<li>Not show any UI when the activity started, </li>
<li>Open a new intent to Pocket’s <code class="prettyprint">AddActivity</code>.</li>
</ol>
<p>Thankfully, most of the magic is in my apps AndroidManifest. I created a <code class="prettyprint">BrowserInterceptActivity</code> and gave it the following properties:</p>
<pre><code class="prettyprint lang-xml"><activity
android:name=".BrowserInterceptActivity"
android:theme="@android:style/Theme.NoDisplay"
android:noHistory="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:mimeType="text/html" />
<data android:mimeType="text/plain" />
<data android:mimeType="application/xhtml+xml" />
</intent-filter>
</activity>
</code></pre>
<p>I shamelessly looked up what <a href="https://github.com/mozilla-mobile/focus-android/blob/51771213b47455287cb96732b82f5a9c78d453c0/app/src/main/AndroidManifest.xml#L31" rel="nofollow">intent filters the Firefox Focus mobile app uses</a> to make sure I had the correct ones. I <em>think</em> added <code class="prettyprint">android:noHistory="true</code> because I was going to add a “About the app” activity and didn’t want to worry about this activity in the back stack. And <code class="prettyprint">android:theme="@android:style/Theme.NoDisplay"</code> is there so that the Activity doesn’t inflate a View. </p>
<p>I was also itching to learn a bit of Kotlin. So I whipped up the following activity in Kotlin as a proof of concept.</p>
<pre><code class="prettyprint lang-kotlin">class BrowserInterceptActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val action = intent.action
var dataString: String
if (Intent.ACTION_VIEW.equals(action)) {
dataString = intent.dataString
if (TextUtils.isEmpty(dataString)) {
showError()
return
} else {
openInPocket(dataString)
}
}
finishAffinity()
}
private fun openInPocket(dataString: String) {
val intent = Intent()
intent.setClassName("com.ideashower.readitlater.pro", "com.ideashower.readitlater.activity.AddActivity")
intent.putExtra(Intent.EXTRA_TEXT, dataString)
if (isPocketAvailable(intent)) {
startActivity(intent)
} else {
showError()
}
finish()
}
private fun isPocketAvailable(intent: Intent): Boolean {
val packageManager = packageManager ?: return false
val activities = packageManager.queryIntentActivities(intent, 0)
return activities.size > 0
}
private fun showError() {
Toast.makeText(this@BrowserInterceptActivity, "Error opening in Pocket", Toast.LENGTH_LONG).show()
}
}
</code></pre>
<p>It’s pretty simple as you can see. There’s a tiny bit of error handling. When the activity opens, it checks the intent for a String, then opens the intent for Pocket’s <code class="prettyprint">AddActivity</code>. You might also notice that I do a check to see if Pocket is installed— I added that after crashing the app in the emulator ?. Then the activity finishes with <a href="https://developer.android.com/reference/android/app/Activity.html#finishAffinity()" rel="nofollow"><code class="prettyprint">finishAffinity()</code></a>. I used that instead of <code class="prettyprint">finish()</code> because the app would also have an “About this app” activity and I didn’t want to have that Activity in the back stack.</p>
<p>That’s all I needed! I was super excited to load the APK on my phone and share on a few slacks. The app, which I’ve called Browser Interceptor looks a little something like this:</p>
<p><img src="http://g.recordit.co/2UZq8RQkiG.gif" alt="gif of app in use"></p>
<p>If you want to check out the app you can download it from the <a href="https://play.google.com/store/apps/details?id=com.chris_guzman.browserinterceptor" rel="nofollow">play store</a>. This app is also open sourced! View the <a href="https://github.com/ChrisGuzman/BrowserInterceptor" rel="nofollow">source code on GitHub</a>. PRs or Issues with any suggestions are welcome.</p>
<p>PS. Props to <a href="https://twitter.com/alexfu914" rel="nofollow">Alex Fu</a> for launching <a href="https://play.google.com/store/apps/details?id=com.alexfu.humemo" rel="nofollow">Humemo</a> and <a href="https://twitter.com/pandanomic" rel="nofollow">Zac Sweers</a> for blogging about shipping <a href="https://medium.com/@sweers/catching-up-on-catchup-introduction-7581c099f4bc" rel="nofollow">Catch Up</a>. Their projects’ prompted me to ship this side project and blog about it.</p>
tag:chrisguzman.svbtle.com,2014:Post/collaborative-tools-for-remote-teams2017-12-04T23:52:03-08:002017-12-04T23:52:03-08:00My favorite collaborative tools for working with a remote team<p>I’m on a fully remote team and there are many challenges we face on a daily basis. To add to our challenges, our team regularly travels to attend conferences and user groups. Often we’re distributed among various timezones and we don’t always have our laptops in front of us during events.</p>
<p>One the biggest problems I face is that often the person I have a question for isn’t online at the moment. At Nexmo we try to respect our team’s offline hours so just contacting them directly after 6pm their time isn’t a choice. To resolve this we’ve followed a common recommendation for remote teams: document everything.</p>
<p>One of the most difficult issues our team faces is how to collaborate while writing code. I think we miss out on the collaboration opportunities that devs at the same office have. It’s not as easy to get another set of eyes on code you’re trying to write. It can also be difficult to explain a particular problem you’re having to a coworker when you’re not in the same room. In turn it can be difficult for another person to explain a possible solution.</p>
<p>Considering some of the challenges that come with a remote team, the Nexmo Developer Relations team has come to love a few products that make our jobs easier:</p>
<p>As I mentioned our team tries our best to document everything so that the answer to a dire question is only a search away. Along those lines my favorite tools are Trello and Google Docs. </p>
<p>Our team has multiple Trello boards: One to track what events we’re attending and want to attend, another to track our progress on various projects we’re working on, another to track blog posts we’re writing, and so forth. Trello works great for us for multiple reasons. It allows us to silo our conversations to a specific topic and not get off topic. The concept of lists in Trello also helps us organize our cards and gives our boards a lifecycle that cards move through: To Do, Doing, Needs Review, and Done. The mobile app is also a dream to use. I really appreciate the work their team puts into it. The best feature so far has been offline access. When I’m on a flight without wifi to the next conference, I’m still able to check my team’s progress and communicate about a project I’m working on.</p>
<p>The other product we use to document everything is Google Docs. As I said our team is regularly spread out among different locations and timezones so async communication and collaboration is a must for us. When I want a team member to review a blog post, all I need to do is write it up in a Google Doc and share a link with them requesting their comments. Like Trello, I appreciate that Google Docs has an offline mode and gives our team a tool to use to silo our conversation to the document that we’re collaborating on.</p>
<p>Of course like most teams we use Slack for real time communication which enables all sorts of productivity. What I most value in slack is the integrations. The Github integration keeps me up to date on issues or pull requests that our uses submit without clogging up my inbox. The Twitter integration also alerts our team when a user is tweeting us with a question.</p>
<p>Although not a mobile app, my absolute favorite project collaboration tool is Screenhero. I mentioned that our team misses out on the face to face collaboration opportunities that office mates have. Screenhero solves that issue completely. With Screenhero all I need to do is share my screen with a team member and now we both have access to our own mouse cursor and both have the opportunity to type anything we need. There’s no meeting code to enter, just hit the “share screen” button.</p>
<p>One topic that often comes up among our team is that we feel that we duplicate information in many products. For instance, we’ll use Google Docs for one one collaboration, Trello for project updates amongst our team, and Atlassian’s Confluence to communicate our progress with the company at large. In dealing with that we try our best to use the plugins offered by those products to connect them to each other. For instance, we use Trello’s Google Drive powerup so we don’t have to copy and paste text from a Google Doc to a Trello card, instead we can just link a file or folder to a Trello card.</p>
<p>But to better solve that issue, I’d recommend that teams choose one product to serve a purpose and stick with it. It can be hard to start a workflow with Asana and then make your whole team move over to Jira.</p>
<p>There you have it! Some of my and my teams favorite tools to work with. Maybe you and your team use the same tools?</p>
tag:chrisguzman.svbtle.com,2014:Post/using-doclava-and-gradle-to-generate-javadocs2017-06-01T12:42:58-07:002017-06-01T12:42:58-07:00Using Doclava and Gradle to exclude classes from your generated Javadocs<p>At <a href="https://nexmo.com" rel="nofollow">Nexmo</a> I help out with the Java and Android client libraries. Recently I generated Javadocs for one of our Android client libraries. Usually, Android Studio makes this super easy. It’s as simple as using the “Generate JavaDoc” under the “Tools” menu. But I wouldn’t be writing a blog post if the problem I had to solve was so easy. </p>
<p>Our team wanted to exclude certain classes from the generated java docs. The engineers I was working with had already started that by using the <code class="prettyprint">@hide</code> annotation at the top of the classes we wanted excluded. But the problem is when you use Android Studio’s built in Javadoc generation tool, you’ll find that it doesn’t respect <code class="prettyprint">@hide</code>! So what to do?</p>
<p>The solution I found is to use <a href="https://github.com/kwf2030/doclava" rel="nofollow">Doclava</a> (the original repo can be found on <a href="https://code.google.com/archive/p/doclava/" rel="nofollow">Google Code</a>). Though the repo hasn’t been updated in a while, it still works.</p>
<p>There aren’t a lot of instructions out there how to integrate this into your Android Studio project so here’s how I did it.</p>
<p>In your library’s build.gradle file, add the following:</p>
<pre><code class="prettyprint lang-gradle">//build.gradle
configurations {
doclava
}
dependencies {
...
doclava 'com.google.doclava:doclava:1.0.6'
}
task generateJavadoc(type: Javadoc, dependsOn: project.configurations.doclava) {
failOnError = true
title = null
source = android.sourceSets.main.java.srcDirs
options.doclet = "com.google.doclava.Doclava"
options.docletpath = configurations.doclava.files.asType(List)
classpath +=
project.files(android.getBootClasspath().join(File.pathSeparator))
destinationDir = file("../javadocs/")
}
</code></pre>
<p>Now you can run the <code class="prettyprint">generateJavadoc</code> task from Gradle. If you don’t have Gradle in your command line you can use the Gradle pane in Android studio.</p>
<p><a href="https://svbtleusercontent.com/ee0oiszgf8dknq.png" rel="nofollow"><img src="https://svbtleusercontent.com/ee0oiszgf8dknq_small.png" alt="Screen Shot 2017-05-30 at 5.15.32 PM.png"></a></p>
<h2 id="troubleshooting_2">Troubleshooting <a class="head_anchor" href="#troubleshooting_2" rel="nofollow">#</a>
</h2><h3 id="after-running-the-gradle-task-an-empty-folder_3">After running the gradle task, an empty folder was created with no docs in it <a class="head_anchor" href="#after-running-the-gradle-task-an-empty-folder_3" rel="nofollow">#</a>
</h3>
<p>This might happen if <code class="prettyprint">failOnError = false</code> is set in your gradle task. I recommend you set <code class="prettyprint">failOnError = true</code> so that you can see what error prevented the javaDoc from being generated.</p>
<h3 id="where-can-i-see-the-errors_3">Where can I see the errors? <a class="head_anchor" href="#where-can-i-see-the-errors_3" rel="nofollow">#</a>
</h3>
<p>Here:<br>
<a href="https://svbtleusercontent.com/xjzsi35bjvlra.png" rel="nofollow"><img src="https://svbtleusercontent.com/xjzsi35bjvlra_small.png" alt="Screen Shot 2017-05-30 at 5.24.40 PM.png"></a></p>
<p>After you click on Gradle Console you’ll see something like this. It’s helpful to <code class="prettyprint">Run with --stacktrace</code> or one of the other options.<br>
<a href="https://svbtleusercontent.com/8sqiott6oxx2dg.png" rel="nofollow"><img src="https://svbtleusercontent.com/8sqiott6oxx2dg_small.png" alt="Screen Shot 2017-05-30 at 5.48.58 PM.png"></a></p>
<h3 id="javadoc-error-illegal-package-name-quotpackag_3">javadoc: error - Illegal package name: “…package.html” <a class="head_anchor" href="#javadoc-error-illegal-package-name-quotpackag_3" rel="nofollow">#</a>
</h3>
<p>Doclava doesn’t like <code class="prettyprint">package.html</code>. Instead it prefers <code class="prettyprint">package-info.java</code> You could convert your old <code class="prettyprint">package.html</code> files to <code class="prettyprint">package-info.java</code> by hand, <em>Or</em> you can also use Android Studio to automate it. Just open preferences for Android Studio and make sure this box is checked. <br>
<a href="https://svbtleusercontent.com/n0forv3nczuoag.png" rel="nofollow"><img src="https://svbtleusercontent.com/n0forv3nczuoag_small.png" alt="Screen Shot 2017-05-30 at 5.29.49 PM.png"></a><br>
Then when you navigate to a <code class="prettyprint">package.html</code> file you’ll see this warning. <br>
<a href="https://svbtleusercontent.com/gbvsygnbvujq.png" rel="nofollow"><img src="https://svbtleusercontent.com/gbvsygnbvujq_small.png" alt="Screen Shot 2017-05-30 at 5.31.48 PM.png"></a><br>
Clicking the convert button will automatically convert the <code class="prettyprint">package-info.java</code> to <code class="prettyprint">package-info.java</code></p>
<h3 id="after-making-changes-to-the-gradle-task-the-t_3">After making changes to the gradle task, the task still doesn’t execute correctly. <a class="head_anchor" href="#after-making-changes-to-the-gradle-task-the-t_3" rel="nofollow">#</a>
</h3>
<p>Make sure you sync your gradle files before you run the gradle task. If you see this warning: you haven’t synced yet.<br>
<a href="https://svbtleusercontent.com/tn9aruhholxcug.png" rel="nofollow"><img src="https://svbtleusercontent.com/tn9aruhholxcug_small.png" alt="Screen Shot 2017-05-30 at 6.01.50 PM.png"></a></p>
<h2 id="questions-i-still-have_2">Questions I still have <a class="head_anchor" href="#questions-i-still-have_2" rel="nofollow">#</a>
</h2>
<ul>
<li>Why do I need to include <code class="prettyprint">title = null</code> in my gradle task? I’ve tried removing that line or replacing it with <code class="prettyprint">title = "Javadoc"</code> and the task fails.</li>
<li>Can I include a navbar in my generated docs? For an example of what I mean look at the difference between the <a href="http://square.github.io/retrofit/2.x/retrofit/" rel="nofollow">Javadocs for Retrofit</a> generated <u>without</u> Doclava and the <a href="http://he5ed.github.io/volley/doc/reference/com/android/volley/package-summary.html" rel="nofollow">Javadocs for this fork of Volley</a> generated <u>with</u> Doclava. Javadocs <u>without</u> Doclava have this awesome Navbar and this “All Classes” link. How can I get that in my Doclava Javadocs?</li>
</ul>
tag:chrisguzman.svbtle.com,2014:Post/java-dates-without-joda-time2017-01-30T15:25:05-08:002017-01-30T15:25:05-08:00Dealing with TimeZones in Java without Joda Time<p>I’ve begun working on a java SDK for nexmo’s API. Thanks to the wonders of timezones I had a crappy time (the 24 hour cold I currently have didn’t help).</p>
<p>Mark works in the UK and I work on East Coast time. Only because of our team’s remote nature did we discover that we had failing tests due to timezones. Cue multiple discussions of “I don’t know what’s wrong, it works on my machine!” After noticing the bug we realized the CI didn’t catch it because Travis CI sets the timezones on its containers to UTC time and the bug only appeared off of UTC time.</p>
<p>We want to minimize dependencies so my colleague Mark and I agreed to not use Joda Time. We were definitely asking for trouble. Here’s what I learned today while debugging our tests:</p>
<ul>
<li>
<code class="prettyprint">java.util.Date.toString()</code> will always print in your timezone</li>
<li>
<code class="prettyprint">java.util.Calendar.toDate()</code> will always print in your timezone</li>
<li>
<code class="prettyprint">java.util.Date</code> doesn’t have the concepts of a timezone, only <code class="prettyprint">java.util.Calendar</code> does</li>
<li>One way to make the tests pass is use <code class="prettyprint">java.util.TimeZone.setDefault(TimeZone.getTimeZone("UTC"));</code> but that’s a cop-out. Definitely a code smell in your tests!</li>
<li>I miss Joda Time</li>
</ul>
<p>Something to note is that the problem with debugging tests is that when the tests fail it prints out the dates as a String. Printing out the dates converts it to your timezone.</p>
<p>What’s the moral of the story? …I’m not sure. I still don’t have the tests passing yet.</p>
<p>Update: We got the tests passing! How you might ask? Well when we instantiated a new <code class="prettyprint">GregorianCalendar</code> we were missing two key pieces. A TimeZone and a Millisecond. Thus I learned that you need explicitly set the TimeZone <em>and</em> the millisecond of a new <code class="prettyprint">java.util.GregorianCalendar</code> or else it defaults to your current time/timezone.</p>