<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>Let Me Explain Myself&hellip;</title>
 <link href="https://evendanan.net/" rel="self"/>
 <link href="https://evendanan.net"/>
 <updated>2026-09-12T18:05:45+00:00</updated>
 <id>https://evendanan.net</id>
 <author>
   <name>Menny Even Danan</name>
   <email></email>
 </author>

 
 <entry>
   <title>Like this. No, Wait... Maybe Like That? a.k.a. The Domain Translation Unlock</title>
   <link href="https://evendanan.net/ai/art/2026/07/11/the-unlocks-we-have-unlocked"/>
   <updated>2026-07-11T12:00:00+00:00</updated>
   <id>https://evendanan.net/ai/art/2026/07/11/the-unlocks-we-have-unlocked</id>
   <content type="html">&lt;p&gt;I have this idea for a tattoo I’ve been thinking about for quite some time—maybe 3 years. I know what the essence of it is:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;It’s a visual representation of an audio frame&lt;/li&gt;
  &lt;li&gt;From the song &lt;a href=&quot;https://open.spotify.com/track/3k3rbIRXYS7g6RN3jeyjwc?si=dc254891c5d04126&quot;&gt;Telegraph Road&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;A specific guitar sequence&lt;/li&gt;
  &lt;li&gt;Likely black ink&lt;/li&gt;
  &lt;li&gt;It will be smallish, I’d say 1.5–2.5 cm&lt;/li&gt;
  &lt;li&gt;It will peek out from behind one of my ears&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yeah. If you’re reading this description, I bet you’re imagining something. You might think “this is cool” or maybe “meh”. Regardless, I’d bet good money that it’s &lt;em&gt;not&lt;/em&gt; what I’m imagining.&lt;/p&gt;

&lt;p&gt;So, over the years I’ve tried to capture that frame using an &lt;a href=&quot;https://en.wikipedia.org/wiki/Oscilloscope&quot;&gt;o-scope&lt;/a&gt;, a &lt;a href=&quot;https://en.wikipedia.org/wiki/Spectrogram&quot;&gt;spectrogram&lt;/a&gt;, or &lt;a href=&quot;https://en.wikipedia.org/wiki/Tablature&quot;&gt;guitar tabs&lt;/a&gt;. I described this to different people, and even tried to draw it.
The outcome never looked the way I wanted or imagined. But why? I know what I want, I even have a vague mental image of it, and yet I cannot produce it.&lt;/p&gt;

&lt;h2 id=&quot;why-is-that&quot;&gt;Why is that?&lt;/h2&gt;
&lt;p&gt;Well, obviously, I am not an artist, so I can’t draw it. I’m not an audio engineer, so I don’t know how to generate the right spectrogram—or if a spectrogram is even the right visual. I’m not a guitarist, so finding the exact tab is guesswork.
Real blockers. My imagination is locked behind a vocabulary and skillset I don’t have: &lt;strong&gt;those are simply not my domains&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You know what domain I &lt;em&gt;do&lt;/em&gt; inhabit? Software engineering.&lt;/p&gt;

&lt;h2 id=&quot;disclaimer&quot;&gt;Disclaimer&lt;/h2&gt;

&lt;p&gt;This post is not about a novel approach for how to use AI; it’s not even an out-of-the-ordinary use case. Here I am merely pointing to what AI allows us—people—to solve in our day-to-day issues.&lt;/p&gt;

&lt;h2 id=&quot;lets-unlock-this&quot;&gt;Let’s unlock this&lt;/h2&gt;

&lt;p&gt;If I can’t produce a quality drawing, maybe I can PoC this—produce various versions of my idea and see what feels right to me. Once I have a couple of versions I like, I can show them to an actual artist and get their feedback. But at that point, the only person I need to excite is me—hey, I will have it permanently on my skin.&lt;/p&gt;

&lt;p&gt;Basically, I am going to use AI to map from my domain (software engineering) to other domains (audio engineering and sketching).&lt;/p&gt;

&lt;h3 id=&quot;plan&quot;&gt;Plan&lt;/h3&gt;
&lt;p&gt;Here is my plan to generate some options:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Get a digital file (MP3) of the song. Luckily, I own the &lt;a href=&quot;https://en.wikipedia.org/wiki/Compact_disc&quot;&gt;CD&lt;/a&gt; (yes, I linked to Wikipedia’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CD&lt;/code&gt; because I have no idea how young you are), so ripping that is easy.&lt;/li&gt;
  &lt;li&gt;Use AI to understand the technical parts:&lt;/li&gt;
  &lt;li&gt;how to read an audio file into a buffer&lt;/li&gt;
  &lt;li&gt;how to extract audio metadata&lt;/li&gt;
  &lt;li&gt;do I need &lt;a href=&quot;https://en.wikipedia.org/wiki/Fast_Fourier_transform&quot;&gt;FFT&lt;/a&gt;, or maybe raw &lt;a href=&quot;https://en.wikipedia.org/wiki/Pulse-code_modulation&quot;&gt;PCM&lt;/a&gt;?&lt;/li&gt;
  &lt;li&gt;what TypeScript (I arbitrarily pick TypeScript for my work here) library can help me with that?&lt;/li&gt;
  &lt;li&gt;what are my options for audio visualization?&lt;/li&gt;
  &lt;li&gt;Use AI to design (plan) the technical execution of the POC tool:
    &lt;ol&gt;
      &lt;li&gt;UX for pinpointing the data frame&lt;/li&gt;
      &lt;li&gt;UX for switching between different visualizations&lt;/li&gt;
      &lt;li&gt;How to adjust each visualization&lt;/li&gt;
      &lt;li&gt;UX for freezing/exporting a frame&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;Use AI to implement the tool&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;the-unlock&quot;&gt;The Unlock&lt;/h2&gt;

&lt;p&gt;Before the days of AI, searching, planning, and summarizing the different tools and options would take hours or days. Obviously, I also had low confidence that I would &lt;em&gt;know&lt;/em&gt; what to search for—how to translate what I need into a search query.&lt;/p&gt;

&lt;p&gt;And so such an endeavor—yes, I use exactly that word—would have been intimidating: do I have the time? Can I really do this? Is my TypeScript proficiency enough? Do I even know if I explored the right audio visualization options? Did I process the audio correctly? Do I have the focus to go over all the options and research them? Etc.
Frankly, I would postpone this or give up. &lt;em&gt;I would just not do it&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;AI &lt;strong&gt;unlocks my imagination&lt;/strong&gt; by acting as a domain translator—mapping my words to the correct audio-engineering domain and performing a well-focused search.&lt;/p&gt;

&lt;p&gt;Not only does AI speed things up, or serve as the best &lt;a href=&quot;https://darren-broemmer.medium.com/the-search-for-intelligence-why-all-ai-is-really-just-search-e2a826ddef02&quot;&gt;search tool&lt;/a&gt; we’ve ever invented; the tools around AI are structured in a way to &lt;em&gt;keep&lt;/em&gt; context! I can give the Agent something to work on and come back later when I have time to focus. The details will be there: our conversation history, the details it found, the questions I asked, the breakdowns it produced, and the decisions I made. All there.&lt;/p&gt;

&lt;h3 id=&quot;anecdotal-benefit&quot;&gt;Anecdotal benefit&lt;/h3&gt;
&lt;p&gt;A quick side note: normally, manually building something in an unfamiliar domain ends in frustration, or me &lt;a href=&quot;https://www.reddit.com/r/Entrepreneur/comments/1r7epfl/comment/o60ykah/?utm_source=share&amp;amp;utm_medium=web3x&amp;amp;utm_name=web3xcss&amp;amp;utm_term=1&amp;amp;utm_content=share_button&quot;&gt;falling in love with my code&lt;/a&gt; just because I spent hours writing it. AI entirely removes that friction. Because generating and discarding ideas costs almost nothing, I can follow Rule #9 of the &lt;a href=&quot;https://www.panopticoncentral.net/2004/02/10/the-ten-rules-of-performance/&quot;&gt;Ten Rules of Performance&lt;/a&gt;—&lt;em&gt;to write it and rewrite it&lt;/em&gt;. I can quickly cycle through iterations until I find the perfect visual to hand off to a professional, and simply throw the code away when I’m done.&lt;/p&gt;

&lt;h2 id=&quot;did-i-do-it&quot;&gt;Did I do it?&lt;/h2&gt;

&lt;p&gt;So, I told you a story, but have I followed through? Was I unlocked?&lt;/p&gt;

&lt;h3 id=&quot;technical-research&quot;&gt;Technical research&lt;/h3&gt;
&lt;p&gt;Okay, let’s prompt Gemini to help us with that:&lt;/p&gt;
&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Explain how to extract audio frames (or raw audio buffers) from an MP3 file using standard modern web capabilities and tools.
Also, give me a very short technical review of what is the data in the audio buffer.

Complement this by finding a TypeScript library that can do the following audio functions:
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; extract frequency values or amplitudes from an audio frame
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; support various frame frequency representations
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; review the various audio visual representations available and what each shows

Give me a basic flow of using this TS library, and very short code examples (with a lot of comments).

My goal is to experiment with different ways of 2D visualization using web canvas.

I am a seasoned coder, I have a good grasp of TypeScript, but I am not an audio engineer and I know very little about audio formats.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Good. First, Gemini told me it’s a “fantastic project”. That’s nice &lt;em&gt;blushing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Then, Gemini gave me some really good pointers:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We should use &lt;a href=&quot;https://www.w3.org/TR/webaudio-1.1/&quot;&gt;Web Audio API&lt;/a&gt; which can decode MP3, with a simple pipeline:
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Fetch the MP3 binary -&amp;gt; Convert to an ArrayBuffer -&amp;gt; Decode into PCM samples inside an AudioBuffer.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also gave me a code snippet:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-TypeScript&quot;&gt;const audioCtx = new (window.AudioContext || (window as any).webkitAudioContext)();

async function getRawAudioBuffer(mp3Url: string): Promise&amp;lt;AudioBuffer&amp;gt; {
  const response = await fetch(mp3Url);
  const fileArrayBuffer = await response.arrayBuffer();
  
  const decodedBuffer = await audioCtx.decodeAudioData(fileArrayBuffer);
  return decodedBuffer;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Pretty nice. It’s a pure plumbing snippet, just to get me off the ground quickly.&lt;/p&gt;

&lt;p&gt;Gemini was also nice enough to give me a crash course on what &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AudioBuffer&lt;/code&gt; and PCM (Pulse Code Modulation) audio are. Look at that, now I feel less awkward with technical audio jargon. It continues to talk about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rms&lt;/code&gt; (Root Mean Square), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;amplitudeSpectrum&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;powerSpectrum&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chroma&lt;/code&gt;, etc.—giving me novice technical descriptions for audio concepts. These gave names to the vague ideas floating around in my head—translating an aesthetic itch into concrete signal-processing vocabulary.&lt;/p&gt;

&lt;p&gt;It then introduces &lt;a href=&quot;https://www.npmjs.com/package/meyda&quot;&gt;meyda&lt;/a&gt; as the gold-standard for audio feature extraction in JS, and gives me a code example of how to use it.&lt;/p&gt;

&lt;p&gt;This phase was so quick: one prompt, a couple of follow-ups, and my ability to express my requirements in a technical sense has grown. I was unlocked in minutes, whereas a previous (non-AI) iteration would have taken me an evening at the least.&lt;/p&gt;

&lt;h2 id=&quot;to-be-continued&quot;&gt;To be continued&lt;/h2&gt;
&lt;p&gt;That was fun. I can pause now, knowing that all the context — summary, links, follow-ups — is still there in the chat session.
Next up: brainstorming and prototyping an audio visualizer, along with a UI for tweaking the visuals.&lt;/p&gt;

&lt;p&gt;TTYL.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>No! We don't use that here! a.k.a. Making AI Coding NP-Complete</title>
   <link href="https://evendanan.net/ai/llm/software-engineering/2026/01/31/no-we-dont-use-that-here"/>
   <updated>2026-01-31T12:00:00+00:00</updated>
   <id>https://evendanan.net/ai/llm/software-engineering/2026/01/31/no-we-dont-use-that-here</id>
   <content type="html">&lt;p&gt;We’ve all been there. You ask your shiny new AI Agent to “add a user profile endpoint,” and it generates 50 lines of pristine, bug-free, idiomatic Python.&lt;/p&gt;

&lt;p&gt;And you hate it.&lt;/p&gt;

&lt;p&gt;It’s not that the code is &lt;em&gt;wrong&lt;/em&gt;. It compiles. It runs. It passes tests. But it’s… wrong.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“We don’t use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FastAPI&lt;/code&gt; dependency injection like that; we use our own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ServiceContainer&lt;/code&gt;.”&lt;/li&gt;
  &lt;li&gt;“Why is there a raw SQL query? We have a strict &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LegacyQueryFramework&lt;/code&gt; wrapper for that.”&lt;/li&gt;
  &lt;li&gt;“Please don’t introduce a new utility for date formatting; use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimeUtils&lt;/code&gt;.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So you write a comment. Then you edit the prompt. Then you update that massive &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AGENTS.md&lt;/code&gt; file that is rapidly becoming a novel. You are stuck in a loop of teaching the same lessons over and over again.&lt;/p&gt;

&lt;p&gt;LLMs are incredible coders. But they are terrible employees — they forget.&lt;/p&gt;

&lt;p&gt;They don’t know “how things are done around here.” And trying to teach them by shoving more and more context into the prompt is a losing battle.&lt;/p&gt;

&lt;p&gt;I think we’re approaching this the wrong way. We’re trying to solve an NP-Hard problem, when we should be solving an NP-Complete one.&lt;/p&gt;

&lt;h1 id=&quot;the-theory-why-generation-is-np-hard&quot;&gt;The Theory: Why Generation is NP-Hard&lt;/h1&gt;

&lt;p&gt;In computational complexity theory, &lt;a href=&quot;https://en.wikipedia.org/wiki/NP-hardness&quot;&gt;NP-Hard&lt;/a&gt; problems are those where finding a solution is extremely difficult. &lt;a href=&quot;https://en.wikipedia.org/wiki/NP-completeness&quot;&gt;NP-Complete&lt;/a&gt; problems are a special subset where &lt;em&gt;finding&lt;/em&gt; the solution is hard, but &lt;em&gt;verifying&lt;/em&gt; a proposed solution is efficient (polynomial time).&lt;/p&gt;

&lt;p&gt;(Note: I’m using these terms loosely as a metaphor—the analogy isn’t mathematically precise, but the intuition holds.)&lt;/p&gt;

&lt;p&gt;Think about coding in that context.&lt;/p&gt;

&lt;p&gt;Code Generation is NP-Hard.
When you ask an Agent to “write code that fits our organization’s standards,” you are asking it to search an infinite space of possible syntaxes, libraries, and architectural patterns to find the &lt;em&gt;one&lt;/em&gt; specific combination that matches your organization’s unwritten rules. To do this successfully, the Agent needs perfect knowledge of your entire codebase, your history, and your personal preferences. That is a massive search problem.
Not only that, but the Agent may find many different solutions that are all correct given the current context, but none of them are “good enough” - they all fail to meet the organization’s actual, ever-evolving standards.&lt;/p&gt;

&lt;p&gt;Code Verification is P (Polynomial Time).
But if I show you a piece of code, you can instantly say: “No, don’t use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GraphQL&lt;/code&gt; here, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LegacyQueryFramework&lt;/code&gt;.” 
Verifying if a solution fits expectations is cheap. You don’t need to know how to write the perfect code to know that the current code is wrong.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Author’s Note: I’m simplifying here. Verifying deep logical correctness or security properties is still incredibly hard (often undecidable). But verifying “Style” and “Local Constraints”—which is what most code review nitpicks are about—is almost always computationally cheap. You scan, you spot the pattern, you flag it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The essence of an NP-Complete problem is that finding a solution is hard, but verifying a solution is easy. Coding is NP-Complete.&lt;/p&gt;

&lt;p&gt;Right now, we are asking our Agents to be perfect Generators. We are overloading them with context hoping they can “solve” the Style Problem in one shot.&lt;/p&gt;

&lt;p&gt;We should stop doing that. We should assume the Generation will be imperfect, and focus on building a really fast, really smart Verifier.&lt;/p&gt;

&lt;h1 id=&quot;enter-the-reviewer-model&quot;&gt;Enter The “Reviewer” Model&lt;/h1&gt;

&lt;p&gt;Imagine a new architecture for AI coding. Instead of one massive “Do-It-All” model, we have two:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The Writer: A capable, general-purpose frontier model. It knows Python, it knows logic, it knows how to solve problems. It doesn’t know your specific internal style quirks, and &lt;em&gt;that’s okay&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;The Reviewer: A small, specialized model (e.g., a fine-tuned Gemma 2B or 7B). It might not be smart enough to write complex logic, but it has been fine-tuned on one thing: Your Code Reviews.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The workflow changes from “Prompt -&amp;gt; Code” to a loop:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Writer generates a plan/code.&lt;/li&gt;
  &lt;li&gt;Reviewer looks at it. It doesn’t check for bugs, code syntax, format, etc. (we have other tools for that). It checks for &lt;em&gt;Taste&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;Reviewer says: “This looks 3/10. Re-direct: We prefer to chain JavaScript promises here.”&lt;/li&gt;
  &lt;li&gt;Writer updates the code.&lt;/li&gt;
  &lt;li&gt;Reviewer says: “8/10. Good enough.”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Reviewer doesn’t need to know the right answer. It just needs to know that the current answer is wrong, and point the Writer in the right direction. We are effectively reducing the problem complexity: The Writer provides the raw intelligence; the Reviewer provides the constraints.&lt;/p&gt;

&lt;h1 id=&quot;the-secret-sauce-living-memory&quot;&gt;The “Secret Sauce”: Living Memory&lt;/h1&gt;

&lt;p&gt;“But wait,” you ask, “Don’t I still have to train this Reviewer? Isn’t that just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AGENTS.md&lt;/code&gt; with extra steps?”&lt;/p&gt;

&lt;p&gt;No. Because you shouldn’t be writing rules. You should just be doing your job.&lt;/p&gt;

&lt;p&gt;We can harvest the training data for this Reviewer automatically from the “Exhaust” of your engineering org.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data Mining the “Taste”:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The “Squash &amp;amp; Merge” Delta: When an Agent opens a PR, and a developer eventually merges it, the &lt;em&gt;difference&lt;/em&gt; between the Agent’s original code and the final merged code is pure gold. It is a labeled dataset of “What the Model wanted” vs. “What the Org wanted.”&lt;/li&gt;
  &lt;li&gt;The Comment Thread: Every time a senior engineer comments “Please use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logger&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print&lt;/code&gt;,” that is a negative training example for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print&lt;/code&gt; and a positive signal for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logger&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;IDE Shadowing: In a “Copilot” setting, if the AI suggests code and the user immediately backspaces and retypes it, that is a strong negative signal.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Direct Feedback:&lt;/strong&gt; When you explicitly correct the Agent in chat, that is high-quality data. We could even add simple reaction emojis (👍/👎) to every Agent reply to gather “Taste” signals with zero friction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can feed this stream of stylistic preferences into a small model. This creates a Living Memory of the organization’s taste. It evolves as you evolve. If you stop using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LegacyQueryFramework&lt;/code&gt; and switch to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GraphQL&lt;/code&gt;, the training data shifts, the Reviewer updates, and the Agents follow suit—without anyone updating a markdown file.&lt;/p&gt;

&lt;p&gt;Of course, this isn’t magic. “Garbage In, Garbage Out” still applies. You can’t just feed raw git logs into a model; you need to filter for “clean” commits, successful merges, and ignore the chaotic “fix typo” churn. But automating &lt;em&gt;data curation&lt;/em&gt; is still infinitely more scalable than manually writing rules.&lt;/p&gt;

&lt;h1 id=&quot;the-logistics-can-we-actually-do-this&quot;&gt;The Logistics: Can We Actually Do This?&lt;/h1&gt;

&lt;p&gt;“Fine-tuning a model” sounds expensive and scary. It used to be. Today, it’s a weekend project.&lt;/p&gt;

&lt;p&gt;Here is the napkin math for building your organization’s reviewer:&lt;/p&gt;

&lt;h2 id=&quot;1-the-data&quot;&gt;1. The Data&lt;/h2&gt;
&lt;p&gt;You don’t need “Big Data.” For a specialized classifier like this (Binary: “Good/Bad” or Classification: “Pass/Refactor”), you can see results with as few as &lt;strong&gt;1,000 - 5,000 examples&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Source:&lt;/em&gt; Your existing PR history.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Labeling:&lt;/em&gt; Automated scripts (e.g., “accepted without comments” vs “commented on”).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;2-the-model&quot;&gt;2. The Model&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;(as of January 2026—this list will likely be outdated soon)&lt;/em&gt;
You don’t need GPT-4. You need something small, fast, and verify-capable.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.google/technology/developers/gemma-open-models/&quot;&gt;&lt;strong&gt;Gemma 2B / 7B&lt;/strong&gt;&lt;/a&gt;: Google’s open models are excellent for this. The 2B version is tiny enough to run on almost any modern laptop.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://azure.microsoft.com/en-us/blog/introducing-phi-3-redefining-what-s-possible-with-slms/&quot;&gt;&lt;strong&gt;Phi-3-mini&lt;/strong&gt;&lt;/a&gt;: Microsoft’s 3.8B model packs a punch way above its weight class.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://llama.meta.com/llama3/&quot;&gt;&lt;strong&gt;Llama 3 8B&lt;/strong&gt;&lt;/a&gt;: A bit heavier, but widely supported and very capable as a generalized “Judge”.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;3-the-method&quot;&gt;3. The Method&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;(Disclaimer: I am not an expert in fine-tuning, so take this as a high-level overview)&lt;/em&gt;
You won’t retrain the whole model. You will use &lt;strong&gt;PEFT&lt;/strong&gt; (Parameter-Efficient Fine-Tuning) techniques:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2106.09685&quot;&gt;&lt;strong&gt;LoRA&lt;/strong&gt;&lt;/a&gt; (Low-Rank Adaptation): Freezes the big model and trains tiny “adapter” layers.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2305.14314&quot;&gt;&lt;strong&gt;QLoRA&lt;/strong&gt;&lt;/a&gt;: Does the above but with 4-bit quantization, allowing you to train on a single consumer GPU (like an NVIDIA T4 or RTX 4090).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;4-the-cost&quot;&gt;4. The Cost&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Training:&lt;/strong&gt; On a cloud GPU (e.g., &lt;a href=&quot;https://cloud.google.com/compute/gpus-pricing&quot;&gt;T4 on Google Colab&lt;/a&gt; or AWS), a fine-tuning run for a small dataset takes &amp;lt;1 hour. Estimated cost: &lt;strong&gt;&amp;lt;$5&lt;/strong&gt;. That’s cheap enough to do re-training every week.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Serving:&lt;/strong&gt; Serving a small model like Gemma or Llama 3B on a cloud provider costs roughly &lt;strong&gt;$0.02–$0.10 per 1M tokens&lt;/strong&gt; (based on &lt;a href=&quot;https://www.together.ai/pricing&quot;&gt;Together AI pricing&lt;/a&gt;). Compared to GPT-4o input costs (~$2.50/1M tokens), it’s negligible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is cheaper to run a custom-trained 2B parameter verification model than to jam 50k tokens of “Rules” into every GPT-4 prompt.&lt;/p&gt;

&lt;h1 id=&quot;the-knowledge-trap-why-your-reviewer-doesnt-need-a-library-card&quot;&gt;The Knowledge Trap: Why Your Reviewer Doesn’t Need a Library Card&lt;/h1&gt;

&lt;p&gt;The primary critique of fine-tuning—that it is a “leaky” and &lt;a href=&quot;https://arxiv.org/abs/2312.05934&quot;&gt;inefficient way to inject factual knowledge compared to RAG&lt;/a&gt;—actually reinforces the &lt;strong&gt;Reviewer/Writer&lt;/strong&gt; split. The theory remains robust because it shifts the objective of fine-tuning from &lt;strong&gt;Data Storage&lt;/strong&gt; to &lt;strong&gt;Behavioral Alignment.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;1-taste-is-a-pattern-not-a-fact&quot;&gt;1. Taste is a Pattern, Not a Fact&lt;/h2&gt;

&lt;p&gt;Fine-tuning is &lt;a href=&quot;https://arxiv.org/abs/2405.05904&quot;&gt;notoriously poor at “memorizing” specific, isolated facts&lt;/a&gt; (like a new API endpoint name), but it is exceptionally good at &lt;strong&gt;Domain Adaptation.&lt;/strong&gt; “Taste” (organizational style, architectural preferences, idiomatic patterns) is a statistical distribution, not a lookup table. The Reviewer doesn’t need to be an encyclopedia; it needs to be a &lt;strong&gt;Filter&lt;/strong&gt; that recognizes the “shape” of acceptable code.&lt;/p&gt;

&lt;h2 id=&quot;2-training-on-the-direction-not-the-state&quot;&gt;2. Training on the “Direction,” Not the “State”&lt;/h2&gt;

&lt;p&gt;While RAG pulls from the current state of a codebase (which is often messy or full of legacy “noise”), the Reviewer is fine-tuned on the &lt;strong&gt;Diffs&lt;/strong&gt;—the “Exhaust” of the PR process.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;This allows the Reviewer to learn the &lt;strong&gt;vector of improvement&lt;/strong&gt; (e.g., &lt;em&gt;“We are moving away from X and toward Y”&lt;/em&gt;).&lt;/li&gt;
  &lt;li&gt;Because it learns the &lt;em&gt;correction&lt;/em&gt; rather than the &lt;em&gt;static fact&lt;/em&gt;, the inherent “lossiness” of fine-tuning becomes an advantage: it ignores the noise of the codebase and focuses on the signal of the organization’s evolution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;3-decoupling-the-brain-from-the-conscience&quot;&gt;3. Decoupling “The Brain” from “The Conscience”&lt;/h2&gt;

&lt;p&gt;The Fine-Tuning Knowledge Gap only breaks a model if that model is expected to be the sole source of truth. In the &lt;strong&gt;Reviewer Architecture&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;The Writer (Frontier Model)&lt;/strong&gt; uses its massive scale and RAG to handle the &lt;strong&gt;NP-Hard Generation&lt;/strong&gt; and factual knowledge.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The Reviewer (Small Fine-Tuned Model)&lt;/strong&gt; handles the &lt;strong&gt;Verification.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By separating these concerns, the fact that a 2B parameter model isn’t a great “database” doesn’t matter—it only needs to be a great &lt;strong&gt;judge.&lt;/strong&gt;&lt;/p&gt;

&lt;h1 id=&quot;prior-art-its-not-just-theory&quot;&gt;Prior Art: It’s Not Just Theory&lt;/h1&gt;

&lt;p&gt;This isn’t just a fun thought experiment. The biggest players in AI and Tech are already validating pieces of this puzzle.&lt;/p&gt;

&lt;h2 id=&quot;1-the-reviewer-architecture-works-google--microsoft&quot;&gt;1. The “Reviewer” Architecture Works (Google &amp;amp; Microsoft)&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Google&lt;/strong&gt; deployed a model trained purely on &lt;a href=&quot;https://research.google/blog/resolving-code-review-comments-with-ml/&quot;&gt;resolving code review comments&lt;/a&gt;. It focuses on the “cleanup” phase—predicting the edit needed to satisfy a human reviewer—and achieved a 50%+ acceptance rate.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Microsoft&lt;/strong&gt; &lt;a href=&quot;https://arxiv.org/abs/2203.09095&quot;&gt;released CodeReviewer&lt;/a&gt;, a model pre-trained on the &lt;em&gt;interaction&lt;/em&gt; of code reviews (Code Change → Review Comment). It proved that a model trained on diffs and comments beats a generic large model at spotting errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;2-the-data-strategy-works-meta--lima&quot;&gt;2. The Data Strategy Works (Meta &amp;amp; LIMA)&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Meta&lt;/strong&gt; fine-tuned Llama on 64,000 internal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;Review Comment, Patch&amp;gt;&lt;/code&gt; pairs (&lt;a href=&quot;https://arxiv.org/abs/2507.13499&quot;&gt;MetaMateCR&lt;/a&gt;). Their fine-tuned model outperformed GPT-4o by 9 percentage points—proving that &lt;em&gt;your own data&lt;/em&gt; beats a bigger generic model.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;LIMA&lt;/strong&gt; (&lt;a href=&quot;https://arxiv.org/abs/2305.11206&quot;&gt;arXiv:2305.11206&lt;/a&gt;) showed that even 1,000 carefully curated examples can produce competitive results, reinforcing that you don’t need “Big Data”—you need &lt;em&gt;clean&lt;/em&gt; data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;3-the-future-is-reward-engineering-deepseek&quot;&gt;3. The Future is Reward Engineering (DeepSeek)&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;DeepSeek-R1&lt;/strong&gt; (released Jan 2025) pioneered using large-scale Reinforcement Learning to force a model to “pause and verify” its own output (&lt;a href=&quot;https://github.com/deepseek-ai/DeepSeek-R1&quot;&gt;Technical Report&lt;/a&gt;). This is the logical endpoint of our Reviewer: training the “Taste” directly into the model’s reward function.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;a-new-kind-of-feedback-loop&quot;&gt;A New Kind of Feedback Loop&lt;/h1&gt;

&lt;p&gt;We are currently stuck in a local maximum of “Prompt Engineering”—trying to craft the perfect set of instructions to get the perfect result.&lt;/p&gt;

&lt;p&gt;We need to move to &lt;strong&gt;Reward Engineering&lt;/strong&gt;. We need to accept that “Taste” is hard to describe but easy to recognize. By building specialized models that act as automated Code Reviewers, we can finally scale the most unscalable part of software engineering: our culture.&lt;/p&gt;

&lt;p&gt;We don’t need generic speed; we need specific taste. It’s time to clone our team’s DNA.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Faster Builds Without Owning Code a.k.a. I Don't Know Rust, But AI Does</title>
   <link href="https://evendanan.net/ai/build-system/rust/2026/01/18/i-dont-know-rust-but-ai-does"/>
   <updated>2026-01-18T12:00:00+00:00</updated>
   <id>https://evendanan.net/ai/build-system/rust/2026/01/18/i-dont-know-rust-but-ai-does</id>
   <content type="html">&lt;p&gt;We software engineers are creatures of habit. We have our comfortable pair of slippers: the languages we know by heart. For me, and for many of my colleagues, those languages are TypeScript, Python, Java, etc.&lt;/p&gt;

&lt;p&gt;We use these languages for everything. Not just for the app code, but for the glue that holds our build system together. In our Bazel setup, we have dozens of little helper tools: generators, linters, formatters, and transpilers.&lt;/p&gt;

&lt;p&gt;But comfort comes with a tax.&lt;/p&gt;

&lt;h1 id=&quot;the-nodejs-tax&quot;&gt;The “Node.js Tax”&lt;/h1&gt;

&lt;p&gt;Let’s look at a specific example: generating &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.d.ts&lt;/code&gt; Declaration files for our TypeScript packages.&lt;/p&gt;

&lt;p&gt;We have a custom tool for this. It’s written in TypeScript, runs on Node.js, and uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;swc&lt;/code&gt; under the hood. It’s a great tool. It’s readable, easy to modify, and by human standards, it’s fast. It takes about &lt;strong&gt;750ms&lt;/strong&gt; to process an average-sized package.&lt;/p&gt;

&lt;p&gt;750ms. That sounds fine, right?&lt;/p&gt;

&lt;p&gt;But we have tens of thousands of packages. And in a build system like Bazel, those milliseconds add up. Every time we spin up a Node.js process, we pay a startup penalty. We pay for the single-threaded nature of the event loop - even if we use async I/O operations.&lt;/p&gt;

&lt;p&gt;To make this &lt;em&gt;truly&lt;/em&gt; fast—we’re talking single-digit milliseconds in most cases and ~100ms on the big ones—we would need to move to a native language. Something like Rust. We could use specific crates (like &lt;a href=&quot;https://github.com/oxc-project/oxc&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oxc&lt;/code&gt;&lt;/a&gt;, the Oxidation Compiler) directly as libraries, bypassing the process-spawning overhead and utilizing true multi-threading.&lt;/p&gt;

&lt;p&gt;But here is the friction: &lt;strong&gt;I don’t know Rust.&lt;/strong&gt; And none of my teammates know Rust.&lt;/p&gt;

&lt;p&gt;At least, not well enough to write a production-grade, highly optimized build tool from scratch. And honestly? I don’t want to learn it just for this one 50-line script. So, we stuck with the slow, comfortable Node.js tool.&lt;/p&gt;

&lt;p&gt;Well… until now.&lt;/p&gt;

&lt;h1 id=&quot;from-coder-to-agent-manager&quot;&gt;From “Coder” to “Agent Manager”&lt;/h1&gt;

&lt;p&gt;We need to fundamentally shift how we view our role in the codebase.&lt;/p&gt;

&lt;p&gt;For decades, we have been the &lt;em&gt;authors&lt;/em&gt; of every line of code. If we didn’t write it, we didn’t own it. If we didn’t understand the syntax, we couldn’t maintain it.&lt;/p&gt;

&lt;p&gt;This implementation constraint is now gone. We should evolve into &lt;strong&gt;AI-Agent Managers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We don’t need to know exactly how every non-critical, non-proprietary piece of code is implemented.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;AI is excellent at parsing code.&lt;/strong&gt; It can explain what a snippet does better than most original authors.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;AI is excellent at pattern matching.&lt;/strong&gt; It can look at an existing tool and say, “I will write a Rust version that behaves exactly like this.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The “secret sauce” of our company isn’t in how we invoke a TypeScript compiler. That’s generic code. It doesn’t need to be “ours.” It just needs to work.&lt;/p&gt;

&lt;h2 id=&quot;letting-ai-write-the-rust&quot;&gt;Letting AI Write the Rust&lt;/h2&gt;

&lt;p&gt;So, I tried an experiment. I took our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dts-emit&lt;/code&gt; tool and I gave it to an AI Coding Agent.&lt;/p&gt;

&lt;p&gt;I didn’t write a single line of Rust. I didn’t worry about the borrow checker. I simply described the &lt;em&gt;behavior&lt;/em&gt; I wanted and the constraints (must use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oxc&lt;/code&gt;, must be a CLI, must match the arguments of the old tool).&lt;/p&gt;

&lt;p&gt;The result? &lt;a href=&quot;https://github.com/menny/rust_oxc_dts_emit&quot;&gt;&lt;strong&gt;rust_oxc_dts_emit&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Development Time to POC&lt;/strong&gt;: ~30 minutes*.
    &lt;ul&gt;
      &lt;li&gt;&lt;em&gt;Caveat: I already had a full, battle-tested TypeScript implementation to reference, some Rust examples in the codebase, and lots of real-world test cases.&lt;/em&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;My Rust Knowledge Required&lt;/strong&gt;: Zero.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: The new tool runs on average in &lt;strong&gt;around 200ms&lt;/strong&gt;. Moreover, it runs in single-digit milliseconds on single-file packages!&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Additional time to productize and document&lt;/strong&gt;: A couple of hours.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is a &lt;strong&gt;~3.5x speedup&lt;/strong&gt; on average!&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Before (Node.js + swc) - 200 typescript files&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;node dts-emit src/
&lt;span class=&quot;c&quot;&gt;# 780ms&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# After (Rust-based + oxc) - same 200 typescript files&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;rust_oxc_dts_emit src/
&lt;span class=&quot;c&quot;&gt;# 215ms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By moving from a “spawning a Node process” model to a “native binary using a library” model, we obliterated the overhead. And because the AI handled the implementation, the “cost” of using a language I don’t know was effectively zero.&lt;/p&gt;

&lt;h2 id=&quot;identifying-candidates-its-not-just-about-rust&quot;&gt;Identifying Candidates: It’s Not Just About Rust&lt;/h2&gt;

&lt;p&gt;While Rust was the right choice for this specific task due to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oxc&lt;/code&gt; library, the principle applies to any language that offers better performance or safety for the job at hand. You might choose Go for a network service, or a pre-compiled Python binary for a complex data task.&lt;/p&gt;

&lt;p&gt;How do you spot a candidate for an AI-rewrite?&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;The “Glue” Script&lt;/strong&gt;: Look for scripts that primarily move data between other tools. If they are just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subprocess.call&lt;/code&gt; wrappers, they can be rewritten in a faster, safer language.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The “Slow but Simple”&lt;/strong&gt;: Tools that take noticeable time (100ms+) but do something logically simple (like generating files, parsing JSON, or checking file existence).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The “Black Box”&lt;/strong&gt;: Legacy scripts that everyone is afraid to touch. Since you don’t understand them anyway, having an AI rewrite them in a modern, strictly-typed language often &lt;em&gt;increases&lt;/em&gt; maintainability because the AI will add types and structure that the original lacked.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;No Secret Sauce&lt;/strong&gt;: Code that relies purely on public logic or open-source libraries. If it contains complex, proprietary business logic, you might want to keep it in a language you can debug manually.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;maintenance-the-context-window&quot;&gt;Maintenance: The “Context Window”&lt;/h2&gt;

&lt;p&gt;The immediate question I get is: &lt;em&gt;“But what happens when it breaks? You don’t know Rust!”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is where the Agent Manager mindset kicks in.&lt;/p&gt;

&lt;p&gt;When it breaks, I don’t open the code and try to debug memory leaks. I copy the error message, paste it to my Agent, and say: &lt;em&gt;“Fix this.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But for this to work reliably, we need to change how we document.&lt;/p&gt;

&lt;p&gt;In this new world, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;README.md&lt;/code&gt; is not just for humans. It is the &lt;strong&gt;Context Window&lt;/strong&gt; for the next Agent. We &lt;strong&gt;MUST&lt;/strong&gt; have the AI document the tool meticulously:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Usage instructions.&lt;/li&gt;
  &lt;li&gt;Design decisions (why &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oxc&lt;/code&gt;? why this crate?).&lt;/li&gt;
  &lt;li&gt;Known edge cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This documentation ensures that when the next Agent picks up the task 6 months from now, it has the full context needed to maintain the code, even if the human “manager” has forgotten everything about it.&lt;/p&gt;

&lt;h2 id=&quot;why-this-matters-the-scale-of-trust&quot;&gt;Why This Matters: The Scale of Trust&lt;/h2&gt;

&lt;p&gt;The implications go beyond just saving a few milliseconds on a build script.&lt;/p&gt;

&lt;p&gt;Right now, we are comfortable delegating these small, isolated scripts. They are easy to verify: input &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;in.json&lt;/code&gt;, output &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;out.rs&lt;/code&gt;. We can treat them as black boxes because the scope of failure is limited to a single build step.&lt;/p&gt;

&lt;p&gt;But this is just the training ground.&lt;/p&gt;

&lt;p&gt;As our confidence in AI Agents grows and their capabilities improve, the definition of “manageable code” will expand. Today it’s a 50-line build tool. Tomorrow it might be a non-critical microservice, or a complex validation library.&lt;/p&gt;

&lt;p&gt;We are training ourselves to be Managers on the small stuff so we are ready for the big stuff. We are learning how to specify behavior rather than implementation, how to verify outcomes rather than syntax, and how to maintain systems we didn’t build.&lt;/p&gt;

&lt;h1 id=&quot;side-note-this-has-happened-before---kinda&quot;&gt;Side Note: This Has Happened Before - kinda&lt;/h1&gt;

&lt;p&gt;This transition might feel unnatural to us software developers. We pride ourselves on knowing exactly how our tools and systems work. But giving up intimate knowledge of implementation details is exactly what happens in every mature engineering discipline as complexity scales. A few examples:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Hardware Engineering (&lt;a href=&quot;https://en.wikipedia.org/wiki/High-level_synthesis&quot;&gt;High-Level Synthesis&lt;/a&gt;):&lt;/strong&gt; Engineers write behavioral code, and the tool synthesizes the cycle-accurate Register-Transfer Level (RTL). The engineer owns the &lt;em&gt;behavior&lt;/em&gt;; the tool owns the &lt;em&gt;implementation&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Mechanical Engineering (&lt;a href=&quot;https://en.wikipedia.org/wiki/Generative_design&quot;&gt;Generative Design&lt;/a&gt;):&lt;/strong&gt; Engineers define the physical constraints (materials, load-bearing points, boundaries), and the software iterates to find the optimal structure. The engineer owns the &lt;em&gt;requirements&lt;/em&gt;; the software &lt;em&gt;owns&lt;/em&gt; the &lt;em&gt;how&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While these tools strictly handle implementation mechanics (with no decision-making), the underlying shift is similar: intimate knowledge of the low-level code is not that important.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;We have unlocked a new category of software in our build pipeline: &lt;strong&gt;High-Performance, Low-Maintenance, AI-Owned Utilities.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We no longer have to choose between “fast” and “readable.” We can have both. We just have to be willing to let go of the idea that we need to write the code ourselves.&lt;/p&gt;

&lt;p&gt;So, look at your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt; scripts. Look at those slow Python glue scripts. What are you keeping around just because it’s comfortable?&lt;/p&gt;

&lt;p&gt;It’s time to evolve. Fire your inner &lt;em&gt;Node.js expert&lt;/em&gt; and hire an AI Agent manager. Start with the small tools, build your confidence, and watch your build times drop. The future of development isn’t just about writing code—it’s about knowing which code you shouldn’t be writing at all.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>CI/CD at AnySoftKeyboard Part 2 aka Ship It!</title>
   <link href="https://evendanan.net/anysoftkeyboard/cd/2025/11/09/ci-cd-ask-part2"/>
   <updated>2025-11-09T12:00:00+00:00</updated>
   <id>https://evendanan.net/anysoftkeyboard/cd/2025/11/09/ci-cd-ask-part2</id>
   <content type="html">&lt;p&gt;In &lt;a href=&quot;/2020/03/30/ci-cd-ask-part1.html&quot;&gt;Part 1 of this series&lt;/a&gt;, we discussed how Continuous Integration (CI) at AnySoftKeyboard ensures that our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt; branch is always in a healthy, shippable state. Through rigorous testing, static analysis, and code style checks, we gain the confidence that our code is robust and ready for prime time.&lt;/p&gt;

&lt;p&gt;But what good is a perfectly integrated codebase if it never reaches our users? This is where &lt;a href=&quot;https://en.wikipedia.org/wiki/Continuous_deployment&quot;&gt;Continuous Deployment&lt;/a&gt; (CD) comes into play. In this second part, we’ll dive into how AnySoftKeyboard automates the process of getting our application from our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt; and release branches directly into the hands of our users.&lt;/p&gt;

&lt;h1 id=&quot;why-continuous-deployment-the-need-for-speed-and-feedback&quot;&gt;Why Continuous Deployment? The Need for Speed and Feedback&lt;/h1&gt;

&lt;p&gt;The primary goal of Continuous Deployment is to make the release process routine, reliable, and, most importantly, fast. Instead of infrequent, high-stress “release days,” CD allows us to deliver value to users continuously.&lt;/p&gt;

&lt;p&gt;For AnySoftKeyboard, this means:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Faster Feedback from Alpha and Beta Users&lt;/strong&gt;: By deploying frequently to our Alpha and Beta channels, we get new features and bug fixes into the hands of our most engaged users almost immediately. This rapid feedback loop is invaluable for validating new functionality, identifying regressions, and ensuring a high-quality user experience before a wider release.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Quick Delivery of Features and Fixes&lt;/strong&gt;: When a critical bug is found or a highly anticipated feature is ready, CD enables us to push it out to users with minimal delay, maximizing impact and user satisfaction.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Reduced Risk&lt;/strong&gt;: Each deployment is a small, incremental change, making it easier to identify and roll back issues if they arise, compared to large, infrequent releases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;our-deployment-triggers-different-cadences-for-different-audiences&quot;&gt;Our Deployment Triggers: Different Cadences for Different Audiences&lt;/h1&gt;

&lt;p&gt;AnySoftKeyboard employs a multi-channel deployment strategy, each with its own trigger and audience, allowing us to balance rapid iteration with stability.&lt;/p&gt;

&lt;h3 id=&quot;alpha-channel-the-cutting-edge&quot;&gt;Alpha Channel (The Cutting Edge)&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Metric&lt;/strong&gt;: Deployment to Alpha takes approximately &lt;strong&gt;15 minutes&lt;/strong&gt; from merge to Play Store.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Pointer&lt;/strong&gt;: The Alpha deployment is kicked off by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;checks.yml&lt;/code&gt; workflow, which runs on every push to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt; branch. After all checks pass, it triggers a deployment request.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Snippet&lt;/strong&gt;: This process is defined in &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/blob/604834d25345cccb033e815f93234625bce64d2e/.github/workflows/checks.yml&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.github/workflows/checks.yml&lt;/code&gt;&lt;/a&gt;. The workflow triggers on a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt;, and if all checks pass, the final &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deploy-request&lt;/code&gt; job is executed to initiate the Alpha deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;beta-channel-the-weekly-digest&quot;&gt;Beta Channel (The Weekly Digest)&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Pointer&lt;/strong&gt;: The Beta promotion is handled by a scheduled workflow that runs daily, but the promotion itself is gated to only happen on Wednesdays.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Snippet&lt;/strong&gt;: The schedule is defined in &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/blob/604834d25345cccb033e815f93234625bce64d2e/.github/workflows/deployment_promote.yml&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.github/workflows/deployment_promote.yml&lt;/code&gt;&lt;/a&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# In .github/workflows/deployment_promote.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;cron&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;04&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;04&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*&apos;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Runs daily at 04:04 UTC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This workflow triggers our custom deployment tool, which contains the gating logic in &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/blob/604834d25345cccb033e815f93234625bce64d2e/js/github_deployments/deployment_config.ts&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;js/github_deployments/deployment_config.ts&lt;/code&gt;&lt;/a&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;promoteOnWednesday&lt;/code&gt; function ensures the promotion to the Beta channel only proceeds when the job runs on a Wednesday.&lt;/p&gt;

&lt;h3 id=&quot;stable-channel-the-official-release&quot;&gt;Stable Channel (The Official Release)&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Metric&lt;/strong&gt;: Our stable rollout completes over &lt;strong&gt;5 days&lt;/strong&gt;: 10% → 20% → 50% → 100%.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Pointer&lt;/strong&gt;: The Stable release process is triggered not by a Git tag, but by a push to a specifically named &lt;em&gt;branch&lt;/em&gt;, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;release-branch-ime-v1.12-r1&lt;/code&gt;. This kicks off a time-based, gradual rollout.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Snippet&lt;/strong&gt;: The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;checks.yml&lt;/code&gt; workflow is also configured to run on pushes to release branches, as seen in &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/blob/604834d25345cccb033e815f93234625bce64d2e/.github/workflows/checks.yml&quot;&gt;the same file&lt;/a&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# In .github/workflows/checks.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;release-branch-*-v*.*-r*&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Our deployment script then uses a regular expression (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/^release-branch-ime-.*$/&lt;/code&gt;) in &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/blob/604834d25345cccb033e815f93234625bce64d2e/js/github_deployments/deployment_config.ts&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;js/github_deployments/deployment_config.ts&lt;/code&gt;&lt;/a&gt; to identify this as a Production release, which begins the automated, multi-day staged rollout.&lt;/p&gt;

&lt;h1 id=&quot;the-brains-of-the-operation-our-github_deployments-tool&quot;&gt;The Brains of the Operation: Our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;github_deployments&lt;/code&gt; Tool&lt;/h1&gt;

&lt;p&gt;While GitHub Actions define the “when” of our deployments, the “what” and “how” are orchestrated by a custom, in-house TypeScript tool located at &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/tree/604834d25345cccb033e815f93234625bce64d2e/js/github_deployments&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;js/github_deployments&lt;/code&gt;&lt;/a&gt;. This command-line tool, executed via Bazel, acts as the central brain for our entire CD process.&lt;/p&gt;

&lt;p&gt;Its primary responsibilities are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Interpreting Triggers&lt;/strong&gt;: It consumes the branch name (e.g., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;release-branch-...&lt;/code&gt;) and determines which deployment configuration to use (Alpha, Beta, or Production).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Calculating Rollout Steps&lt;/strong&gt;: For our time-based rollouts, it calculates the current day of the release and selects the correct rollout percentage from a predefined array.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Interacting with the GitHub API&lt;/strong&gt;: It creates the official &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deployment&lt;/code&gt; events using the GitHub API, which in turn trigger the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deploy.yml&lt;/code&gt; workflow. It’s also used to report the status back to GitHub.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, when the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;checks.yml&lt;/code&gt; workflow runs on a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt; branch push, it executes the following command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# This command is wrapped in our .github/actions/deploy-request action&lt;/span&gt;
bazel run //js/github_deployments &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; deploy &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--refname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;main&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--deployMode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;force_new&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;# ... other parameters&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This command tells our tool to create a new “Alpha” deployment. This layer of abstraction allows us to keep our deployment logic in well-tested TypeScript code, rather than in complex and hard-to-maintain YAML files.&lt;/p&gt;

&lt;h1 id=&quot;executing-the-release-the-magic-of-automation&quot;&gt;Executing the Release: The Magic of Automation&lt;/h1&gt;

&lt;p&gt;Regardless of the trigger, the actual process of building, signing, and publishing the application is entirely automated. Once a deployment is triggered, a dedicated GitHub Actions workflow takes over.&lt;/p&gt;

&lt;p&gt;This workflow executes a series of scripts that perform all the necessary steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Building the Application&lt;/strong&gt;: The latest code is compiled and packaged into an Android App Bundle (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.aab&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Secure Signing&lt;/strong&gt;: Signing keys and API credentials, stored as encrypted GitHub Secrets, are injected at build time. The App Bundle is then cryptographically signed using these credentials to ensure its authenticity and integrity.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Publishing to Google Play&lt;/strong&gt;: The signed App Bundle is uploaded to the Google Play Store. This entire process is handled via programmatic API calls to the Google Play publishing APIs, powered by the &lt;a href=&quot;https://github.com/Triple-T/gradle-play-publisher&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gradle-play-publisher&lt;/code&gt;&lt;/a&gt; plugin. This eliminates manual uploads, reduces human error, and ensures a consistent, reliable deployment every time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key here is that no human intervention is required for these steps. The automation handles everything, from compiling the code to interacting with the Play Store’s backend.&lt;/p&gt;

&lt;h1 id=&quot;closing-the-loop-visibility-and-supporting-tools&quot;&gt;Closing the Loop: Visibility and Supporting Tools&lt;/h1&gt;

&lt;p&gt;An automated deployment process is only effective if it provides clear visibility into its status. For AnySoftKeyboard, various tools and mechanisms are in place to provide this feedback directly within our development workflow.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Deployment Status Updates&lt;/strong&gt;: We use our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;github_deployments&lt;/code&gt; tool to communicate with the GitHub Deployments API. The same tool that initiates a deployment is also called at various stages within the main &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deploy.yml&lt;/code&gt; workflow to report back the status. For example, as soon as a deployment begins, we run the following command to set the status to “in progress”:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# In .github/workflows/deploy.yml&lt;/span&gt;
bazel run //js/github_deployments &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; status &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--deployment-id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;$&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;in_progress
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This immediately updates the deployment status in the GitHub UI, providing real-time feedback.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Automated GitHub Releases&lt;/strong&gt;: After a successful deployment to the Google Play Store, a dedicated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;post_deployment&lt;/code&gt; job in our &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/blob/604834d25345cccb033e815f93234625bce64d2e/.github/workflows/deploy.yml&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deploy.yml&lt;/code&gt; workflow&lt;/a&gt; handles the creation of a public-facing GitHub Release. Interestingly, this is a two-step process. When the stable rollout first begins (at 10%), we use the popular &lt;a href=&quot;https://github.com/softprops/action-gh-release&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;softprops/action-gh-release&lt;/code&gt;&lt;/a&gt; action to create a &lt;strong&gt;Pre-release&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# In .github/workflows/deploy.yml&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Create GitHub Pre-Release&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;endsWith(needs.deploy.outputs.deployment_environment, &apos;_010&apos;)&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;softprops/action-gh-release@v2.4.1&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;prerelease&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# ... other parameters&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once the rollout successfully completes and reaches 100% of users, a later step in the same job updates the release, removing the “pre-release” flag. This ensures the official release notes are only marked as “Latest” when the deployment is fully complete.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# In .github/workflows/deploy.yml&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Update GitHub Release to Stable&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;endsWith(needs.deploy.outputs.deployment_environment, &apos;_100&apos;)&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gh release edit &quot;$&quot; --prerelease=false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These mechanisms ensure that while the deployment itself is automated, the development team remains fully informed and can quickly react to any issues.&lt;/p&gt;

&lt;h1 id=&quot;to-be-continued&quot;&gt;To be continued…&lt;/h1&gt;

&lt;p&gt;In the next and final part of this series, we’ll explore the “Complimenting Workflows” that support our core CI/CD pipeline. These include other automation tasks that enhance developer experience, maintain code quality, and ensure the overall health of the AnySoftKeyboard project. Stay tuned!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My LLM Tooling Doctrine: The Unix Philosophy for Local AI</title>
   <link href="https://evendanan.net/ai/llm/2025/11/08/llm-as-unix-philosophy"/>
   <updated>2025-11-08T12:00:00+00:00</updated>
   <id>https://evendanan.net/ai/llm/2025/11/08/llm-as-unix-philosophy</id>
   <content type="html">&lt;p&gt;I’ve been building a lot of small LLM-powered CLI tools for about a year now. These are tiny utilities, each with a very specific job: reviewing my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git diff&lt;/code&gt;, generating a commit message, or answering quick shell command questions.&lt;/p&gt;

&lt;p&gt;My terminal is full of these “micro-tools.”&lt;/p&gt;

&lt;p&gt;This is a deliberate choice. It’s a move away from the clunky, do-it-all monoliths and a full embrace of a philosophy that’s been battle-tested for decades: the Unix philosophy.&lt;/p&gt;

&lt;h2 id=&quot;but-why-not-just-use-an-llm-agentbot&quot;&gt;But Why Not Just Use an LLM Agent/Bot?&lt;/h2&gt;
&lt;p&gt;I know what you’re thinking. “Why build a CLI tool at all? I’ll just ask Gemini, ChatGPT, or my Claude Code Agent.”&lt;/p&gt;

&lt;p&gt;And you’re right. For exploratory, conversational, or complex one-off tasks, those tools are fantastic. I use them every day.&lt;/p&gt;

&lt;p&gt;But I’m talking about repeatable, automated, in-terminal workflows. The kind of stuff I do 50 times a day.&lt;/p&gt;

&lt;p&gt;When I’m deep in my terminal, I want to stay in my terminal. Switching to a browser tab or an IDE-specific chat panel is a context switch. It’s friction.&lt;/p&gt;

&lt;p&gt;Let’s look at the “code review” task:&lt;/p&gt;

&lt;h3 id=&quot;the-ai-chat-bot-flow&quot;&gt;The AI Chat Bot Flow:&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;Realize I want a review.&lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git diff&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Copy the output.&lt;/li&gt;
  &lt;li&gt;Alt-Tab to my browser.&lt;/li&gt;
  &lt;li&gt;Find the right Gemini/ChatGPT tab.&lt;/li&gt;
  &lt;li&gt;Paste the code.&lt;/li&gt;
  &lt;li&gt;Type the prompt: “Please review this git diff, focusing on…”&lt;/li&gt;
  &lt;li&gt;Wait for the response.&lt;/li&gt;
  &lt;li&gt;Read it, then Alt-Tab back to my code.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s 9 steps, and it pulled me completely out of my flow.&lt;/p&gt;

&lt;h3 id=&quot;the-specialized-cli-tool-flow&quot;&gt;The Specialized CLI Tool Flow:&lt;/h3&gt;

&lt;p&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-review&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is the entire philosophy. The specialized tool isn’t trying to be a general-purpose brain. It’s an ergonomic, zero-friction extension of your existing CLI workflow.&lt;/p&gt;

&lt;h2 id=&quot;why-monolithic-ai-cli-tools-feel-wrong&quot;&gt;Why Monolithic AI CLI Tools Feel Wrong&lt;/h2&gt;
&lt;p&gt;Once we’ve established why a CLI tool is the right fit for these tasks, there’s a fundamental design choice to make: should it be a single, monolithic tool, or a collection of small, specialized ones?&lt;/p&gt;

&lt;p&gt;The “one big tool” approach has serious flaws. The tool is either a jack-of-all-trades with a million confusing flags (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-tool --task=review --target=file.js&lt;/code&gt;), or it has a “smart” prompt-router that tries (and often fails) to guess what you want.&lt;/p&gt;

&lt;p&gt;Worst of all, it puts the burden of context on you, the user. You have to manually copy-paste your code, your diff, or your error message. It’s dumb.&lt;/p&gt;

&lt;h2 id=&quot;the-beauty-of-specialized-llm-tools&quot;&gt;The Beauty of Specialized LLM Tools&lt;/h2&gt;
&lt;p&gt;This is where the Unix philosophy comes in: make each program do one thing well.&lt;/p&gt;

&lt;p&gt;When you build small, separate tools, magical things happen.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Implicit, “Smart” Context: This is the killer feature. A specialized tool knows its job, so it can fetch its own context.
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-review&lt;/code&gt; (my code-review tool) doesn’t need me to pipe a diff. It just runs git diff itself.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-gc&lt;/code&gt; (commit-message) runs git diff –staged automatically.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-pr&lt;/code&gt; (PR title/desc) knows to run git diff main… to see the whole branch, then pushes to GitHub and creates a PR. The tool feels smart, not because the LLM is a genius, but because the tool itself is a good assistant.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Perfectly Tuned Prompts: You can’t use the same system prompt to review code and to generate a commit message. One needs to be a critical engineer, the other a concise technical writer. By splitting them, each tool has a small, highly-optimized prompt that does its one job perfectly. No prompt-routing, no “you are a helpful assistant” fluff.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Reliable, Focused Evaluation: This is a huge engineering win. When a tool has one job, it’s so much easier to evaluate. I can build a specific, high-quality test set for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-gc&lt;/code&gt; (commit messages) and another for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-review&lt;/code&gt; (code review). I’m not trying to test a massive, do-it-all prompt; I’m testing a small, focused one, which makes it easier to measure quality and prevent regressions.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Simplicity (UX): The user experience is just… clean. You want a commit message? Type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-gc&lt;/code&gt;. You want a review? Type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-review&lt;/code&gt;. No flags, no sub-commands, no cognitive overhead.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;the-critical-base-aka-dont-repeat-yourself&quot;&gt;The Critical Base a.k.a. Don’t Repeat Yourself&lt;/h2&gt;
&lt;p&gt;This all sounds great, but what about the maintenance nightmare of having 15 different tools? How do you manage API keys, model selection, and updates?&lt;/p&gt;

&lt;p&gt;This is the most important part: you must build a base implementation.&lt;/p&gt;

&lt;p&gt;I have a single, local llm-cli-utils library. Every micro-tool is just a 20-line script that imports this base. The base library handles all the boring, critical stuff:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;API Key Retrieval&lt;/strong&gt;: It knows exactly where to find the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OPENAI_API_KEY&lt;/code&gt; (or any other key) from the environment or a config file. No need to hardcode keys in every script.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;LLM Framework Abstraction&lt;/strong&gt;: The base library handles the actual API call (using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;langchain&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;litellm&lt;/code&gt;, or just raw &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openai-python&lt;/code&gt;). If I want to add a new model provider, I change it in one place, and all tools get the upgrade instantly. This also means I can easily choose different models—like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpt-4o&lt;/code&gt; for power and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;haiku-4.5&lt;/code&gt; for speed—matching the right model to the job.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Interaction Helpers&lt;/strong&gt;: This is where the magic happens. The base library has helpers for common tasks like parsing model output, handling streaming responses, and even managing conversation history for more complex interactions. This keeps the individual tool scripts clean and focused on their specific logic.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;my-family-of-tools-what-ive-built-and-how-i-integrate-them&quot;&gt;My Family of Tools: What I’ve Built and How I Integrate Them&lt;/h2&gt;
&lt;p&gt;Here’s an extra detail on how I build these. I don’t just dump them all into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/bin&lt;/code&gt;. I put them where they make the most sense.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;For Git-centric tasks, I create simple binaries like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-review&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-pr&lt;/code&gt;. To make them feel like native Git commands, I name them &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git-ai-review&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git-ai-pr&lt;/code&gt; and place them in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PATH&lt;/code&gt;. Git automatically discovers and executes them as sub-commands, so I can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git ai-review&lt;/code&gt; and it just works. This gives me the best of both worlds: a simple, independent binary that can also be used as a native Git command, which is a huge win for discoverability and ergonomics.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;For general tasks, I build standalone commands. My “quick question” tool is just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qq&lt;/code&gt;. Since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qq&lt;/code&gt; has no concept of a repository, it wouldn’t make sense as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; sub-command. Instead, it lives in the standard shell, right next to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This division is part of the same philosophy: design your tool for its specific job, and that includes its “home” on the command line.&lt;/p&gt;

&lt;p&gt;Here’s a peek at my local &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/bin&lt;/code&gt; directory:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qq&lt;/code&gt;: Quick question. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qq &quot;how to find files modified in the last 2 days&quot;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-review&lt;/code&gt;: Code review for the current git diff.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-gc&lt;/code&gt;: Git commit message for staged files.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-pr&lt;/code&gt;: Generate a PR title and description from the current branch’s diff, then pushes to GitHub and creates a PR.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-conflict-fix&lt;/code&gt;: Takes a file with git conflict markers (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/code&gt;) and attempts a clean merge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each one is simple, fast, and does one thing. And the list is growing! Here are some ideas for other tools I’m thinking of building:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-doc-gen&lt;/code&gt;: Scopes to a function or class and writes its docstring.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-test-gen&lt;/code&gt;: Scopes to a file or function and stubs out unit tests.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-fix-types&lt;/code&gt;: Fix/add types to Python and TypeScript code.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-fix-text&lt;/code&gt;: Typo/grammar/syntax fixer given a file (fixes variables names and comments, warn on classes and functions since those might be APIs).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ai-log-analyzer&lt;/code&gt;: Takes a log file and summarizes the errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The possibilities are endless. The key is to identify a repetitive task and build a small, focused tool to automate it.&lt;/p&gt;

&lt;p&gt;This is the way. You get the beautiful UX of small, specialized tools and the maintenance sanity of a single, shared core.&lt;/p&gt;

&lt;p&gt;By embracing the Unix philosophy, you can build a suite of AI-powered tools that are simple, powerful, and a joy to use. Stop building all-in-one AI behemoths. Start building small, sharp, Unix-style tools.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>LLM Evaluations a.k.a. How We Avoid Wat-Did-You-Say Moments</title>
   <link href="https://evendanan.net/ai/llm/asana/2025/08/29/llm-evaluations-at-asana"/>
   <updated>2025-08-29T12:00:00+00:00</updated>
   <id>https://evendanan.net/ai/llm/asana/2025/08/29/llm-evaluations-at-asana</id>
   <content type="html">&lt;p&gt;A few weeks ago, I had the pleasure of presenting at an &lt;a href=&quot;https://www.aicamp.ai/event/eventdetails/W2025062614&quot;&gt;AI-Camp event&lt;/a&gt; hosted at the Asana office in NYC. The topic was “LLM Evaluations: How we ensure AI tools produce consistent quality.” I wanted to share some of the key takeaways from that presentation here.&lt;/p&gt;

&lt;p&gt;You can find the slides from the presentation &lt;a href=&quot;/assets/llm-pres.pdf&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-problem-with-evaluating-llms&quot;&gt;The Problem with Evaluating LLMs&lt;/h2&gt;

&lt;p&gt;We all know how to test traditional software. We write unit tests, integration tests, and end-to-end tests. These tests are deterministic, objective, and easy to run. However, LLMs are not like traditional software. They are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Not deterministic:&lt;/strong&gt; The same input can produce different outputs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Not objective:&lt;/strong&gt; It’s hard to define a single “correct” output.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Quite a lift to run:&lt;/strong&gt; Evaluating LLMs can be time-consuming and resource-intensive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, how do we ensure the quality of our LLM-based tools?&lt;/p&gt;

&lt;h2 id=&quot;ai-as-a-judge&quot;&gt;AI as a Judge&lt;/h2&gt;

&lt;p&gt;One solution we’ve found effective at Asana is using an “AI as a Judge.” Instead of asking humans to evaluate every output of our internal AI tool, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z chat&lt;/code&gt;, we use another AI to do the judging. This approach has several advantages:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Easy to scale:&lt;/strong&gt; We can run evaluations on a large number of examples without human intervention.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Consistent:&lt;/strong&gt; While not deterministic, the judge is very consistent when using the same model, prompt, and questions.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Easy to automate:&lt;/strong&gt; We can easily integrate this into our CI/CD pipeline.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach is similar to what’s being used in AI Studio and other places in the industry.&lt;/p&gt;

&lt;h2 id=&quot;openevals-and-langchain&quot;&gt;OpenEvals and LangChain&lt;/h2&gt;

&lt;p&gt;To implement our AI Judge, we use &lt;a href=&quot;https://github.com/langchain-ai/openevals&quot;&gt;OpenEvals&lt;/a&gt;, a framework from LangChain that helps us write evaluations for AI applications. With OpenEvals, we can define criteria for correctness, conciseness, and other quality metrics, and then use an LLM to score the output of our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z chat&lt;/code&gt; tool.&lt;/p&gt;

&lt;p&gt;Here’s a simplified look at our evaluation flow:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;We have a set of evaluation data, which includes questions and criteria for correct answers.&lt;/li&gt;
  &lt;li&gt;For each question, our simulator calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z chat&lt;/code&gt; to get a response.&lt;/li&gt;
  &lt;li&gt;The response, along with the criteria and a judging prompt, is sent to our AI Judge.&lt;/li&gt;
  &lt;li&gt;The judge provides a grade, which we use to score the quality of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z chat&lt;/code&gt; response.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;findings-and-next-steps&quot;&gt;Findings and Next Steps&lt;/h2&gt;

&lt;p&gt;We currently have 32 evaluation cases and are seeing a response score of around 72% and a document retrieval score of 90%. This process has helped us identify several issues, such as missing ownership data and test cases that were failing despite the data being available to the LLM. We’ve also been able to improve our system’s prompt to get better results.&lt;/p&gt;

&lt;p&gt;This is an ongoing process, and we’re continuously adding new evaluation cases and refining our prompts and models to improve the quality of our AI tools.&lt;/p&gt;

&lt;p&gt;If you’re working with LLMs, I highly recommend setting up an evaluation framework. It’s a great way to ensure the quality and consistency of your AI applications.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>CI/CD at AnySoftKeyboard Part 1 aka Is it ready to ship?</title>
   <link href="https://evendanan.net/anysoftkeyboard/ci/2020/03/30/ci-cd-ask-part1"/>
   <updated>2020-03-30T20:03:30+00:00</updated>
   <id>https://evendanan.net/anysoftkeyboard/ci/2020/03/30/ci-cd-ask-part1</id>
   <content type="html">&lt;p&gt;Recently, I’ve &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/pull/1991&quot;&gt;merged&lt;/a&gt; several AnySoftKeyboard repositories into a &lt;a href=&quot;https://en.wikipedia.org/wiki/Monorepo&quot;&gt;monorepo&lt;/a&gt; - I’m a believer in the monorepo process. During that process, I also refined the Continuous Integration (CI)/Continuous Deployment (CD) process and wanted to give an overview of the process and the logic behind that.&lt;/p&gt;

&lt;p&gt;This is the first of three parts:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/2020/03/30/ci-cd-ask-part1.html&quot;&gt;Continuous Integration&lt;/a&gt;, aka &lt;em&gt;CI&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/11/09/ci-cd-ask-part2.html&quot;&gt;Continuous Deployment&lt;/a&gt;, aka &lt;em&gt;CD&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;Complimenting workflows.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;process&quot;&gt;Process&lt;/h1&gt;
&lt;p&gt;The CI/CD flow in AnySoftKeyboard starts when a pull request is submitted. Then, a series of checks are run, the code is reviewed, the pull request is merged, the checks are run again on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt; branch, and finally, the changes are deployed to users.&lt;/p&gt;

&lt;p&gt;The first five steps are the CI part, and the last is the CD part.&lt;/p&gt;

&lt;h1 id=&quot;why-ci---continuous-integration&quot;&gt;Why CI - Continuous Integration&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Continuous_integration&quot;&gt;Continuous Integration&lt;/a&gt; is pretty much an industry-standard, and well-accepted. In essence, for every commit (or a set of commits, e.g., a pull request), we run unit-tests, static-analysis tools, and code-style verifications. These checks will ensure three things:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;You are confident that your change did not break anything - hence you do not need to check &lt;em&gt;every&lt;/em&gt; possible scenario and use case.&lt;/li&gt;
  &lt;li&gt;The next developer who picks up your changes gets a healthy, readable, and working code, feeling confident that they can start working on their code-change.&lt;/li&gt;
  &lt;li&gt;With some confidence, any feature and use case are still working for end-users.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You’ll notice that the keyword here is &lt;em&gt;confidence&lt;/em&gt;. Continuous Integration gives us confidence that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt; commit of &lt;em&gt;main&lt;/em&gt; is valid and working as intended.&lt;/p&gt;

&lt;h2 id=&quot;pull-requests&quot;&gt;Pull Requests&lt;/h2&gt;
&lt;p&gt;Every change will be proposed as a pull request. The system will then perform the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Assigning all relevant code-owners as reviewers according to the &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/blob/main/.github/CODEOWNERS&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CODEOWNERS&lt;/code&gt;&lt;/a&gt; file. Assigning is done automatically by &lt;a href=&quot;https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners&quot;&gt;GitHub&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Running all checks. Defined in the &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/blob/main/.github/workflows/checks.yml&quot;&gt;checks&lt;/a&gt; workflow.&lt;/li&gt;
  &lt;li&gt;Block merging until checks pass and code-review approved. This is configured in the &lt;em&gt;protected branch&lt;/em&gt; setting in the repository settings page.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;checks&quot;&gt;Checks&lt;/h2&gt;
&lt;p&gt;Checks are what make us confident that our code works and does not break any existing functionality is still working as before this proposed code change. Several types of checks run on each code-change.&lt;/p&gt;

&lt;h3 id=&quot;tests&quot;&gt;Tests&lt;/h3&gt;
&lt;p&gt;The most common check that we run is &lt;a href=&quot;https://en.wikipedia.org/wiki/Unit_testing&quot;&gt;unit-tests&lt;/a&gt;. Essentially, unit-tests will verify that distinct pieces of code are doing what they are supposed to do correctly. For example, a test could verify that a Dictionary implementation can look up words and find typos.&lt;/p&gt;

&lt;p&gt;We also run &lt;a href=&quot;https://en.wikipedia.org/wiki/Integration_testing&quot;&gt;integration-tests&lt;/a&gt;, which ensure that different parts of the codebase work with each other as intended. For example, a test that given a user interaction with the keyboard view a specific text is printed in the Android’s text box.&lt;/p&gt;

&lt;p&gt;We write tests in two main cases:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;New functionality that we want to verify that is doing what we wanted: for each possible input category (or for each internal code branch), we have a test.&lt;/li&gt;
  &lt;li&gt;A bug fix that we want to verify that it won’t regress.
These types of tests provide us with the most confidence in our code.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note that tests cover the behavior of the code, and only the branches we individually coded in the test. So, of course, this is not complete.&lt;/p&gt;

&lt;h3 id=&quot;static-analysis&quot;&gt;Static-Analysis&lt;/h3&gt;
&lt;p&gt;Even if your code works under typical use-cases and all tests pass, it does not mean it is correct. &lt;a href=&quot;https://en.wikipedia.org/wiki/Static_program_analysis&quot;&gt;Static-Analysis&lt;/a&gt; tools can search your codebase for common programming issues. Such issues could be an incorrect use of an external API or assuming something is not null when it could.&lt;/p&gt;

&lt;p&gt;In AnySoftKeyboard, we use:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/google/error-prone&quot;&gt;Error-Prone&lt;/a&gt;, with an Android-specific &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/blob/main/gradle/errorprone.gradle&quot;&gt;configuration&lt;/a&gt;. Error-Prone is a fast checker that can find many Java-related code smells. It is widely popular and actively maintained.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.android.com/studio/write/lint&quot;&gt;Lint&lt;/a&gt; with an AnySoftKeyboard-specific &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/blob/main/configs/lint.xml&quot;&gt;configuration&lt;/a&gt;. Lint is the gold-standard in Android-centric code and resources static-analysis. It is well-known and actively maintained.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://checkstyle.org/&quot;&gt;checkstyle&lt;/a&gt; with an AnySoftKeyboard-specific &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/tree/main/configs/checkstyle&quot;&gt;configuration&lt;/a&gt;. Another well-known Java code analysis. We use it to find illegal/undesirable code usage and files.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/google/google-java-format&quot;&gt;Google-Java-Format&lt;/a&gt;. This tool ensures that the code style across the codebase is defined well and maintained. It also supports automatic formatting, so win-win.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, many other tools can be used. A few notable mentions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://pmd.github.io/&quot;&gt;PMD&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://findbugs.sourceforge.net/&quot;&gt;FindBugs&lt;/a&gt;/&lt;a href=&quot;https://spotbugs.github.io/&quot;&gt;SpotBugs&lt;/a&gt;. FindBugs seems to be dead, so maybe use SpotBugs, if interested.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://pitest.org/&quot;&gt;Mutation-Testing&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For us, we decided to focus on a few, very popular and fast, static-analysis to reduce the complexity of the check suite and the requirements from the developers.&lt;/p&gt;

&lt;h2 id=&quot;cache&quot;&gt;Cache&lt;/h2&gt;
&lt;p&gt;A side-note. A lot is going on here; compiling production code, debug code and tests, running tests, and several tools on top of that code. This process could be slow, both in the CI environment and in the local developer environment. To that end, it is crucial to pick a build system and tools that support caching (that is, do not execute actions if the input has not changed) and configure them correctly.
Luckily, Gradle has an excellent &lt;a href=&quot;https://docs.gradle.org/current/userguide/build_cache.html&quot;&gt;caching&lt;/a&gt; mechanism (as long as your system is not too complicated): it will cache compilation steps and test runs.
Error-Prone is running inside the compilation step, so we get caching for free here.
Lint also has an excellent caching mechanism, relying on Gradle’s caching mechanism.&lt;/p&gt;

&lt;p&gt;Another benefit of caching is that faster feedback makes developers happier. Like, much much happier.&lt;/p&gt;

&lt;h3 id=&quot;persistent-cache&quot;&gt;Persistent Cache&lt;/h3&gt;
&lt;p&gt;Gradle will store its cache on disk (there is an option to use &lt;a href=&quot;https://docs.gradle.org/current/userguide/build_cache.html#sec:build_cache_setup_http_backend&quot;&gt;remote cache&lt;/a&gt;, under a specified path. Gradle is using this disk cache in sequential runs to skip actions that have run in the past. This state is called &lt;em&gt;warm cache&lt;/em&gt; - skipping a step because you already have the result of it in the cache.&lt;/p&gt;

&lt;p&gt;Keeping your local cache warm, is easy, and happens as you build locally. But, on CI, it’s a bit harder since every time you run a job on CI, you will get a different machine, one that does &lt;em&gt;not&lt;/em&gt; have any cache related to your build. Run checks without cache means much slower checks.&lt;/p&gt;

&lt;p&gt;Since we are using &lt;a href=&quot;https://docs.github.com/en/actions/managing-workflow-runs&quot;&gt;Github Workflows&lt;/a&gt;, we can specify where is our cache is located, how to identify its version, etc. See our checks-workflow &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/blob/master/.github/workflows/checks.yml&quot;&gt;configuration&lt;/a&gt; for details.&lt;/p&gt;

&lt;p&gt;For AnySoftKeyboard, running the PR checks without cached can take up to 20 minutes, while about 8 minutes with the cache warmed.&lt;/p&gt;

&lt;h3 id=&quot;docker&quot;&gt;Docker&lt;/h3&gt;
&lt;p&gt;Caching relies on the assumption that if the input is the same, the action will produce the same output. This assumption is true only if:
The inputs are defined correctly.
The action only uses the inputs to produce the output.
If both these guidelines apply, then the action is defined as &lt;em&gt;hermetic&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;As a rule, Gradle is &lt;em&gt;not&lt;/em&gt; a hermetic build system (&lt;a href=&quot;https://bazel.build/&quot;&gt;bazel&lt;/a&gt;, for example, is), but it does an outstanding job nonetheless.&lt;/p&gt;

&lt;p&gt;To reduce the chances of a misbehaving action, we’ll use Docker in CI, so Gradle will have the same environment during its execution. Same Java, same bash, same SDK, same NDK, etc.&lt;/p&gt;

&lt;p&gt;We are using an &lt;a href=&quot;https://hub.docker.com/r/menny/ndk_ask/tags&quot;&gt;image&lt;/a&gt; that is built explicitly for AnySoftKeyboard. This image uses &lt;a href=&quot;https://hub.docker.com/r/adoptopenjdk/openjdk10/tags&quot;&gt;Java10&lt;/a&gt; (this helps ErrorProne run faster), has the required SDK, NDK and build-tools all preinstalled and ready to go. Check out the &lt;a href=&quot;https://github.com/menny/docker_android/blob/main/android_ndk/Dockerfile&quot;&gt;Dockerfile&lt;/a&gt; for this image and the &lt;a href=&quot;https://github.com/menny/docker_android/blob/main/.circleci/config.yml&quot;&gt;CI build script&lt;/a&gt; on &lt;a href=&quot;https://github.com/menny/docker_android&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As a side note, I would mention that it’s pretty easy to create a custom image and store it in on &lt;a href=&quot;https://ropensci.github.io/r-docker-tutorial/&quot;&gt;Docker Hub&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;to-be-continued&quot;&gt;To be continued&lt;/h1&gt;
&lt;p&gt;What will happen to our pull request? How will it be integrated into the &lt;em&gt;main&lt;/em&gt; branch? And, how will it reach end-users?
Stay tuned for the next parts.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Bazel and Gradle Co-exist a.k.a. Can't we all just get along?</title>
   <link href="https://evendanan.net/android/bazel/gradle/2016/01/05/bazel-part-1"/>
   <updated>2016-01-05T11:32:00+00:00</updated>
   <id>https://evendanan.net/android/bazel/gradle/2016/01/05/bazel-part-1</id>
   <content type="html">&lt;p&gt;After reviewing &lt;a href=&quot;https://buckbuild.com/&quot;&gt;Buck&lt;/a&gt; in a previous &lt;a href=&quot;http://evendanan.net/android/buck/gradle/maven/2015/11/06/buck-maven-and-support/&quot;&gt;post&lt;/a&gt;,
it was time to check out &lt;a href=&quot;http://bazel.io/&quot;&gt;Bazel&lt;/a&gt;. Bazel is the open-source version of Google’s internal &lt;em&gt;Blaze&lt;/em&gt; build system (and for the record,
Buck was &lt;a href=&quot;http://www.infoq.com/news/2013/05/Facebook-buck-xctool-build&quot;&gt;inspired&lt;/a&gt; by Blaze).&lt;br /&gt;
I’m going to have a few posts about Bazel in the journy to convert a project into Bazel from Gradle.&lt;/p&gt;

&lt;h1 id=&quot;why&quot;&gt;Why&lt;/h1&gt;
&lt;p&gt;Well, given you have an existing Android project; it is probably being built with Gradle. It’s working, it’s stable, and it has all kind
of neccesery tasks you are depending on. Which make converting to Bazel (or any other build system) quite a block.&lt;br /&gt;
So let’s try to have our project support both Bazel and Gradle at the same time.&lt;/p&gt;

&lt;h1 id=&quot;pre-requisites&quot;&gt;Pre-requisites&lt;/h1&gt;
&lt;p&gt;For our posts, let’s say that we have an awesome Android app - currently built using Gradle - that we want to convert to Bazel. We’ll
use &lt;a href=&quot;https://github.com/menny/BazelPlayground&quot;&gt;this &lt;em&gt;git&lt;/em&gt; repo&lt;/a&gt; for our posts, and &lt;a href=&quot;https://github.com/menny/BazelPlayground/commit/ac5e50f6532cc24419ccefd481d5374cc0982e64&quot;&gt;here&lt;/a&gt; is the app’s initial state (simple Android app, built with Gradle).&lt;/p&gt;

&lt;h1 id=&quot;the-problem&quot;&gt;The Problem&lt;/h1&gt;
&lt;p&gt;Bazel two type of files that define the build:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WORKSPACE&lt;/code&gt; - which defines the global build properties (like SDK path), and defines external repos (libraries) which can be used in our build.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BUILD&lt;/code&gt; - which defines a module in our build, and how to build it. A module can be a library, content, or binary (our app).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well, Gradle default build output folder is called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build&lt;/code&gt;, and on some OSes (I’m looking at you, MacOSX), the filesystem can be set (or default) to
be &lt;a href=&quot;https://en.wikipedia.org/wiki/HFS_Plus#Limitations&quot;&gt;case-insensitive&lt;/a&gt;. This introduces the issue of Gradle-Bazel: both require a file-system
object named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build&lt;/code&gt;. This is, of course, not an issue for Linux, but it is an issue for MacOSX.&lt;br /&gt;&lt;/p&gt;

&lt;h1 id=&quot;what-to-do&quot;&gt;What to do?&lt;/h1&gt;
&lt;p&gt;Unlike Bazel, Gradle is very configurable: we’ll just tell Gradle to use a different folder name for its build outputs. 
This is done by adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buildDir = &apos;gen_build&apos;&lt;/code&gt; to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build.gradle&lt;/code&gt; file, and revising the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitignore&lt;/code&gt; settings.&lt;br /&gt;&lt;br /&gt;
Here is the &lt;a href=&quot;https://github.com/menny/BazelPlayground/commit/945daffa11249c3ac0841edf89c97098faf0c5e7&quot;&gt;commit&lt;/a&gt; that fixes this for our awesome demo app.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Maven and Support Library Artifacts in Buck a.k.a. Wait, It Doesn't Have Full Dependency Resolution Support!?</title>
   <link href="https://evendanan.net/android/buck/gradle/maven/2015/11/06/buck-maven-and-support"/>
   <updated>2015-11-06T11:32:00+00:00</updated>
   <id>https://evendanan.net/android/buck/gradle/maven/2015/11/06/buck-maven-and-support</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://buckbuild.com/&quot;&gt;Buck&lt;/a&gt; is &lt;em&gt;Facebook&lt;/em&gt;’s super fast build system. How fast? Up to &lt;a href=&quot;https://buckbuild.com/article/exopackage.html&quot;&gt;7.5 times&lt;/a&gt;. That was intriging enough to get me to try it out, and immediately realizing that the platform is not complete..
One of the features missing is proper and complete support for &lt;em&gt;Maven&lt;/em&gt; remote artifacts (aar and jar) and locally hosted Support Library artifacts.&lt;/p&gt;

&lt;h1 id=&quot;remote-maven-artifacts&quot;&gt;Remote Maven Artifacts&lt;/h1&gt;
&lt;p&gt;So, &lt;em&gt;Buck&lt;/em&gt; does support &lt;em&gt;Maven&lt;/em&gt; artifacts in a rule called &lt;a href=&quot;https://buckbuild.com/rule/remote_file.html&quot;&gt;remote_file&lt;/a&gt;, which can fetch
files remotely, process them and make them available to the code-base. For example, if you want &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.google.guava:guava:18.0&lt;/code&gt; dependency
you’ll create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;remote_file&lt;/code&gt; rule like this:
&lt;script src=&quot;https://gist.github.com/832c6a3a5e10752fd357.js?file=file-remote_file_jar.py&quot;&gt; &lt;/script&gt; 
and depend on it in one of your targets:
&lt;script src=&quot;https://gist.github.com/832c6a3a5e10752fd357.js?file=android_library_with_remote_file.py&quot;&gt; &lt;/script&gt; 
&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jar&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aar&lt;/code&gt; if you want to fetch &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aar&lt;/code&gt; libraries.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-maven-rule&quot;&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maven&lt;/code&gt; rule&lt;/h2&gt;
&lt;p&gt;This works, but wouldn’t it be easier and more friendly if you could import artifacts as easily as &lt;em&gt;Gradle&lt;/em&gt; allows?&lt;br /&gt;
Based on &lt;a href=&quot;https://github.com/zserge/buckbone/blob/master/buckbonejava&quot;&gt;zserge&lt;/a&gt; work, here’s a simple way to achieve exactly that.
First, create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DEFS&lt;/code&gt; file in your project’s root folder, and add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mavensha1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maven&lt;/code&gt; functions to it:
&lt;script src=&quot;https://gist.github.com/832c6a3a5e10752fd357.js?file=maven_rule.py&quot;&gt; &lt;/script&gt;
Second, import the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DEFS&lt;/code&gt; file to you project, but add the import code to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.buckconfig&lt;/code&gt; file:
&lt;script src=&quot;https://gist.github.com/832c6a3a5e10752fd357.js?file=.buckconfig.yaml&quot;&gt; &lt;/script&gt;
This will make &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maven&lt;/code&gt; rule available to every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BUCK&lt;/code&gt; file. So, now you can simply depend on the rule in your target, like this:
&lt;script src=&quot;https://gist.github.com/832c6a3a5e10752fd357.js?file=library_with_maven_rule.py&quot;&gt; &lt;/script&gt;&lt;/p&gt;

&lt;p&gt;It doesn’t matter if the artifact is an AAR or a JAR, this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maven&lt;/code&gt; rule will fetch either!&lt;/p&gt;

&lt;h1 id=&quot;the-support-library-case&quot;&gt;The Support Library case&lt;/h1&gt;
&lt;p&gt;For some reason (legal? distribution?), the support library is not available in &lt;a href=&quot;http://search.maven.org/&quot;&gt;Maven Central&lt;/a&gt;
and instead, Google directly place it under your SDK folder (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[SDK folder root]/extras/android/m2repository/&lt;/code&gt;). That does not help the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mvn:&lt;/code&gt;
prefix to find it.. The only safe way to make this work is by manually copying the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AAR&lt;/code&gt; file from there to a folder in your project
(Buck will not allow to reference a file outside the project’s folder structure).&lt;/p&gt;

&lt;h2 id=&quot;the-android_support_library-rule&quot;&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android_support_library&lt;/code&gt; rule&lt;/h2&gt;
&lt;p&gt;First, add another &lt;em&gt;rule&lt;/em&gt; to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DEFS&lt;/code&gt; file we defined above:
&lt;script src=&quot;https://gist.github.com/832c6a3a5e10752fd357.js?file=android_support_library.py&quot;&gt; &lt;/script&gt;
Then copy the libraries you need in your project to a locale folder inside your project (lets say &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libs&lt;/code&gt;). Create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BUCK&lt;/code&gt; file
in that folder and import the support libraries:
&lt;script src=&quot;https://gist.github.com/832c6a3a5e10752fd357.js?file=DEFS_support_libraries.py&quot;&gt; &lt;/script&gt;
Now you have targets you can depend on when creating Android app:
&lt;script src=&quot;https://gist.github.com/832c6a3a5e10752fd357.js?file=target_with_support_library.py&quot;&gt; &lt;/script&gt;&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;This is an amazing build platform! So fast, and so explicit to define and makes complete sense. The problem is that if you are to use it, you kinda giving up on &lt;em&gt;Gradle&lt;/em&gt;, or any other (&lt;em&gt;Bazel?&lt;/em&gt;) build system. The reason is that Facebook has its own idea of how an Android project is built, and how the relations between libraries and modules are defined. For example, you will not longer have resource merging, resource overwriting, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;R&lt;/code&gt; constant values. Aligning with &lt;em&gt;Buck’s&lt;/em&gt; paradigm might turn out to be a very costly operation, and will definetly will not be compatible with &lt;em&gt;Gradle&lt;/em&gt;, meaning you will not be able to run both build systems on the same codebase.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Auto-Generate Version Name a.k.a. It Is So Boring Incrementing Build, Let Git Do It</title>
   <link href="https://evendanan.net/android/git/gradle/2015/04/13/auto-generate-version-name"/>
   <updated>2015-04-13T20:52:00+00:00</updated>
   <id>https://evendanan.net/android/git/gradle/2015/04/13/auto-generate-version-name</id>
   <content type="html">&lt;p&gt;&lt;em&gt;0.0.1&lt;/em&gt;, &lt;em&gt;0.0.2&lt;/em&gt;, &lt;em&gt;0.1.0&lt;/em&gt;, &lt;em&gt;0.1.1&lt;/em&gt;, &lt;em&gt;0.1.3&lt;/em&gt;,…. I’m super lazy, and I get bored really quickly with repeating tasks. So, whenever
I can offload something to a script, I just do it.&lt;br /&gt;
One such offloading I’ve done lately, is versioning the APK.&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;So, I’ve created a very simple &lt;em&gt;Groovy&lt;/em&gt; plugin for &lt;em&gt;Gradle&lt;/em&gt;, which will calculate
the &lt;a href=&quot;http://developer.android.com/tools/publishing/versioning.html&quot;&gt;version-code&lt;/a&gt; and
version-name based on the count of commits in the git repo. It’s is not the brightest
method, but it is so damn simple, it has to work.. Right?&lt;/p&gt;

&lt;h1 id=&quot;plugin&quot;&gt;Plugin&lt;/h1&gt;
&lt;p&gt;Gradle can be extended using &lt;a href=&quot;http://en.wikipedia.org/wiki/Groovy_%28programming_language%29&quot;&gt;Groovy&lt;/a&gt; plugins (under the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buildSrc&lt;/code&gt; folder). We’ll use that to provide two static methods to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build.gradle&lt;/code&gt; file:
&lt;script src=&quot;https://gist.github.com/adeaa10544e911c55365.js?file=VersionBuilder.groovy&quot;&gt; &lt;/script&gt;
This file should be saved as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[project root]/buildSrc/src/main/groovy/versionbuilder/VersionBuilder.groovy&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;api&quot;&gt;API&lt;/h2&gt;
&lt;p&gt;This is will give you two public static methods which you can use:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buildGitVersionNumber&lt;/code&gt; - Returns an integer which can be used as your project’s
 &lt;em&gt;versionCode&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buildGitVersionName&lt;/code&gt; - Returns a &lt;em&gt;major.minor.build&lt;/em&gt; string which can be used as
 project’s &lt;em&gt;versionName&lt;/em&gt;. Change the plugin’s code, if you prefer a different template for
 versioning.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;example&quot;&gt;Example&lt;/h2&gt;
&lt;p&gt;To use those methods, in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build.gradle&lt;/code&gt; file:
&lt;script src=&quot;https://gist.github.com/adeaa10544e911c55365.js?file=build.gradle&quot;&gt; &lt;/script&gt;&lt;/p&gt;

&lt;h2 id=&quot;configuring&quot;&gt;Configuring&lt;/h2&gt;
&lt;p&gt;You probably noticed two &lt;em&gt;constants&lt;/em&gt; in &lt;a href=&quot;https://gist.github.com/menny/adeaa10544e911c55365#file-versionbuilder-groovy&quot;&gt;plugin’s code&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GIT_COMMIT_COUNT_NORMALIZE&lt;/code&gt; - Use this to subtract a constant from the commits count. Use this to set the initial &lt;em&gt;versionCode&lt;/em&gt; as close as possible to your current &lt;em&gt;versionCode&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GIT_COMMIT_COUNT_MINOR_NORMALIZE&lt;/code&gt; - you’ll probably want to reset your &lt;em&gt;build&lt;/em&gt; versioning every time you bump &lt;em&gt;minor&lt;/em&gt; or &lt;em&gt;major&lt;/em&gt;.&lt;br /&gt;
 &lt;br /&gt;
 Things will just work from now on! No need to figure versions anymore, what a &lt;a href=&quot;https://xkcd.com/1205/&quot;&gt;profit&lt;/a&gt;, ah?&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Tabs to Spaces in a GIT repo a.k.a If everyone is Doing It, I'm Doing It Too</title>
   <link href="https://evendanan.net/android/git/code-style/2015/04/11/tabs-to-spaces-using-git"/>
   <updated>2015-04-11T13:52:00+00:00</updated>
   <id>https://evendanan.net/android/git/code-style/2015/04/11/tabs-to-spaces-using-git</id>
   <content type="html">&lt;p&gt;I work on a few projects right now, some using the &lt;a href=&quot;https://source.android.com/source/code-style.html&quot;&gt;Android code-style&lt;/a&gt; and some their &lt;a href=&quot;http://robolectric.org/contributing/&quot;&gt;own&lt;/a&gt;. This is very confusing, and always causes problems when pushing a commit for merge.&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;I’m trying to consolidate all the projects into Android’s code-style. Specifically, &lt;em&gt;no-tabs&lt;/em&gt; and &lt;em&gt;4 spaces indents&lt;/em&gt;.&lt;/p&gt;

&lt;h1 id=&quot;game-plan&quot;&gt;Game Plan&lt;/h1&gt;
&lt;p&gt;Going over all the files and changing the tabs to spaces is, of course, not an options. More over, we do not want our name on every single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git blame&lt;/code&gt; code line! So, we’ll need to convert all the tabs to spaces, ensure all indentation is 4 spaces, and re-write history to hide it.&lt;/p&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Linux or Mac OSX machine. If you know how to do it under Windows, please leave a comment.&lt;/li&gt;
  &lt;li&gt;You’ll need &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push -f&lt;/code&gt; permission to the remote repo. That’s the only way to re-write history.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expand&lt;/code&gt; should be installed on you machine&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;switching-tabs-to-4-spaces&quot;&gt;Switching Tabs to 4 Spaces&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find . -name &apos;*.java&apos; ! -type d -exec bash -c &apos;expand -t 4 &quot;$0&quot; &amp;gt; /tmp/e &amp;amp;&amp;amp; mv /tmp/e &quot;$0&quot;&apos; {} \;&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Here’s what we are doing with that command:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;find&lt;/em&gt; all the &lt;strong&gt;Java&lt;/strong&gt; files in the repo.&lt;/li&gt;
  &lt;li&gt;On each file, execute &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expand&lt;/code&gt; (using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt;) which will convert tab characters to 4 spaces, and save into a temporary file.&lt;/li&gt;
  &lt;li&gt;The move the temporary file to overwrite the original file.&lt;/li&gt;
  &lt;li&gt;If you also want to convert your &lt;em&gt;XML&lt;/em&gt; files to spaces, then run the same command with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.xml&lt;/code&gt; filter.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;commit-the-changes&quot;&gt;Commit the Changes&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git add .&lt;/code&gt;&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git commit -m &quot;converting tabs to spaces&quot;&lt;/code&gt;&lt;br /&gt;
Now, we have a commit in our local git repo that holds all the changes. Let’s get the hash for the commit using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git rev-parse HEAD&lt;/code&gt;. For our example, let’s say it is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0867e54002195d16dbfb69076d1df2d8b1f83e6f&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;re-write-git-history&quot;&gt;Re-write git History&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git filter-branch --tree-filter &apos;git diff-tree --name-only --diff-filter=AM -r --no-commit-id 0867e54002195d16dbfb69076d1df2d8b1f83e6f&apos; HEAD&lt;/code&gt;&lt;br /&gt;
This is the cool part: hiding all the evidence that you made the switch. This will ensure that when developers check &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git blame&lt;/code&gt; on a file, the &lt;em&gt;tabs-&amp;gt;spaces&lt;/em&gt; changes will not show.&lt;br /&gt;
This takes &lt;em&gt;a very long time&lt;/em&gt; to run. The bigger the repo the longer it will take.&lt;/p&gt;

&lt;h2 id=&quot;force-push-to-master&quot;&gt;Force push to &lt;em&gt;master&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push -f origin master&lt;/code&gt;&lt;br /&gt;
Pushing to the remote repo (be it &lt;em&gt;origin&lt;/em&gt; or &lt;em&gt;upstream&lt;/em&gt;).
&lt;br /&gt;&lt;/p&gt;

&lt;h1 id=&quot;possible-problems&quot;&gt;Possible Problems&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter-branch&lt;/code&gt; creates a new, revised, branch in your git repo. This means a few things:&lt;/li&gt;
  &lt;li&gt;the size of your repo may be doubled!&lt;/li&gt;
  &lt;li&gt;hash of the commits in your history will be changed! This means that if you have a link to a specific commit, this link &lt;em&gt;might&lt;/em&gt; be dead after this process. The reason for that is that if &lt;em&gt;git&lt;/em&gt; sees that nothing is pointing to a specific commit, it will garbage collect it.&lt;br /&gt;
 A simple way to fix that, is to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tag&lt;/code&gt;, and annotate the commit prior to starting this process.&lt;/li&gt;
  &lt;li&gt;Applying the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter-branch&lt;/code&gt; may fail (“Cannot create a new backup.”) because of an existing backup in your &lt;em&gt;refs&lt;/em&gt; folder. Delete those with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rm -rf .git/refs/original/refs/heads/master&lt;/code&gt; from the root of your repo.&lt;/li&gt;
  &lt;li&gt;If you get &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;! [rejected]        master -&amp;gt; master (non-fast-forward)&lt;/code&gt; when pushing the changes to your git repo, it means that you do not have &lt;em&gt;force push&lt;/em&gt; permissions in the git repo. Talk to the owner.&lt;/li&gt;
  &lt;li&gt;Previous tags are kept, and are pointing to the original commit, not the revised one. So we are not completely rewriting history.&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Migrating to Robolectric v3 a.k.a Just Work, Dear God, Just Work!</title>
   <link href="https://evendanan.net/robolectric/unit-test/2015/04/09/migrating-to-robolectric-v3"/>
   <updated>2015-04-09T13:52:00+00:00</updated>
   <id>https://evendanan.net/robolectric/unit-test/2015/04/09/migrating-to-robolectric-v3</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://robolectric.org&quot;&gt;Robolectric&lt;/a&gt; has reach a big milestone recently: &lt;a href=&quot;https://groups.google.com/forum/#!topic/robolectric/1XWVJvKiFjA&quot;&gt;V3-rc2&lt;/a&gt;, so it was time
to upgrade all my personal projects.&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;With the new release, &lt;a href=&quot;https://github.com/orgs/robolectric/people&quot;&gt;the team&lt;/a&gt; has introduced new concepts and new APIs (and removed and renamed others). In most cases, I find the new API and structure much more readable, and generally like it.&lt;br /&gt;
But as a long time user of &lt;em&gt;Robolectric&lt;/em&gt;, it also means that I have a lot of code to refactor.
&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; all code in this post is &lt;em&gt;Public Domain&lt;/em&gt; as described in &lt;a href=&quot;https://wiki.creativecommons.org/CC0&quot;&gt;CC0&lt;/a&gt;.
&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;First, to make things clear, if it wasn’t worth the hassle, I would not upgrade to v3, but this new version comes with a lot of nice improvements
and &lt;em&gt;Robolectric&lt;/em&gt;, in general, is &lt;a href=&quot;http://stackoverflow.com/a/18271651/1324235&quot;&gt;awesome&lt;/a&gt; beyond words.
&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Second, let’s get to the migration guide:&lt;br /&gt;&lt;/p&gt;
&lt;h1 id=&quot;gradle-stuff&quot;&gt;Gradle stuff&lt;/h1&gt;
&lt;p&gt;You no longer need the &lt;em&gt;Robolectric&lt;/em&gt; Gradle plugin. So just remove
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;classpath &apos;org.robolectric:robolectric-gradle-plugin&apos;&lt;/code&gt; from &lt;em&gt;build.gradle&lt;/em&gt;.&lt;br /&gt;
Some developers had used a hack to get &lt;em&gt;Robolectric&lt;/em&gt; tests navigable in Android Studio:
&lt;script src=&quot;https://gist.github.com/c45781b8d980f4a60ae3.js?file=remove-androidTest&quot;&gt; &lt;/script&gt;
and/or
&lt;script src=&quot;https://gist.github.com/c45781b8d980f4a60ae3.js?file=remove_idea_plugin&quot;&gt; &lt;/script&gt;
You don’t need those anymore.
&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Any specific &lt;em&gt;Robolectric&lt;/em&gt; settings should also be removed, since we are not using the &lt;em&gt;Robolectric Gradle Plugin&lt;/em&gt; anymore. Remove this:
&lt;script src=&quot;https://gist.github.com/c45781b8d980f4a60ae3.js?file=remove_robolectric_config&quot;&gt; &lt;/script&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;With &lt;em&gt;Android Gradle Plugin v1.1.0&lt;/em&gt; there is a &lt;a href=&quot;http://tools.android.com/tech-docs/unit-testing-support&quot;&gt;built-in support for unit-test&lt;/a&gt; (using &lt;a href=&quot;https://android.googlesource.com/platform/tools/base/+/e8f845b47459973838c99656ae98563b268f21ff/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/tasks/MockableAndroidJarTask.groovy&quot;&gt;&lt;em&gt;mockableAndroidJar&lt;/em&gt;&lt;/a&gt;), so we no longer need to setup
&lt;em&gt;Robolectric&lt;/em&gt; as &lt;em&gt;androidTestCompile&lt;/em&gt;.&lt;br /&gt;
Change &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;androidTestCompile(&apos;org.robolectric:robolectric:2.4&apos;)&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testCompile &apos;org.robolectric:robolectric:3.0-rc2&apos;&lt;/code&gt;&lt;br /&gt;
&lt;strong&gt;DO NOT USE&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.squareup:fest-android:1.0.8&lt;/code&gt;. If you have it in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testCompile&lt;/code&gt; be sure to remove it. There is a conflict between
it and &lt;em&gt;Robolectric&lt;/em&gt; which causes all Fragment/Activity related unit-tests to &lt;a href=&quot;https://github.com/robolectric/robolectric/issues/1633&quot;&gt;fail&lt;/a&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NoSuchMethodError&lt;/code&gt;.
&lt;br /&gt;
If you have to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.squareup:fest-android:1.0.8&lt;/code&gt;, then remove &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;support-v4&lt;/code&gt; from it:
&lt;script src=&quot;https://gist.github.com/c45781b8d980f4a60ae3.js?file=remove_fest.groovy&quot;&gt; &lt;/script&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;h1 id=&quot;testrunner&quot;&gt;TestRunner&lt;/h1&gt;
&lt;p&gt;You have probably used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@RunWith(RobolectricTestRunner.class)&lt;/code&gt; in your classes. Switch to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RobolectricGradleTestRunner&lt;/code&gt;, but also add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Config&lt;/code&gt; annotation, and point to your app’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BuildConfig&lt;/code&gt; class:
&lt;script src=&quot;https://gist.github.com/c45781b8d980f4a60ae3.js?file=MainActivityTest.java&quot;&gt; &lt;/script&gt;
This is enough, but I opted to a different approach: a custom test-runner that sets the &lt;em&gt;Robolectric&lt;/em&gt; configuration:
&lt;script src=&quot;https://gist.github.com/c45781b8d980f4a60ae3.js?file=CustomGradleTestRunner.java&quot;&gt; &lt;/script&gt;
This test-runner will set the app’s SDK level to 21 (since &lt;em&gt;Robolectric&lt;/em&gt; does not support anything higher than that, right now), and will
set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;constants&lt;/code&gt; value to the app’s BuildConfig class.
&lt;br /&gt;&lt;/p&gt;

&lt;h1 id=&quot;api-and-behavioral-changes&quot;&gt;API and Behavioral Changes&lt;/h1&gt;
&lt;h2 id=&quot;entry-points&quot;&gt;Entry Points&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Robolectric.application&lt;/code&gt; does not exist anymore. From now on use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RuntimeEnvironment.application&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Robolectric.shadowOf()&lt;/code&gt; does not exist anymore. From now on use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Shadows.shadowOf()&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Robolectric.getShadowApplication()&lt;/code&gt; does not exist anymore. From now on use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ShadowApplication.getInstance()&lt;/code&gt;.
 &lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;activity-life-cycle-management&quot;&gt;Activity life-cycle management&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Robolectric&lt;/em&gt; lets you manage the life cycle of the activity using it’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActivityController&lt;/code&gt;. This is still there, but there are two
additions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActivityController.postCreate()&lt;/code&gt; was added.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActivityController&lt;/code&gt; now has a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup&lt;/code&gt; method which is a shorthand for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create().start().postCreate(null).resume().visible().get()&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;and another shorthand was added &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Robolectric.setupActivity(Class&amp;lt;T extends Activity&amp;gt;)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quite useful.
&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;add-on-modules&quot;&gt;Add-On Modules&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Robolectric&lt;/em&gt; was broken up to modules; the main library will only include Shadows for the main Android framework classes, and anything else (support library, maps, etc) is served as an &lt;a href=&quot;http://robolectric.org/using-add-on-modules/&quot;&gt;Add-On Module&lt;/a&gt;.&lt;br /&gt;
I don’t have much to say about that, other than: if you want a Shadow of a class that is part of an external library (for example, &lt;em&gt;support-v4&lt;/em&gt;’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LocalBroadcastManager&lt;/code&gt;), be sure to include the right &lt;em&gt;Add-On&lt;/em&gt; (for example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testCompile &apos;org.robolectric:shadows-support-v4&apos;&lt;/code&gt;). You can access the Shadow using the &lt;em&gt;Add-On&lt;/em&gt;’s specific &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Shadows&lt;/code&gt; entry-point (for completeness: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org.robolectric.support.v4.Shadows&lt;/code&gt;).&lt;/p&gt;

&lt;h2 id=&quot;missing-stuff&quot;&gt;Missing stuff&lt;/h2&gt;
&lt;p&gt;This is what I’ve found so far:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IntentFilter&lt;/code&gt; will not do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;equals&lt;/code&gt; correctly anymore. I guess that this is due to some cleanup, which resulted in &lt;em&gt;Shadows&lt;/em&gt; to be removed. I used a new static method to calculate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;match&lt;/code&gt;:
&lt;script src=&quot;https://gist.github.com/c45781b8d980f4a60ae3.js?file=IntentFilterIsSimilar.java&quot;&gt; &lt;/script&gt;
I use &lt;a href=&quot;http://mockito.org/&quot;&gt;Mockito&lt;/a&gt; a lot and needed a  &lt;a href=&quot;https://gist.github.com/menny/c45781b8d980f4a60ae3#file-intentfilterequalmatcher-java&quot;&gt;Matcher&lt;/a&gt; too.
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;replaced-stuff&quot;&gt;Replaced stuff&lt;/h2&gt;
&lt;p&gt;This is what I’ve found so far:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TestCursor&lt;/code&gt; was replaced with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RoboCursor&lt;/code&gt;, which is an improved version of it. This is a great addition.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;UI Thread&lt;/em&gt; is now called &lt;em&gt;ForegroundScheduler&lt;/em&gt;, which means that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Robolectric.runUiThreadTasksIncludingDelayedTasks()&lt;/code&gt; is now called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Robolectric.flushForegroundScheduler();&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;other&quot;&gt;Other&lt;/h2&gt;
&lt;p&gt;This is what I’ve found so far:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://developer.android.com/reference/android/text/format/Time.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Time&lt;/code&gt;&lt;/a&gt; now &lt;strong&gt;respects&lt;/strong&gt; the default time-zone! So make sure you set it correctly in your unit-tests (most probably in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Before&lt;/code&gt; method):
 &lt;script src=&quot;https://gist.github.com/c45781b8d980f4a60ae3.js?file=TimeTest.java&quot;&gt; &lt;/script&gt;
 Also, note that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Time&lt;/code&gt; is &lt;em&gt;Deprecated&lt;/em&gt; with API-22, so you might want to refactor to use &lt;a href=&quot;http://developer.android.com/reference/java/util/GregorianCalendar.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GregorianCalendar&lt;/code&gt;&lt;/a&gt;. But, no pressure.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Robolectric&lt;/em&gt; now supports API level 21! This means that if you &lt;em&gt;assumed&lt;/em&gt; in your tests that you are running on an API level 18 (which was the highest/default API level for &lt;em&gt;Robolectric&lt;/em&gt; v2.4), this is &lt;strong&gt;no&lt;/strong&gt; longer the case! The default API level is 21!&lt;br /&gt;
 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Config(emulateSdk = 18)&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Config(qualifiers = &quot;v19&quot;)&lt;/code&gt; are your best friends there. And don’t forget to add the missing unit-test for API levels higher than that.&lt;/li&gt;
  &lt;li&gt;Since the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;robolectric&lt;/code&gt; gradle closure is removed, any special Java related configuration (e.g., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maxHeapSize&lt;/code&gt;) should be moved to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android-&amp;gt;testOptions-&amp;gt;unitTests.all&lt;/code&gt; closure. Something like this:
 &lt;script src=&quot;https://gist.github.com/c45781b8d980f4a60ae3.js?file=file-gradle_java_unit_test_config-build&quot;&gt; &lt;/script&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;
Please share your tips and tricks for migrating in the comments! I’ll update this post as things coming in&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Multiple API Level APK Release a.k.a I'm Done With Legacy</title>
   <link href="https://evendanan.net/android/2013/12/10/multi_API_level_release"/>
   <updated>2013-12-10T10:29:00+00:00</updated>
   <id>https://evendanan.net/android/2013/12/10/multi_API_level_release</id>
   <content type="html">&lt;p&gt;Up until recently, &lt;a href=&quot;http://anysoftkeyboard.github.io&quot;&gt;AnySoftKeyboard&lt;/a&gt; was supporting Android devices all the way back to 1.5 in a single APK.
Mostly, it worked fine, and I was able to use &lt;a href=&quot;http://evendanan.net/2011/04/Backward-compatibility-in-Android-OR-How-I-made-a-2-2-Android-APK-run-under-1-5/&quot;&gt;FrankenRobot&lt;/a&gt;
to utilize higher API levels features successfully. But there was a cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL; DR;&lt;/strong&gt;, you can check out &lt;a href=&quot;https://lh6.googleusercontent.com/--sfgL7VjzbE/Uqc_eTbtugI/AAAAAAABVlg/VlDicW7uERY/s1800/multiple_API_versions_releasing.png&quot;&gt;this screen-shot&lt;/a&gt;, which explains a lot.&lt;/p&gt;

&lt;p&gt;Android 1.5 can not support the &lt;a href=&quot;http://developer.android.com/tools/support-library/index.html&quot;&gt;Support Library&lt;/a&gt; (which includes the Fragment support), and Android 1.6 till 2.0 can not support
the App-Compact library (which includes the ActionBar), so providing a single APK for all devices had it cost: no modern UI/UX.&lt;/p&gt;

&lt;h2 id=&quot;separating&quot;&gt;Separating&lt;/h2&gt;
&lt;p&gt;After reading what Google has to &lt;a href=&quot;http://developer.android.com/google/play/publishing/multiple-apks.html&quot;&gt;say&lt;/a&gt; (read it, it is very good background) about it, 
I’ve decided to separate the App by API level: API 3, API 4-6, and API 7+. Why this separation:&lt;/p&gt;

&lt;h3 id=&quot;api-3---android-v15&quot;&gt;API 3 - Android v1.5&lt;/h3&gt;
&lt;p&gt;Devices still running Android 1.5 are very &lt;a href=&quot;http://en.wikipedia.org/wiki/HTC_Dream&quot;&gt;old&lt;/a&gt;. They are very limited on internal storage, RAM and CPU power.
I’ve decided to create a special &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/tree/API3&quot;&gt;build&lt;/a&gt; for those phones: minimal resources (just MDPI), just ARM jni, and removed all code that does not support this API level.
I was able to reduce the size of the APK from 3.2MB to 2.1MB, which is very helpful for owners of those devices.&lt;/p&gt;

&lt;p&gt;This is the &lt;em&gt;AndroidManifest&lt;/em&gt; file for this build:
&lt;script src=&quot;https://gist.github.com/7893053.js?file=AndroidManifest_API3.xml&quot;&gt; &lt;/script&gt;&lt;/p&gt;

&lt;h3 id=&quot;api-4-6---android-v16-v20&quot;&gt;API 4-6 - Android v1.6-v2.0&lt;/h3&gt;
&lt;p&gt;These devices are usually much &lt;a href=&quot;http://en.wikipedia.org/wiki/Motorola_Droid&quot;&gt;better&lt;/a&gt;. But due to OS limitations, they can not support
modern UI language (like the ActionBar). So, here, I’ve decided to also create a separate &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/tree/API4_6&quot;&gt;build&lt;/a&gt; for these API levels. 
Again, reduced set of resources (MDPI and HDPI), a few more jni libs, and reduced set of functionality (e.g., no voice input).&lt;/p&gt;

&lt;p&gt;This is the &lt;em&gt;AndroidManifest&lt;/em&gt; file for this build:
&lt;script src=&quot;https://gist.github.com/7893053.js?file=AndroidManifest_API4.xml&quot;&gt; &lt;/script&gt;&lt;/p&gt;

&lt;h3 id=&quot;api-7---android-v21&quot;&gt;API 7+ - Android v2.1&lt;/h3&gt;
&lt;p&gt;This API level supports all the required support library, and usually, those devices are &lt;a href=&quot;http://en.wikipedia.org/wiki/Samsung_Galaxy_S_II&quot;&gt;very&lt;/a&gt; &lt;a href=&quot;http://en.wikipedia.org/wiki/Nexus_4&quot;&gt;strong&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is the &lt;em&gt;AndroidManifest&lt;/em&gt; file for this build:
&lt;script src=&quot;https://gist.github.com/7893053.js?file=AndroidManifest_API7.xml&quot;&gt; &lt;/script&gt;&lt;/p&gt;

&lt;h3 id=&quot;things-to-note&quot;&gt;Things to Note&lt;/h3&gt;
&lt;p&gt;You’ll notice that for an higher API level, there is an higher &lt;em&gt;versionCode&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;First APK is for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android:minSdkVersion=&quot;3&quot;&lt;/code&gt;, and has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android:versionCode=&quot;112&quot;&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Second APK is for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android:minSdkVersion=&quot;4&quot;&lt;/code&gt;, and has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android:versionCode=&quot;113&quot;&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Third APK is for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android:minSdkVersion=&quot;7&quot;&lt;/code&gt;, and has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android:versionCode=&quot;114&quot;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is required in case the user’s device will get an OS upgrade that requires an APK for an higher API level, 
e.g., say your HTC G1 is running Android 1.5, and then, one glory morning, you are pushed with an OS upgrade to Android 1.6,
prior to the OS upgrade, you have be given &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android:versionCode=&quot;112&quot;&lt;/code&gt;, but after the OS upgrade, this version is no longer compatible with your device.
Don’t worry, Play Store to the rescue! It will automatically update your APK to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android:versionCode=&quot;113&quot;&lt;/code&gt;. And since it needs to &lt;em&gt;update&lt;/em&gt;
your APK, it is required the that new APK will have a higher &lt;em&gt;versionCode&lt;/em&gt;. Got it?&lt;/p&gt;

&lt;h2 id=&quot;play-store-configuration&quot;&gt;Play Store Configuration&lt;/h2&gt;
&lt;p&gt;After you have your APKs built, each with its own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android:minSdkVersion&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android:maxSdkVersion&lt;/code&gt;, you can upload them to the &lt;em&gt;Play Store&lt;/em&gt;.
You should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://lh6.googleusercontent.com/--sfgL7VjzbE/Uqc_eTbtugI/AAAAAAABVlg/VlDicW7uERY/s1000/multiple_API_versions_releasing.png&quot; alt=&quot;Play Store multiple APKs release&quot; title=&quot;Note the versionCode and the API levels&quot; /&gt;&lt;/p&gt;

&lt;p&gt;How’s that for &lt;em&gt;A picture is worth 37.5 tweets!&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;Good luck.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Branding the EdgeEffect aka Hit the Wall with Your Own Color</title>
   <link href="https://evendanan.net/android/branding/2013/12/09/branding-edge-effect"/>
   <updated>2013-12-09T13:07:00+00:00</updated>
   <id>https://evendanan.net/android/branding/2013/12/09/branding-edge-effect</id>
   <content type="html">&lt;p&gt;Lately, I’ve become sort of branding obsessed; colors, font sizes and types, UI language, they all seem &lt;em&gt;so&lt;/em&gt; important to me, 
I can hardly look at an App these days without pulling on my hairs and scream “God, oh God, why would they use inconsistent, non-brand colors!”
Ya, but that’s my problem. Your is another: how to brand the &lt;a href=&quot;http://developer.android.com/reference/android/widget/EdgeEffect.html&quot;&gt;EdgeEffect&lt;/a&gt; glow, ah?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR;&lt;/strong&gt; Use the gist &lt;a href=&quot;https://gist.github.com/menny/7878762#file-brandgloweffect_full-java&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is usually pretty easy to change colors, sizes, etc. for widgets which are in your App’s real estate (e.i., not the OS’s chrome: notification bar icons, for example), but, weird as it sounds, Android does not provide any method to change (or brand) the &lt;em&gt;hitting-the-wall&lt;/em&gt; feedback glow effect in its widget (ListView, GridView, ScrollView, etc.), and will only use the OS’s color.
This even gets weirder, when Android 2.3 has a green edge-effect, while Android 3+ uses a blueish (Holo) color, and KitKat (v4.4) uses a gray glow effect (ironic fact: Android team changed the colors of the chrome in KitKat to a nuetral gray color, so it will not collide with your branding, although, a gray Edge-Effect inside your App’s UI is clearly colliding, IMHO).&lt;/p&gt;

&lt;h2 id=&quot;the-cause&quot;&gt;The Cause&lt;/h2&gt;
&lt;p&gt;The &lt;em&gt;EdgeEffect&lt;/em&gt; class uses a &lt;a href=&quot;https://android.googlesource.com/platform/frameworks/base/+/android-4.4.1_r1/core/res/res/drawable-xxhdpi/overscroll_glow.png&quot;&gt;&lt;em&gt;Drawable&lt;/em&gt;&lt;/a&gt; resource (an OS’s resource, not yours, &lt;em&gt;hint, hint, Google&lt;/em&gt;), which provides the glow effect, this resource is loaded internally
by the EdgeEffect class:
&lt;script src=&quot;https://gist.github.com/7878762.js?file=EdgeEffect_ctor.java&quot;&gt; &lt;/script&gt;&lt;/p&gt;

&lt;p&gt;And the size of the glow (how much the user &lt;em&gt;over-pulled&lt;/em&gt; the list) is calculated during &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draw(Canvas)&lt;/code&gt; call:
&lt;script src=&quot;https://gist.github.com/7878762.js?file=EdgeEffect_draw.java&quot;&gt; &lt;/script&gt;&lt;/p&gt;

&lt;p&gt;There is no way to change the drawable resource (&lt;em&gt;hint one, Google.&lt;/em&gt;), or apply a filter on the drawable (&lt;em&gt;hint two, Google.&lt;/em&gt;). A shame.&lt;/p&gt;

&lt;h2 id=&quot;solution---hacker-style&quot;&gt;Solution - Hacker style&lt;/h2&gt;
&lt;p&gt;What can be done? I’m going to &lt;em&gt;hack&lt;/em&gt; my way into branding this drawable. I’ll take advantage of the fact that the Drawable instance is shared, and not mutate (which makes sense, right?)
and I’ll load that Drawable in my code, and apply a filter on it:
&lt;script src=&quot;https://gist.github.com/7878762.js?file=brandGlowEffect.java&quot;&gt; &lt;/script&gt;&lt;/p&gt;

&lt;h3 id=&quot;what-just-happened&quot;&gt;What just happened?&lt;/h3&gt;
&lt;p&gt;The name of the glow resource has never changed in Android’s history (so far), (line 1) so I’ll get its identification,
(line 2) load the Drawable, which is shared instance across my Application, thus the same one EdgeEffect is using,
(line 3) and apply a MULTIPLY filter on it, with the brand’s color.&lt;/p&gt;

&lt;h3 id=&quot;what-about-the-overscroll_edge-resource&quot;&gt;What about the overscroll_edge resource&lt;/h3&gt;
&lt;p&gt;The edge-effect is broken to two drawables: the glow (&lt;em&gt;overscroll_glow&lt;/em&gt;), and the edge (&lt;em&gt;overscroll_edge&lt;/em&gt;). This method allows the EdgeEffect class to stretch just the glow, and keep the edge crisp.
To brand the edge (you’ll probably want to do that too), use this revised method, which brand both the glow and the edge:
&lt;script src=&quot;https://gist.github.com/7878762.js?file=brandGlowEffect_full.java&quot;&gt; &lt;/script&gt;&lt;/p&gt;

&lt;h2 id=&quot;pitfalls&quot;&gt;Pitfalls&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Android may decide to change the name of the resource, in which case you’ll need to change the call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getIdentifier&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;The shared instance may be unloaded from memory, so remember to call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brandGlowEffect&lt;/code&gt; after each creation of EdgeEffect (after creation of ListView, GridView, ScrollView, or your own instance of EdgeEffect), best place will be in the Activity’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onCreate&lt;/code&gt;, right after setting the layout.&lt;/li&gt;
  &lt;li&gt;I’ll emphasis the first point (since this is the core of the hack, and it’s a &lt;em&gt;reflection&lt;/em&gt; kind of solution, thus dangerous): make sure this method works on the target Android version you had in mind. I take no responsibility!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;branding---the-technical-challenge&quot;&gt;Branding - The Technical Challenge&lt;/h2&gt;
&lt;p&gt;An App should be useful, easy to use, effective, beautiful, stable. It should also be unique, and represent its creator. This is where &lt;em&gt;Branding&lt;/em&gt; comes in -
a way to express and show yourself in the App UI: colors, Type-face and font sizes, language, behavior, icons and graphics, etc.&lt;/p&gt;

&lt;p&gt;I’ll try to post more about this subject, but not from a marketing, design, or decision (reason) standpoint. Instead I’ll show you &lt;em&gt;how&lt;/em&gt;, technically, to brand your App. 
E.g., in this post I’ve shown how to change the color of Android’s build-in Edge-Effect, you can use this method to change the glow color to your brand’s main (or secondary) color! What ever that color maybe (which is a design decision).&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Android Memory Leaks OR Different Ways to Leak</title>
   <link href="https://evendanan.net/2013/02/Android-Memory-Leaks-OR-Different-Ways-to-Leak"/>
   <updated>2013-02-10T12:00:00+00:00</updated>
   <id>https://evendanan.net/2013/02/android-leaks</id>
   <content type="html">&lt;p&gt;A few weeks ago I released &lt;a href=&quot;https://plus.google.com/b/112371284450211079595/112371284450211079595/posts/MXGFUrZK7kV&quot;&gt;version 85&lt;/a&gt; of AnySoftKeyboard and soon after, crash reports started to hit my email box. It usually happens - mostly because I forget to test a feature, or just because the specific device decided it is time to crash (&lt;a href=&quot;http://stackoverflow.com/questions/12732929/android-app-crashes-on-some-random-devices-targetinvocationexception&quot; target=&quot;_blank&quot;&gt;it happens&lt;/a&gt;, really! The floor is crooked). But this time, all the crashes were of the same nature, and were many, about 5-10 a day.&lt;/br&gt;
The crashes were due to &lt;a href=&quot;http://developer.android.com/reference/java/lang/OutOfMemoryError.html&quot; target=&quot;_blank&quot;&gt;OutOfMemoryError&lt;/a&gt; (a.k.a, OOM), which usually means a memory leak in Android (&lt;i&gt;why?&lt;/i&gt; Unlike a desktop, an Android device has very little heap to play with and leaking will fill that little space quickly), I started to look for them.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;&lt;u&gt;TL;DR:&lt;/u&gt;&amp;nbsp;&lt;/b&gt;If I was able to recreate this crash on my devices, I would have find the cause immediately, and just fix the 6th cause in this list. But, I wasn&apos;t able to reproduce it (&lt;i&gt;why?&lt;/i&gt; later on that), and it took me some time to understand that the reporting users want me to fix it, and are willing to help!&lt;/p&gt;

&lt;h2&gt;Cause 1: Context Leak&lt;/h2&gt;
The most common leak in Android is the &lt;a href=&quot;http://developer.android.com/reference/android/content/Context.html&quot; target=&quot;_blank&quot;&gt;Context&lt;/a&gt; leak (here is a great &lt;a href=&quot;http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html&quot; target=&quot;_blank&quot;&gt;explanation&lt;/a&gt;). Although all the examples are talking about Activity leaking, a Service (which AnySoftKeyboard is) has the same problems as Activity in that regard.
&lt;h3&gt;Solution&lt;/h3&gt;
It&apos;s a no-brainer, but tedious: where ever I used a Context object, I made sure that instance is the Application&apos;s Context  - &lt;a href=&quot;http://developer.android.com/reference/android/content/Context.html#getApplicationContext()&quot; target=&quot;_blank&quot;&gt;getApplicationContext()&lt;/a&gt;.

&lt;h2&gt;Cause 2: The Handler Leak OR the Inner Class&lt;/h2&gt;
The &lt;a href=&quot;http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html&quot; target=&quot;_blank&quot;&gt;inner-class&lt;/a&gt; (a.k.a. &lt;i&gt;nested classes&lt;/i&gt;) in Java has an implicit property where it keeps a reference to its outer class, this is why you can call outer-class&apos;s functions in the inner-class scope. As long as the Handler is alive, so is the outer-class, and in my case - the whole Input Method Service, which in turn holds the entire tree!&lt;/br&gt;
One thing to note, once there are no messages in the Handler&apos;s loop, it will go out of reference in the Main Looper, and if there are no other references to it, well, it will be collected, with its implicit outer-class. &lt;i&gt;So what is the problem?&lt;/i&gt; you may ask, and you are right. The problem is that some of the messages are delayed (or just still in queue), and as long as there is a message in the queue, the Handler will not be collected, so even if the OS will decide to kill AnySoftKeyboard (say, orientation change) and create a new one, the old &lt;b&gt;one is still in memory&lt;/b&gt;, and we have two full trees at the same time. Maybe not for long, but enough time to cause OOM on some devices.
&lt;h3&gt;Solution&lt;/h3&gt;
Very simple: gone over all the inner-classes I had (including the Handlers) and made sure they are static classes, and if they required a reference to the the outer-class, I used &lt;a href=&quot;http://developer.android.com/reference/java/lang/ref/WeakReference.html&quot; target=&quot;_blank&quot;&gt;WeakReference&lt;/a&gt;:&lt;/br&gt;
&lt;script src=&quot;https://gist.github.com/menny/4747886.js&quot;&gt;&lt;/script&gt;&lt;/br&gt;
In the code snippet above, I used a WeakReference field to keep a pointer to the main AnySoftKeyboard instance. By using this pattern I was able to call methods of AnySoftKeyboard, but also made sure I was not keeping AnySoftKeyboard from being garbage-collected if needed.&lt;/br&gt;
Do remember that if using this pattern it is required to check that the weak reference is still pointing to something (e.g., not null).

&lt;h2&gt;Cause 3: The Drawable Callback&lt;/h2&gt;
&lt;p&gt;Most of the &lt;a href=&quot;http://developer.android.com/reference/android/graphics/drawable/Drawable.html&quot; target=&quot;_blank&quot;&gt;Drawables&lt;/a&gt; I use are not attached to Views, but some are, and since I keep reference to the drawables objects, this was still a possible leak. &lt;i&gt;Why?&lt;/i&gt; The drawable requires that its client (usually a View) will implement the &lt;a href=&quot;http://developer.android.com/reference/android/graphics/drawable/Drawable.Callback.html&quot; target=&quot;_blank&quot;&gt;Drawable.Callback&lt;/a&gt; interface, which is used by the Drawable to perform animation related tasks. So, if the View is removed from the window/activity, but Drawable is still referenced, then the View is still referenced too, and will not be garbage-collected.&lt;/p&gt;
&lt;h3&gt;Solution&lt;/h3&gt;
I explicitly unbind the drawables when the keyboard&apos;s View is no longer needed:
&lt;script src=&quot;https://gist.github.com/menny/4748000.js&quot;&gt;&lt;/script&gt;
This was, actually, not required in my case since the only reference to the Drawable was the View which is the Callback for the Drawable.&lt;/br&gt;
&lt;a href=&quot;https://github.com/menny/AnySoftKeyboard/commit/28e7fc0899510fc4f231cf8f3429f8ac8e4765f2&quot; target=&quot;_blank&quot;&gt;Commit&lt;/a&gt; (look for changes in &lt;i&gt;AnyKeyboardBaseView.java&lt;/i&gt;).

&lt;h2&gt;Cause 4: The External Context&lt;/h2&gt;
This is special for AnySoftKeyboard, most apps do not reference to an external package&apos;s Context, but since AnySoftKeyboard supports external packages (i.e., language packs, themes, etc.), I was keeping a reference to that external Context always: let&apos;s say you use three languages (not very rare or the users of AnySoftKeyboard), and want to use the &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.anysoftkeyboard.theme.ics&amp;hl=en&quot; target=&quot;_blank&quot;&gt;Ice Cream Sandwich theme&lt;/a&gt;, you&apos;ll end up with &lt;b&gt;four&lt;/b&gt; external Context objects sitting around in AnySoftKeyboard heap. This is quite a waste, since most of the time I use the external Context only once or twice!
&lt;h3&gt;Solution&lt;/h3&gt;
I moved the external from an explicit reference, to a WeakReference, and added the package&apos;s name to class&apos;s fields so I&apos;ll be able to create the context if needed.&lt;/br&gt;
&lt;a href=&quot;https://github.com/menny/AnySoftKeyboard/blob/ba12763e7c6e0d484b6bb4cac777a3ad6297580e/src/com/anysoftkeyboard/addons/AddOnImpl.java&quot; target=&quot;_blank&quot;&gt;Commit&lt;/a&gt;.

&lt;h2&gt;Cause 5: The Too Large Objects&lt;/h2&gt;
&lt;i&gt;Key background, Shift, Control, Enter, Space, Tab, Cancel, Globe, Microphone, Settings, Arrows&lt;/i&gt; and more. All these are Drawables which were loaded into the keyboard View, and were kept in memory. About 15 of them, and some with various states (like the Enter key, which has a Normal, Search, Done, Go, etc states, each a full drawable). That&apos;s not a leak, but it is still a lot of memory for some devices - HTC Desire will not allow more than 32MB of heap, for example.
&lt;h3&gt;Solution&lt;/h3&gt;
I created a &lt;a href=&quot;https://github.com/menny/AnySoftKeyboard/blob/master/src/com/anysoftkeyboard/keyboards/views/DrawableBuilder.java&quot; target=&quot;_blank&quot;&gt;DrawableBuilder&lt;/a&gt; class which have all the information required to get the drawable. This way, if the layout does not use a specific icon, it will not be loaded into memory.&lt;/br&gt;
&lt;a href=&quot;https://github.com/menny/AnySoftKeyboard/commit/6ea97d8055875dfe1217bc6d6f0fb2e74e63f2c8&quot; target=&quot;_blank&quot;&gt;Commit&lt;/a&gt;.

&lt;h2&gt;Cause 6: The Database Connection&lt;/h2&gt;
After I fixed all the issues above, the app was still crashing on some devices. I had no idea where to look anymore, I was sure there was a leak, and it very elusive, maybe even vendor specific (although the crashes came from many vendors, and various OS versions), so I added to the &lt;a href=&quot;http://developer.android.com/reference/java/lang/Thread.html#setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)&quot; target=&quot;_blank&quot;&gt;UncaughtExceptionHandler&lt;/a&gt; a code that check of the thrown Exception type, and if it is OutOfMemoryError, I asked the framework do create a &lt;a href=&quot;http://developer.android.com/reference/android/os/Debug.html#dumpHprofData(java.lang.String)&quot; target=&quot;_blank&quot;&gt;memory dump&lt;/a&gt;:&lt;/br&gt;
&lt;script src=&quot;https://gist.github.com/4689035.js&quot;&gt;&lt;/script&gt;
I found the leak easily using &lt;a href=&quot;http://www.eclipse.org/mat/&quot; target=&quot;_blank&quot;&gt;Eclipse Memory Analyzer (a.k.a, MAT)&lt;/a&gt;: the leak was a Database Connection Transport, and a huge one, each such transport used 0.5MB (for that user - since he had a large user-dictionary), and it leaked every time the user switched language! It was leaking due to a race-condition.
&lt;h3&gt;The Race Condition&lt;/h3&gt;
When a dictionary is created, it loads its words list using an &lt;a href=&quot;http://developer.android.com/reference/android/os/AsyncTask.html&quot; target=&quot;_blank&quot;&gt;AsyncTask&lt;/a&gt; (so it wont hold up the UI thread), but when the list is long and the device is slow, the dictionary&apos;s &lt;i&gt;close&lt;/i&gt; method (which is called when the language changes) may be called before the loading is done, hence not closing anything!
&lt;p&gt;
So, if it happens always why haven&apos;t I recreated it on my devices? Some flavors (Samsung devices and &lt;a href=&quot;http://www.cyanogenmod.org/&quot; target=&quot;_blank&quot;&gt;CyanogenMod&lt;/a&gt;) could handle that release automatically (there is a finalizer in Java, you know), and they did. Some vendors did not. I had a Samsung, a Motorola and a device running CyanogenMod. Moreover, my devices are fast just enough, and used a small words list. Bummer, ah? Ya..
&lt;/p&gt;
&lt;h3&gt;Solution&lt;/h3&gt;
Just closed the database connection: by ensuring the related functions use monitors.&lt;/br&gt;
&lt;a href=&quot;https://github.com/menny/AnySoftKeyboard/commit/de9dcaa6b57f8b74d1f2a067b745ab3315997b27&quot; target=&quot;_blank&quot;&gt;Commit&lt;/a&gt; and &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard/commit/c2d74fff434ef9bd05d051a20d4b117b553928df&quot; target=&quot;_blank&quot;&gt;commit&lt;/a&gt;.

&lt;h2&gt;The End&lt;/h2&gt;
&lt;a href=&quot;https://plus.google.com/112371284450211079595/posts/FmmRc8EpthF&quot;&gt;v95&lt;/a&gt;</content>
 </entry>
 
 <entry>
   <title>In App Purchase Verification using PHP OR Making sure you ain't getting ripped off, PHP style!</title>
   <link href="https://evendanan.net/2012/03/In-App-Purchase-Verification-using-PHP-OR-Making-sure-you-ain-t-getting-ripped-off-PHP-style"/>
   <updated>2012-03-01T12:00:00+00:00</updated>
   <id>https://evendanan.net/2012/03/gettting-paid-php-style</id>
   <content type="html">&lt;p&gt;At&amp;nbsp;&lt;a href=&quot;http://www.speakingpal.com/&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;SpeakingPal&lt;/a&gt;&amp;nbsp;we offer our&amp;nbsp;&lt;a href=&quot;https://market.android.com/details?id=com.speakingpal.speechtrainer.sp&quot;&gt;apps&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;http://itunes.apple.com/us/app/speakingpal-english-tutor/id437219346?mt=8&amp;amp;uo=4&quot; title=&quot;&quot; target=&quot;&quot;&gt;for&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;https://market.android.com/details?id=com.speakingpal.speechtrainer.ets.ets&quot; title=&quot;&quot; target=&quot;&quot;&gt;free&lt;/a&gt;, and provide customers ways to extend content availability via in-app purchase.&lt;/p&gt;

&lt;p&gt;In our case, in-app purchase is build from two parts: the first, is the client&apos;s &amp;nbsp;integration with the device&apos;s apps services (AppStore in iOS, Market in Android), the second is the server-side verification of the in-app receipt, avoiding trickery of the&amp;nbsp;&lt;a href=&quot;http://jailbreakstory.com/2011/09/crack-in-app-purchases-in-ios-with-iap-cracker/&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;client&apos;s app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Client-side code samples are in&amp;nbsp;abundance, and usually are very well documented (well, of course, the OS vendor wants to get paid, no?). But the other side, the server-side, is not well documented and lack samples, although considered a&amp;nbsp;&lt;a href=&quot;http://developer.android.com/guide/market/billing/billing_best_practices.html&quot; title=&quot;&quot; target=&quot;&quot;&gt;best-practice&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Android&lt;/h3&gt;
&lt;p&gt;Android dev-site suggests making the in-app&amp;nbsp;verification&amp;nbsp;at the server-side as safer, but does not provide any code sample.&lt;br&gt;Google &lt;a href=&quot;http://developer.android.com/guide/market/billing/billing_overview.html&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;method&lt;/a&gt; of in-app verification is via digital signatures verification: You provide a nonce, they give you a receipt with that nonce prefixed and a&amp;nbsp;&lt;a href=&quot;http://en.wikipedia.org/wiki/RSA_(algorithm)#Signing_messages&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;RSA-SHA1&lt;/a&gt;&amp;nbsp;signature on that text. The key used for signing is defined in your Market developer console. With that public key, the signed receipt and the signature, you can verify that the receipt is real, that is, produced by the Market service.&lt;/p&gt;
&lt;p&gt;Verifying an&amp;nbsp;&lt;a href=&quot;http://php.net/manual/en/function.openssl-verify.php&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;RSA-SHA1 signature is simple in PHP&lt;/a&gt;, but tricky. Here is a&amp;nbsp;&lt;i&gt;working&lt;/i&gt;&amp;nbsp;code:&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/1985010.js?file=verify_market_in_app.php&quot;&gt;&lt;/script&gt;

&lt;h3&gt;iOS&lt;/h3&gt;
&lt;p&gt;Apple went with something else; verifying the AppStore receipt is done by asking iTunes&apos; servers whether a receipt is real: after in-app is done, device&apos;s AppStore service gives you a base64 encoded receipt, which you&apos;ll send to iTunes&apos; servers, and get a json response about the validity of the receipt. Here&apos;s a snippet:&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/1985010.js?file=verify_appstore_in_app.php&quot;&gt;&lt;/script&gt;

&lt;h3&gt;PayPal&lt;/h3&gt;
&lt;p&gt;PayPal actually provide all the information you need, in several programming languages, i.e.,&amp;nbsp;&lt;a href=&quot;https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&amp;amp;content_ID=developer/e_howto_admin_IPNImplementation&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;IPN verification&lt;/a&gt;. I see no reason to paste here snippets.&lt;/p&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Font Metrics in Java OR How, the hell, Should I Position This Font?!</title>
   <link href="https://evendanan.net/2011/12/Font-Metrics-in-Java-OR-How-the-hell-Should-I-Position-This-Font"/>
   <updated>2011-12-19T12:00:00+00:00</updated>
   <id>https://evendanan.net/2011/12/font-metrics</id>
   <content type="html">&lt;p&gt;I&apos;m working on a new feature for &lt;a href=&quot;https://market.android.com/details?id=com.menny.android.anysoftkeyboard&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;AnySoftKeyboard&lt;/a&gt;,&amp;nbsp;where it will &apos;hint&apos; the user about what lays behind long-pressing a key.&lt;/p&gt;

&lt;p&gt;I&apos;ve decided that the best UX I can provide, is to draw a small character, at the bottom-right corner of the key, showing the long-press output, or &quot;...&quot; in case there are lots of them. It will probably look like this:&lt;br/&gt;
&lt;img src=&quot;https://lh5.googleusercontent.com/-5S4erP83MQ8/Tu8RPs7XVbI/AAAAAAAAg-Q/IL4sbnNJy-8/s288/screenshot-1324290297408.jpg&quot; alt=&quot;hinting hinting HO, cool&quot; title=&quot;hinting hinting HO, cool&quot; align=&quot;none&quot;&gt;&lt;br/&gt;
&lt;/p&gt;

&lt;p&gt;So, I had to figure out how to position the character there. Java/Android provides a nice service called &lt;a href=&quot;http://developer.android.com/reference/android/graphics/Paint.FontMetrics.html&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;FontMetrics&lt;/a&gt;, which provide general metrics on the font, 
like what is the tallest glyph (character), lowest glyph, etc.&lt;/p&gt;

&lt;p&gt;Since the hint is very small, I had to make sure the position is precised. So I&apos;ve dived into the definitions of those metrics, 
and their values, and cooked up a small &lt;a href=&quot;https://github.com/menny/FontMetrics-Visualization/downloads&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;Android app&lt;/a&gt; which
visualizing the metrics, and since an image is worth 37.5 tweets, I give you a screenshot:&lt;br/&gt;
&lt;img src=&quot;https://lh4.googleusercontent.com/-NvhvlDvSlds/Tu8Nf4sx3EI/AAAAAAAAg9w/-o7zBGD0rCg/s800/font_metrics_screenshot.png&quot; alt=&quot;An image is worth 37.5 tweets&quot; title=&quot;An image is worth 37.5 tweets&quot;&gt;
&lt;/p&gt;
&lt;p&gt;Here is FontMetrics in layman lingo:
&lt;ul&gt;
  &lt;li&gt;&lt;u&gt;baseline:&lt;/u&gt;&amp;nbsp;the base of your font drawing. Your text will be drawn ABOVE this X.&lt;/li&gt;
  &lt;li&gt;&lt;u&gt;top:&lt;/u&gt;&amp;nbsp;the maximum distance above the baseline for the tallest glyph. Since it is ABOVE the baseline, it is a negative value!&lt;/li&gt;
  &lt;li&gt;&lt;u&gt;ascent:&lt;/u&gt;&amp;nbsp;this is the recommended space above the baseline you should make for a line of this text. Again, above the baseline, so negative.&lt;/li&gt;
  &lt;li&gt;&lt;u&gt;descent:&lt;/u&gt;&amp;nbsp;this is the recommended space below the baseline you should keep for text, since some characters (e.g, y) are drawn some below the baseline.&lt;/li&gt;
  &lt;li&gt;&lt;u&gt;bottom:&lt;/u&gt;&amp;nbsp;this is the lowest glyph space below the baseline.&lt;/li&gt;
  &lt;li&gt;&lt;u&gt;leading:&lt;/u&gt; I haven&apos;t included that in the image (or the app) since it is usually 0. But, to make the post complete, &lt;i&gt;leading&lt;/i&gt; is the recommended extra space to add between two line of text. I think that the reason that it is usually defined as none, is because &lt;i&gt;bottom&lt;/i&gt; (or &lt;i&gt;top&lt;/i&gt;) takes care of the extra space needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Fingerpainting app for Hagar OR Multitouch sample code</title>
   <link href="https://evendanan.net/2011/08/Fingerpainting-app-for-Hagar-OR-Multitouch-sample-code"/>
   <updated>2011-08-03T12:00:00+00:00</updated>
   <id>https://evendanan.net/2011/08/hagars-fingerpainting</id>
   <content type="html">&lt;p&gt;In preparation for a five hours flight with my year and a half daughter Hagar, I thought of all kind of toys I could bring along so the confined space wont drive her nuts; 
I brought a dull (a.k.a Julia), and a toy&amp;nbsp;stroller for Julia &lt;i&gt;(fun fact: in the UK, stroller is called &lt;a href=&quot;http://www.yourdictionary.com/pushchair&quot; title=&quot;Pushchair&quot; target=&quot;_blank&quot;&gt;&apos;pushchair&apos;&lt;/a&gt;)&lt;/i&gt;&amp;nbsp;,
 some books, and a rattle. I also wanted to bring papers and crayons for her to paint with, but didn&apos;t want to totally vandalize the airplane :)&lt;/p&gt;
 
&lt;p&gt;So, what better than a fingerpainting app for Android to play with while on the flight?&lt;/p&gt;

&lt;p&gt;A free, fun, simple and multi-touch supporting app in the Market is not an easy thing to find, and after noticing that Google provides &lt;a href=&quot;http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/FingerPaint.html&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;sample code&lt;/a&gt; for such app, I decided to build one by myself.&lt;/p&gt;

&lt;p&gt;It took an hour to improve Google&apos;s sample code to include multi-touch. And about five minutes of Hagar&apos;s &lt;a href=&quot;http://developer.android.com/tools/help/monkeyrunner_concepts.html&quot;&gt;playing&lt;/a&gt; with it to notice that I had a bug there! I didn&apos;t handle the pointers ID correctly. Fixing wasn&apos;t too difficult, 
and pretty much straight-forward, but I wanted to share it, mostly because non of the posts I found about multi-touch noted it.&lt;/p&gt;

&lt;h3&gt;Technical stuff*&lt;/h3&gt;
&lt;p&gt;&lt;small&gt;*For simplicity, I&apos;m only talking about API 8.&lt;/small&gt;&lt;br/&gt;
Android&apos;s &lt;a href=&quot;http://developer.android.com/reference/android/view/MotionEvent.html&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;MotionEvent&lt;/a&gt; will be delivered to the &lt;i&gt;onTouchEvent&lt;/i&gt; function on event Up, Down, and Move events; where Up and Down will 
provide information about the relevant pointer only, and Move will provide information about ALL active pointers. 
In other words, when you get a &lt;i&gt;MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN, MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP&lt;/i&gt;&amp;nbsp;action from&amp;nbsp;&lt;i&gt;getActionMasked&lt;/i&gt; 
function you should only handle the specific pointer (given by &lt;i&gt;getActionIndex&lt;/i&gt;) , and when you get a&amp;nbsp;&lt;i&gt;MotionEvent.ACTION_MOVE&lt;/i&gt; action, you should handle ALL pointers in the event. 
Also, it is important to notice that each pointer has an index (the index of the pointer in the event&apos;s data list - &lt;b&gt;this may change!&lt;/b&gt;), 
and an ID (which &lt;b&gt;will not change&lt;/b&gt; for the life time of touch point).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@Override
    public boolean onTouchEvent(MotionEvent event) {

    	switch (event.getActionMasked()) {

        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_POINTER_DOWN:
        	touch_start(event.getX(event.getActionIndex()), event.getY(event.getActionIndex()), event.getPointerId(event.getActionIndex()));
            invalidate();
            break;

        case MotionEvent.ACTION_MOVE:
        	for(int pointerIndex=0; pointerIndex&amp;lt;event.getPointerCount(); pointerIndex++)
        	{
        		final int pointerId = event.getPointerId(pointerIndex);
        		float pointerX = event.getX(pointerIndex);
                float pointerY = event.getY(pointerIndex);
                
                touch_move(pointerX, pointerY, pointerId);
                invalidate();
        	}
            break;

        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_POINTER_UP:
            touch_up(event.getPointerId(event.getActionIndex()));
            invalidate();
            break;
    	}
        
        return true;
    }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the code above:
&lt;ul&gt;
  &lt;li&gt;On&amp;nbsp;&lt;i&gt;ACTION_DOWN:&lt;/i&gt;&amp;nbsp;I created a new &lt;a href=&quot;http://developer.android.com/reference/android/graphics/Path.html&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;Path&lt;/a&gt; object associated with the pointer&apos;s ID (&lt;i&gt;event.getPointerId(event.getActionIndex())&lt;/i&gt;)&lt;/li&gt;
  &lt;li&gt;On&amp;nbsp;&lt;i&gt;ACTION_MOVE:&lt;/i&gt;&amp;nbsp;I added a path point for ALL pointers.&lt;/li&gt;
  &lt;li&gt;On&amp;nbsp;&lt;i&gt;ACTION_UP:&lt;/i&gt;&amp;nbsp;I removed the pointer (by its ID) from the tracked pointers map.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Notes:&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Always reference a pointer to its ID! Index is only used for retrieving pointer information (like its X, Y coordinates) from the MotionEvent object.&lt;/li&gt;
  &lt;li&gt;Up and Down actions should be used to start/stop tracking code (I&apos;m using it to create a Path object and to associate a color to the pointer)&lt;/li&gt;
  &lt;li&gt;Move action&apos;s object contain information about ALL active pointers. You can get the count of active pointers using the &lt;i&gt;getPointerCount&lt;/i&gt; function. You should iterate ALL active pointers since Android will not tell you which pointer has moved, and furthermore, the event will be called at a regular intervals, so sometimes you may notice that there are no changes in the pointers&apos; coordinates and some times more than one pointer will have new coordinates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Epilogue&lt;/h3&gt;
As always, you can find my code at &lt;a href=&quot;https://github.com/menny&quot; title=&quot;My GitHub profile&quot; target=&quot;_blank&quot;&gt;github&lt;/a&gt; account.</content>
 </entry>
 
 <entry>
   <title>My Boxee Remote OR How Cool is Open Source!</title>
   <link href="https://evendanan.net/2011/07/My-Boxee-Remote-OR-How-Cool-is-Open-Source"/>
   <updated>2011-07-12T12:00:00+00:00</updated>
   <id>https://evendanan.net/2011/07/boxee-remote</id>
   <content type="html">&lt;p&gt;For the last year I&apos;ve been using &lt;a href=&quot;http://www.boxee.tv&quot; target=&quot;_blank&quot;&gt;Boxee&lt;/a&gt; as my media center application, and it rulez!!11! 
I encourage you all to try it out. But this is not the point of this post. I&apos;m going to talk about remote controlling Boxee.&lt;/p&gt;

&lt;p&gt;My Boxee is installed on a Linux machine, and I controlled it with &lt;a href=&quot;https://market.android.com/details?id=net.supware.boxee&quot; target=&quot;_blank&quot;&gt;Supware&apos;s Boxee Remote&lt;/a&gt;, a 
simple gesture based remote control: swipes (navigating) or a tap (select). As you can understand, you don&apos;t need to look at the remote UI, or to remember where each action button is located on the 
UI: just swipe! And with Boxee exceptional UI/UX the whole experience has totally won the &lt;a href=&quot;http://en.wikipedia.org/wiki/Wife_acceptance_factor&quot; target=&quot;_blank&quot;&gt;WAF!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a few months everything went great. Some remote crashes from time to time, but all in all, it was good. 
And then my curious side got the best of me: I found out that &lt;a href=&quot;http://www.supware.net/&quot;&gt;Supware&apos;s&lt;/a&gt; remote is actually &lt;a href=&quot;http://boxeeremote.googlecode.com&quot;&gt;open-sourced&lt;/a&gt;, which was the incentive to start hacking it. 
I fixed a few &lt;a href=&quot;http://code.google.com/p/boxeeremote/source/detail?r=74&quot; target=&quot;_blank&quot;&gt;issues&lt;/a&gt;, and added some too and sent a patch to Supware. But it was not integrated very well, mostly because the guy had a new daughter (&lt;i&gt;CONGRATS!&lt;/i&gt;), and had very little time to play with a working project.&lt;/p&gt;

&lt;h3&gt;Open Source rulez!&lt;/h3&gt;
&lt;p&gt;How hard is it to write my own Boxee remote? &lt;b&gt;Not so much!&lt;/b&gt;&lt;br&gt;
I took Supware&apos;s &lt;a href=&quot;http://code.google.com/p/boxeeremote/source/browse/&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;code&lt;/a&gt; for reference, browsed Boxee&apos;s official iPhone remote control &lt;a href=&quot;http://www.boxee.tv/&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;code&lt;/a&gt;, 
browsed XBMC &lt;a href=&quot;ftp://ohnopublishing.net/distfiles/svn-src/xbmc/trunk/xbmc/lib/libGoAhead/XBMChttp.cpp&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;code&lt;/a&gt; and &lt;a href=&quot;http://wiki.xbmc.org/index.php?title=Web_Server_HTTP_API&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;API&lt;/a&gt; and read Boxee 
&lt;a href=&quot;http://developer.boxee.tv/Remote_Control_Interface#Remote_Control_Interface&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;remote API&lt;/a&gt;.&lt;br&gt;Coded for a few hours (took base64 code from &lt;a href=&quot;http://iharder.net/base64&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;iharder&lt;/a&gt;), 
and that&apos;s it! I have my own version!&lt;/p&gt;

&lt;h3&gt;Features&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Automatically detecting your Boxee server&lt;/li&gt;
  &lt;li&gt;Navigate via swipe gestures&lt;/li&gt;
  &lt;li&gt;Selecting via tapping&lt;/li&gt;
  &lt;li&gt;Running media poster as remote background&lt;/li&gt;
  &lt;li&gt;Media control buttons (just for fun)&lt;/li&gt;
  &lt;li&gt;Notification icon when media is playing (showing media title, and allow quick return to the remote activity upon clicking it)&lt;/li&gt;
  &lt;li&gt;Pausing running media upon phone call&lt;/li&gt;
  &lt;li&gt;And probably more!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Currently, this remote App is &lt;span class=&quot;strikethrough&quot;&gt;not published to the Market&lt;/span&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=net.evendanan.android.thumbremote&quot;&gt;here&lt;/a&gt;. It is available at my &lt;a href=&quot;https://github.com/menny/Boxee-Thumb-Remote&quot; title=&quot;&quot; target=&quot;_blank&quot;&gt;Github&lt;/a&gt; account.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Backward compatibility in Android OR How I made a 2.2 Android APK run under 1.5!</title>
   <link href="https://evendanan.net/2011/04/Backward-compatibility-in-Android-OR-How-I-made-a-2-2-Android-APK-run-under-1-5"/>
   <updated>2011-04-27T12:00:00+00:00</updated>
   <id>https://evendanan.net/2011/04/pre-frankenrobot</id>
   <content type="html">&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt; I&apos;ve refined the process, and produced an Android jar which can be used in your project to achieve the resource-qualifier based injection! Check it out at &lt;a href=&quot;https://github.com/menny/FrankenRobot&quot; target=&quot;_blank&quot;&gt;FrankenRobot project page&lt;/a&gt;!.&lt;/p&gt;

&lt;p&gt;As the the maintainer of &lt;a href=&quot;http://softkeyboard.googlecode.com&quot; target=&quot;_blank&quot;&gt;AnySoftKeyboard&lt;/a&gt;, I&apos;m obligated to create a product which works flawlessly on various Android OS versions, starting with 1.5, and to provide advanced features as the underlying OS provides. Luckily, the Android platform provides several ways to create products which are backward compatible, e.g., custom resources per API version and Java&apos;s reflection.
&lt;/p&gt;
&lt;p&gt;
In this post I&apos;ll show how backward compatibility is done in AnySoftKeyboard, and how advanced features like multi-touch, &lt;a href=&quot;http://developer.android.com/reference/android/app/backup/BackupAgent.html&quot; target=&quot;_blank&quot;&gt;Backup Agent&lt;/a&gt;, Contacts dictionary, and more, are consumed without crashing the application due to &lt;a href=&quot;http://download.oracle.com/javase/1.4.2/docs/api/java/lang/ClassNotFoundException.html&quot; target=&quot;_blank&quot;&gt;ClassNotFoundException&lt;/a&gt; or &lt;a href=&quot;http://download.oracle.com/javase/1.4.2/docs/api/java/lang/NoSuchMethodException.html&quot; target=&quot;_blank&quot;&gt;NoSuchMethodException&lt;/a&gt;.
&lt;/p&gt;
&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;
The main Android feature I use for achieving backward compatibility, is the ability to &lt;a href=&quot;http://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResources&quot; target=&quot;_blank&quot;&gt;provide API level specific resources&lt;/a&gt;, along with a nifty software engineering (a.k.a, object oriented programming). Java&apos;s reflection is also used, but scarcely.
&lt;/p&gt;
&lt;h2&gt;Practice&lt;/h2&gt;
&lt;h3&gt;Contacts dictionary checkbox&lt;/h3&gt;
&lt;p&gt;
The ability to query the &lt;a href=&quot;http://developer.android.com/reference/android/provider/ContactsContract.Contacts.html&quot; target=&quot;_blank&quot;&gt;contacts list in Android was introduced in version 2 (a.k.a API Level 5)&lt;/a&gt;, so I wanted to limit its settings in devices with API Level 3 (OS 1.5) and 4 (OS 1.6).&lt;br/&gt;
This was done by providing a specific dictionary settings layout file for devices running API 5 (where the &apos;Contacts dictionary&apos; checkbox is enabled) and a different layout file for devices running API 4 or 3  (where the &apos;Contacts dictionary&apos; checkbox is disabled, and marked off).&lt;br/&gt;
&lt;/p&gt;
&lt;img src=&quot;https://lh4.googleusercontent.com/_YI66aqZG43M/TbbTBpwjIGI/AAAAAAAAbJc/pK9ucWFs6JU/s800/dictionary_pref.jpg&quot;/&gt;&lt;br/&gt;
&lt;p&gt;In the image above, you see two folders, one for API 4 and the other for API 5. When inflating the Dictionary Settings layout, the file &lt;i&gt;&apos;prefs_dictionaries.xml&apos;&lt;/i&gt; is searched. Android will locate the best match starting with the device&apos;s API level, and then searching for lower API levels, so, if my device is API 6, Android will look for &lt;i&gt;&apos;prefs_dictionaries.xml&apos;&lt;/i&gt; under &lt;i&gt;layout-v6&lt;/i&gt; and since it is not there, it will look under &lt;i&gt;layout-v5&lt;/i&gt;. In my case, it will stop there since the file was found, but if it wasn&apos;t there, it would have continue the search. In API 4 devices, it will start with &lt;i&gt;layout-v4&lt;/i&gt; folder.&lt;/p&gt;
&lt;p&gt;In API 4 &lt;i&gt;&apos;prefs_dictionaries.xml&apos;&lt;/i&gt; I&apos;ve disabled the dictionary by including this XML layout:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;CheckBoxPreference
		android:key=&amp;quot;@string/settings_key_use_contacts_dictionary&amp;quot;
		android:title=&amp;quot;@string/use_contacts_dictionary&amp;quot;
		android:persistent=&amp;quot;true&amp;quot;
		android:defaultValue=&amp;quot;false&amp;quot;
		android:summaryOn=&amp;quot;@string/use_contacts_dictionary_not_supported_summary&amp;quot;
		android:summaryOff=&amp;quot;@string/use_contacts_dictionary_not_supported_summary&amp;quot;
		android:clickable=&amp;quot;false&amp;quot; android:selectable=&amp;quot;false&amp;quot; android:enabled=&amp;quot;false&amp;quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;While in API 5 &lt;i&gt;&apos;prefs_dictionaries.xml&apos;&lt;/i&gt; I&apos;ve enabled it with this XML layout:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;CheckBoxPreference
		android:key=&amp;quot;@string/settings_key_use_contacts_dictionary&amp;quot;
		android:title=&amp;quot;@string/use_contacts_dictionary&amp;quot;
		android:persistent=&amp;quot;true&amp;quot;
		android:defaultValue=&amp;quot;@bool/settings_default_contacts_dictionary&amp;quot;
		android:summaryOn=&amp;quot;@string/use_contacts_dictionary_on_summary&amp;quot;
		android:summaryOff=&amp;quot;@string/use_contacts_dictionary_off_summary&amp;quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt; 

&lt;h3&gt;Keyboard view&lt;/h3&gt;
&lt;p&gt;
The Keyboard View is the main UI and input object in AnySoftKeyboard: in some Android versions it should provide multitouch capabilities, in some should not, in some it should query the motion events in one way, in others in a different way.&lt;br/&gt;
To get this done, I&apos;ve used a nifty trick: I use the Android&apos;s resources tree decision mechanism (the same mechanism I used in the dictionary settings layout inflation) to create an object which encapsulate the required behavior. The difference in behavior are:
&lt;ol&gt;
&lt;li&gt;Multitouch capability detection.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://developer.android.com/reference/android/view/MotionEvent.html&quot; target=&quot;_blank&quot;&gt;MotionEvent&lt;/a&gt; query functions.&lt;/li&gt;
&lt;li&gt;and &lt;a href=&quot;http://developer.android.com/reference/android/view/GestureDetector.html&quot; target=&quot;_blank&quot;&gt;GestureDetector&lt;/a&gt; creation.&lt;/li&gt;
&lt;/ol&gt;
These functions are grouped together using the &lt;i&gt;DeviceSpecific&lt;/i&gt; interface. API levels 3, 5, 7 and 8 require different behavior, so I created four classes which implement that interface. The concrete class is created by inflating a API level specific layout, which I called &lt;i&gt;FactoryView&lt;/i&gt;, and then calling a factory method in that view:&lt;br/&gt;
Here is a simple class diagram to explain what I mean:&lt;br/&gt;
&lt;img src=&quot;https://lh3.googleusercontent.com/_YI66aqZG43M/Tbe4pWEW3pI/AAAAAAAAbLA/79FIxI5qceY/s640/AnySoftKeyboardDeviceSpecificclassdiagram.jpg&quot;/&gt;&lt;br/&gt;
My resources tree looks like this:&lt;br/&gt;
&lt;img src=&quot;https://lh3.googleusercontent.com/_YI66aqZG43M/TbbaE3SRb7I/AAAAAAAAbJ0/w55j9aZzFcs/s800/device_specific_tree.jpg&quot; /&gt;&lt;br/&gt;
The &lt;i&gt;device_specific.xml&lt;/i&gt; file inflates the required FactoryView, which in turn used to create the concrete &lt;i&gt;DeviceSpecific&lt;/i&gt; instance.&lt;br/&gt;
This inflation is done in the application onCreate method, which ensures that the &lt;i&gt;DeviceSpecific&lt;/i&gt; instance exists before anything requires it.&lt;br/&gt;
Upon request for a layout inflation, Android will search for the most suitable layout, e.g., if I&apos;m running under Android 1.5, which is API level 3, Android will first look for the requested layout under the folder &apos;layout-v3&apos;, and if it finds it there, it will load it, if not, it will search for it under &apos;layout&apos;. If I&apos;m running Android 2.2, it will search for the layout under the folder &apos;layout-v8&apos;, then &apos;layout-v7&apos; and so on, till the default folder.&lt;br/&gt;
Each of those layout files points to a different class, e.g., &apos;layout-v8/device_specific.xml&apos; points to &lt;i&gt;&apos;com.anysoftkeyboard.devicespecific.FactoryView_V8&apos;&lt;/i&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;com.anysoftkeyboard.devicespecific.FactoryView_V8
        xmlns:android=&amp;quot;http://schemas.android.com/apk/res/android&amp;quot;
        android:id=&amp;quot;@+id/DeviceSpecific&amp;quot;
        android:layout_width=&amp;quot;fill_parent&amp;quot;
        android:layout_height=&amp;quot;fill_parent&amp;quot;
        /&amp;gt;
&lt;/code&gt;&lt;/pre&gt; 
&lt;i&gt;FactoryView_V8&lt;/i&gt; instantize &lt;i&gt;DeviceSpecific_V8&lt;/i&gt; which includes code conforms to API level 8. What did I achieve from this? The ability to use higher API level MotionEvent methods, and query functions! The higher the level, the more functionality I can use. In v3 I can only get X,Y and event type (up, down, move, etc) while in v8 I can also get pointer ID, better multitouch capability detection and more.
&lt;/p&gt;
&lt;p&gt;Note that I haven&apos;t used ANY Java&apos;s &lt;a href=&quot;http://code.google.com/p/android-smspopup/source/browse/trunk/SMSPopup/src/net/everythingandroid/smspopup/wrappers/ContactWrapper.java?r=312&quot; target=&quot;_blank&quot;&gt;reflection&lt;a/&gt; &lt;a href=&quot;http://stackoverflow.com/questions/4552250/how-to-support-multiple-android-version-in-your-code&quot; target=&quot;_blank&quot;&gt;methods&lt;/a&gt;, or &lt;a href=&quot;http://android-developers.blogspot.com/2010/07/how-to-have-your-cupcake-and-eat-it-too.html&quot; target=&quot;_blank&quot;&gt;lazy loading technics (which are implementation specific!)&lt;/a&gt;. I find my method to be much more elegant.&lt;/p&gt;

&lt;h3&gt;Tap sound&lt;/h3&gt;
&lt;p&gt;In AnySoftKeyboard, the user can select enabling click sound upon pressing a key (this option is off by default). Upon a key press, the keyboard will take the sound level of the system, and use it to produce the click sound.&lt;br/&gt;
In pre-Eclair times, the sound leel was [0..8], while in post-Eclair it is [0..1]. To support this API change, I query the OS for its API Level, and performs the necessary calculation according to the API level:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//pre-eclair
// volume is between 0..8 (float)
//eclair
// volume is between 0..1 (float)
if (Workarounds.getApiLevel() &amp;gt;= 5)
{
	fxVolume = ((float)volume)/((float)maxVolume);
}
else
{
	fxVolume = 8*((float)volume)/((float)maxVolume);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&apos;s it.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By leveraging on Android&apos;s API Level specific resource mechanism, I was able to easily provide a keyboard for Android devices running 1.5 up to 2.2 or higher with no ugly reflection code (which may break), and no lazy loading technics (which may not work in some optimized Java implementations), and still use advance system functions as the OS provides.&lt;br/&gt;
You can always see the source code of AnySoftKeyboard &lt;a href=&quot;https://github.com/AnySoftKeyboard/AnySoftKeyboard&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.&lt;br/&gt;
&lt;h3&gt;Notes&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Android 1.5 and 1.6 only match resources with this qualifier when it exactly matches the platform version. This is a &lt;a href=&quot;http://developer.android.com/guide/topics/resources/providing-resources.html#KnownIssues&quot; target=&quot;_blank&quot;&gt;known issue&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Providing density specific resources (i.e., &lt;i&gt;drawable-hdpi&lt;/i&gt;) is not supported in  Android 1.5, so make sure you provide the default too (i.e., &lt;i&gt;drawable&lt;/i&gt;). The default resources are considered as mdpi,  normal qualifiers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Good luck.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Creating videos for Android devices</title>
   <link href="https://evendanan.net/2011/04/Creating-videos-for-Android-devices"/>
   <updated>2011-04-05T12:00:00+00:00</updated>
   <id>https://evendanan.net/2011/04/android-video-encoding</id>
   <content type="html">&lt;p&gt;Encoding the perfect video for mobile devices is always hard: you want a small sized file, with high quality, and a codec which will work on all devices, no matter how &lt;a href=&quot;http://www.zdnet.com/blog/open-source/androids-biggest-worry-fragmentation/8022&quot; target=&quot;_blank&quot;&gt;fragmented the platform is&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Android supports a very nice set of &lt;a href=&quot;http://developer.android.com/guide/appendix/media-formats.html&quot; target=&quot;_blank&quot;&gt;video and audio formats&lt;/a&gt;. We see that support for H264 and AAC is available in all Android versions, but with some limitations: H264 is only supported in baseline profile and AAC is supported in MP4 container, and while testing we encountered some more limitations, like inability to play DCT-DECIMATTION encoded files.&lt;br/&gt;
I want to share the &lt;a href=&quot;http://www.ffmpeg.org&quot; target=&quot;_blank&quot;&gt;FFMpeg&lt;/a&gt; encoding configuration which worked for us.&lt;/p&gt;
&lt;h2&gt;FFMpeg configuration&lt;/h3&gt;
&lt;p&gt;
We used H264, 20fps, 200kbps baseline profile, and AAC single channel 32kbps. 2 passes encoding was used.
&lt;/p&gt;
&lt;h3&gt;Preset files&lt;/h3&gt;
&lt;p&gt;
FFMpeg supports preset files to help removing the endless flags from the command line:&lt;br&gt;
&lt;strong&gt;&lt;u&gt;First pass preset file (named &apos;libx264-android_firstpass.ffpreset&apos;)&lt;/u&gt;&lt;/strong&gt;
&lt;pre&gt;&lt;code&gt;coder=1
flags=+loop
cmp=+chroma
partitions=+parti8x8+parti4x4+partp8x8+partb8x8
me_method=dia
subq=2
me_range=16
g=250
keyint_min=25
sc_threshold=40
i_qfactor=0.71
b_strategy=1
qcomp=0.6
qmin=10
qmax=51
qdiff=4
bf=0
refs=1
directpred=1
trellis=0
flags2=-bpyramid-mixed_refs-wpred-dct8x8-fastpskip
wpredp=0
rc_lookahead=30&lt;/code&gt;&lt;/pre&gt;
&lt;strong&gt;&lt;u&gt;Second pass preset file (named &apos;libx264-android_baseline.ffpreset&apos;)&lt;/u&gt;&lt;/strong&gt;
&lt;pre&gt;&lt;code&gt;coder=0
flags=+loop
cmp=+chroma
partitions=+parti8x8+parti4x4+partp8x8+partb8x8
me_method=dia
subq=2
me_range=16
g=250
keyint_min=25
sc_threshold=40
i_qfactor=0.71
b_strategy=1
qcomp=0.6
qmin=10
qmax=51
qdiff=4
bf=0
refs=1
directpred=1
trellis=0
flags2=-bpyramid-mixed_refs-wpred-dct8x8-fastpskip
wpredp=0
rc_lookahead=30&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;
&lt;h3&gt;Encoding&lt;/h3&gt;
&lt;p&gt;
From FFMpeg folder run the following command for the first pass:
&lt;pre&gt;&lt;code&gt;bin\ffmpeg -i source_video_file.avi -vcodec libx264 -r 20 -s 320x240 -b 220k -bt 240k -pass 1 -fpre presets\libx264-android_firstpass.ffpreset -y -an NUL.mp4&lt;/code&gt;&lt;/pre&gt;
For the second pass (and actual encoding):
&lt;pre&gt;&lt;code&gt;bin\ffmpeg -i source_video_file.avi -strict experimental -acodec aac -ar 16000 -ab 32k -ac 1 -vcodec libx264 -r 20 -s 320x240 -b 220k -bt 240k -pass 2 -fpre presets\libx264-android_baseline.ffpreset -y target_video_file.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Notes&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;The FFMpeg version I used has &lt;i&gt;experimental&lt;/i&gt; support for AAC, so I used the &lt;strong&gt;&lt;i&gt;-strict experimental&lt;/i&gt;&lt;/strong&gt; flag in the second pass&lt;/li&gt;
&lt;li&gt;I know that some versions of FFMpeg do not accept the &lt;strong&gt;&lt;i&gt;-fpre&lt;/i&gt;&lt;/strong&gt; option to specify the preset file, and requires &lt;strong&gt;&lt;i&gt;-vpre&lt;/i&gt;&lt;/strong&gt;&lt;/li&gt; option instead. Just copy the profile files into the &lt;strong&gt;presets&lt;/strong&gt; folder, and use &lt;strong&gt;&lt;i&gt;android_baseline&lt;/i&gt;&lt;/strong&gt; instead of &lt;strong&gt;&lt;i&gt;presets\libx264-android_baseline.ffpreset&lt;/i&gt;&lt;/strong&gt;
&lt;li&gt;If you want to stream these video files, you&apos;ll need to add hints tracks. &lt;a href=&quot;http://mp4creator.sourceforge.net/&quot; target=&quot;_blank&quot;&gt;MP4Creator&lt;/a&gt; is a great tool that can add those hint tranks. Don&apos;t forget to add hints for both the video track and the audio track.&lt;/li&gt;
&lt;li&gt;If you see this warning in FFMpeg conversion: &quot;&lt;i&gt;Incompatible pixel format &apos;bgr24&apos; for codec &apos;libx264&apos;, auto-selecting format &apos;yuv420p&apos;&lt;/i&gt;&quot;, then &lt;b&gt;IT WONT WORK IN ANDROID&lt;/b&gt;! I noticed this warning when editing video files with &lt;a href=&quot;http://www.techsmith.com/camtasia/&quot; target=&quot;_blank&quot;&gt;Camtasia&lt;/a&gt;. Switching to &lt;a href=&quot;http://www.pitivi.org/&quot; target=&quot;_blank&quot;&gt;Piviti&lt;/a&gt; fixed the issue.&lt;/li&gt;
&lt;/ol&gt;
&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Android resources and device fragmentation</title>
   <link href="https://evendanan.net/2011/03/Android-resources-and-device-fragmentation"/>
   <updated>2011-03-17T12:00:00+00:00</updated>
   <id>https://evendanan.net/2011/03/android-fragmentation</id>
   <content type="html">&lt;p&gt;With all those Android devices out there, as a developer you bound to encounter one that is really annoying.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://uk.store.creative.com/entertainment-devices-mp3-players/ziio-7/948-20231.aspx?utm_source=uk.creative.com&amp;utm_medium=Banner&amp;utm_content=HomepageMHL_Ziio7&amp;utm_campaign=ProductAd_Ziio7&quot; target=&quot;_blank&quot;&gt;ZiiO 7&quot;&lt;/a&gt; is such: It is an Android 2.1 tablet with a &apos;large&apos;, &apos;long&apos;, &apos;mdpi&apos; resources qualifiers, which makes it an odd device. Most tablets are marked as &apos;large&apos;, &apos;hdpi&apos;, so they use the same resources (specifically, drawables) as other Android hdpi devices.&lt;/p&gt;

&lt;h3&gt;Naive solution&lt;/h3&gt;
&lt;p&gt;To make the UI look good, the ZiiO7 requires some of the hdpi drawables and some of the mdpi resources, so the only way to do it is by creating a new folder in the application resources folder named &apos;drawable-large-mdpi&apos;, and copy there the mdpi or hdpi images.&lt;/p&gt;
&lt;p&gt;This is nice, but since we also want to support &apos;large-hdpi&apos;, and since Android has a very strict &lt;a href=&quot;http://developer.android.com/guide/topics/resources/providing-resources.html&quot; target=&quot;_blank&quot;&gt;&quot;How Android Finds the Best-matching Resource&quot;&lt;/a&gt;, it means that we also need to create &apos;drawable-large-hdpi&apos; folder to handle hdpi tables (like the Samsung Tab) - which will result in a huge APK, due to resources duplication, and a maintenance nightmare when revisiting the icons. We don&apos;t want that nightmare, right?&lt;/p&gt;

&lt;h3&gt;The best solution I found&lt;/h3&gt;
&lt;p&gt;After looking around, I noticed &lt;a href=&quot;http://developer.android.com/guide/topics/resources/providing-resources.html#AliasResources&quot; target=&quot;_blank&quot;&gt;the resource alias&lt;/a&gt; option in Android aapt mechanism.
Basically, I can make a resource reference to another resource by providing an XML instead of PNG file. The XML will be of type &apos;bitmap&apos; and will point to a real drawable:&lt;/p&gt;
&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;bitmap&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns:android=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.android.com/apk/res/android&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;android:src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@drawable/icon_ca&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;or, if the image is a &lt;a href=&quot;http://developer.android.com/guide/topics/resources/drawable-resource.html#NinePatch&quot; target=&quot;_blank&quot;&gt;9 patch&lt;/a&gt;, you should use:&lt;/p&gt;
&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;nine-patch&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns:android=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.android.com/apk/res/android&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;android:src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@drawable/image_ca&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3&gt;How to&lt;/h3&gt;
I wanted to have the images in &apos;drawable-large-hdpi&apos; and &apos;drawable-large-mdpi&apos; are the same as in &apos;drawable-hdpi&apos;:
&lt;ol&gt;
&lt;li&gt;In the &lt;b&gt;&apos;drawable-hdpi&apos;&lt;/b&gt; I&apos;ll rename all the files to &lt;b&gt;[real_resource_name]_hdpi.png&lt;/b&gt; and move them to &lt;b&gt;&apos;drawable-nodpi&apos;&lt;/b&gt; folder. It is important to move them to the nodpi folder, since we do not want Android resize them.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Create a new XML file named &lt;b&gt;[real_resource_name].xml&lt;/b&gt; with the alias text mention above. &lt;b&gt;Remember that it needs to point to the renamed file! In our example &apos;[real_resource_name]_hdpi.png&apos;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Copy the newly created XML file to &apos;drawable-hdpi&apos;, &apos;drawable-large-hdpi&apos; and &apos;drawable-large-mdpi&apos; folders.&lt;/li&gt;
&lt;li&gt;Run you application to see that it all works. You might want to delete some of the resource, or point them to a different resource. I, for instance, wanted that some of the images ZiiO7 uses are mdpi.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Why it is not perfect&lt;/h3&gt;
This is the part I hate most about this solution - now I&apos;m required to provide drawables based on the size of the screen, where in a perfect world, I would provide images based on the screen density, and the layouts should change based on the screen size...
</content>
 </entry>
 
 <entry>
   <title>Dynamic Copyright Year Text</title>
   <link href="https://evendanan.net/p/65"/>
   <updated>2011-01-02T12:00:00+00:00</updated>
   <id>https://evendanan.net/p/auto-copyright-year</id>
   <content type="html">&lt;p&gt;Have you remembered to changed the copyright text?&lt;/p&gt;

&lt;p&gt;Well, I didn&apos;t. So now I have this code in the company&apos;s website footer: 
&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Y&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Fuck the CPU cycles.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>I've been a bad boy</title>
   <link href="https://evendanan.net/p/66"/>
   <updated>2010-12-16T12:00:00+00:00</updated>
   <id>https://evendanan.net/p/bad-boy</id>
   <content type="html">&lt;p&gt;We release &lt;a href=&quot;http://www.speakingpal.com&quot;&gt;our software&lt;/a&gt; in a few days. And in the last cycle of tests, all kind of bugs are surfacing up. And we are tight, &lt;i&gt;real tight&lt;/i&gt; - our release date was two weeks ago.&lt;/p&gt;

&lt;p&gt;No time to fix bugs, no time to switch to a different technology, no time to re-design classes: we need to ship the software! &lt;a href=&quot;http://thebabyboomerentrepreneur.com/1202/seth-godin-says-real-artists-ship-do-you/&quot;&gt;Like real artists&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So, I hacked network code to make a &lt;a href=&quot;http://code.google.com/p/ksoap2-android/issues/detail?id=35&quot;&gt;3rd party code work properly&lt;/a&gt;, I kept unrelated code in classes just so their inheriting classes could use it, I &lt;a href=&quot;http://en.wikipedia.org/wiki/Spaghetti_code&quot;&gt;&apos;Spaghetti-ed&apos;&lt;/a&gt; the hell out of my code just to quickly implement a requirement.&lt;/p&gt;
&lt;p&gt;All-in-all, &lt;strong&gt;I&apos;ve been a bad boy&lt;/strong&gt;. But sometimes a software developer has to be. I&apos;m not proud of it, but if we want to release, we need to make it work.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Easy on the eye MD5-generated signature*</title>
   <link href="https://evendanan.net/p/29"/>
   <updated>2010-11-26T12:00:00+00:00</updated>
   <id>https://evendanan.net/p/easy-on-the-eye-md5</id>
   <content type="html">&lt;p&gt;At my &lt;a href=&quot;http://speakingpal.com&quot;&gt;work&lt;/a&gt;, we employ light-weighted content licensing by applying &lt;a href=&quot;http://en.wikipedia.org/wiki/MD5&quot;&gt;MD5&lt;/a&gt; over a crafted string. MD5 is quite long, and includes letters and digits, or example:
&lt;code&gt;MD5(&quot;&lt;a title=&quot;The quick brown fox jumps over the lazy dog&quot; href=&quot;http://en.wikipedia.org/wiki/The_quick_brown_fox_jumps_over_the_lazy_dog&quot;&gt;The quick brown fox jumps over the lazy dog&lt;/a&gt;&lt;strong&gt;.&lt;/strong&gt;&quot;) = e4d909c290d0fb1ca068ffaddf22cbd0&lt;/code&gt;
Since most of our users are using mobile devices, we wanted to make it easy:
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;only alphabets - so the user will not be required to switch layouts (alphabet&amp;lt;-&amp;gt;numbers)&lt;/li&gt;
&lt;li&gt;all uppercase - so it will be easy to read, and clear.&lt;/li&gt;
&lt;li&gt;replace the letter O - so the user will not confuse it with the digit zero.&lt;/li&gt;
&lt;/ul&gt;
How safe is it? Well, as I see it, we still get 16 in the power of 10 permutations (A..P ten times), which are &lt;a href=&quot;http://vault.benbenson.com/data/001/md5/section5.html&quot;&gt;very nicely distributed&lt;/a&gt;, the &lt;a href=&quot;http://en.wikipedia.org/wiki/Birthday_problem&quot;&gt;Birthday Paradox&lt;/a&gt; does not apply here (since the hash is not used to &apos;hide&apos; the source, but to &apos;certify&apos; the source. I think...), and it is very easy on the users&apos; eye.
So we have an MD5 string (e.g. &lt;strong&gt;37aa53dc03fd0a18617bda509c243012&lt;/strong&gt;), and we want to get a human readable, type-able string (e.g. &lt;strong&gt;JNAALJDCGJ&lt;/strong&gt;). Here is the PHP code we used (I take not responsibility for any MD5 or this code limitations!):
&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;md5_shrinking&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$md5_signature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;//the input of this function is an MD5 hex (easily created with PHP&apos;s function md5($string) )&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;//actually, it doesn&apos;t have to be MD5 string. It can be anything from a random number&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;//to SHA1 to a specially crafted string/&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;//I want to do this:&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;// 37aa53dc03fd0a18617bda509c243012 -&amp;gt; JNAALJDCGJ&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;//sub-stringing to 10: easier on users&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$trimmed_md5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;substr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$md5_signature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;//upper-case so users will read it easily&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$upper_trimmed_md5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;strtoupper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$trimmed_md5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;//converting the digits to alphabets. And I&apos;m going to convert to letters after F (no doubles with&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;//HEX A-F letters.&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$regex_trimmed_md5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;preg_replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;/(\d)/e&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;chr(\\1+71)&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$upper_trimmed_md5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;//And converting the letter O to X, so users will not confuse it with zero&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$regex_trimmed_md5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;preg_replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;/(O)/&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;X&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$regex_trimmed_md5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$regex_trimmed_md5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3&gt;Security issues:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Append a &lt;a href=&quot;http://en.wikipedia.org/wiki/Salt_%28cryptography%29&quot;&gt;salt&lt;/a&gt; string to your MD5 input! It will make you feel safer :)&lt;/li&gt;
&lt;li&gt;Make sure you understand the &lt;a href=&quot;http://www.google.com/search?q=md5+collision+chance&quot;&gt;MD5&lt;/a&gt; and &lt;a href=&quot;http://forr.st/~Cfg&quot;&gt;this function&lt;/a&gt; limitations, and see if they are acceptable for your purpose (as mentioned above - I take no responsibility!)&lt;/li&gt;
&lt;li&gt;The sub-stringing increases the chances of collisions. This is the cost you&apos;ll pay for a human readable/type-able string.&lt;/li&gt;
&lt;li&gt;I &lt;em&gt;ONLY &lt;/em&gt;think that the Birthday Paradox does not apply here, but I&apos;m no mathematician. It could be.&lt;/li&gt;
&lt;/ul&gt;

&lt;h6&gt;*note: can be applied to any string.&lt;/h6&gt;
</content>
 </entry>
 
 <entry>
   <title>A Tiny XmlWriter or Why Not to Use Java's XML Framework</title>
   <link href="https://evendanan.net/p/29"/>
   <updated>2010-11-15T12:00:00+00:00</updated>
   <id>https://evendanan.net/p/tiny-xml</id>
   <content type="html">&lt;p&gt;We are now at the final stages of releasing our first public version of &lt;a href=&quot;http://speakingpal.com&quot; target=&quot;_blank&quot;&gt;SpeakingPal&lt;/a&gt; and go over content and finalize interfaces and data structures.&lt;/p&gt;

&lt;p&gt;
This can be a very tedious job: we find some kind of error in XML structures and content types, then go to fix this, just to realize we forgot something (this is how it is in a very small startup...) - then redefine the XML structures and try again.
While at it, I try to be as &lt;a href=&quot;http://blogoscoped.com/archive/2005-08-24-n14.html&quot; target=&quot;_blank&quot;&gt;lazy&lt;/a&gt; as possible and write &lt;a href=&quot;http://en.wikipedia.org/wiki/Eating_your_own_dog_food&quot;&gt;dogfood kinda tools&lt;/a&gt; to do the work for me. This time, I wanted to write a small Java application (I use Java where others use scripting languages) for creating the required XML structures, but I couldn&apos;t remember how to use Java&apos;s very extensive XML framework, so I googled for &quot;&lt;a href=&quot;http://www.google.com/search?q=java+xmlwriter&quot;&gt;Java XmlWriter&lt;/a&gt;&quot;, and first came something I didn&apos;t expect: &lt;a href=&quot;http://www.generationjava.com/docs/XmlWriter/XmlWriter.java.html&quot;&gt;source code&lt;/a&gt; for a tiny XML files writer. I browsed over the code, and loved it immediately! It was short, and clear, and did almost everything I need.&lt;/p&gt;
&lt;p&gt;So, I decided to use this code for my XML generating tool instead of using Java&apos;s XML framework, which, I&apos;m sure, handles all the possible errors, and all the special &lt;a href=&quot;http://xmmssc-www.star.le.ac.uk/SAS/xmmsas_20070308_1802/doc/param/node24.html&quot;&gt;escape &lt;/a&gt;&lt;a href=&quot;http://tntluoma.com/sidebars/codes/&quot;&gt;codes&lt;/a&gt;, and every XML &lt;a href=&quot;http://download.oracle.com/javase/1.4.2/docs/api/javax/xml/transform/Transformer.html&quot;&gt;format&lt;/a&gt; you may want, etc, etc.
The reasons I&apos;ve decided to use this code and not Java&apos;s are:
&lt;ol&gt;
&lt;li&gt;It is beautiful: no really! It is so simple, and so small, and has 80% of the features you may need, and had a very straight-forward API, I just had to use it.&lt;/li&gt;
&lt;li&gt;It is small: our company is creating client applications for Android, Symbian and Blackberry, all uses Java as a programming language, and all are mobile platforms which are low on resources.&lt;/li&gt;
&lt;li&gt;It is fast: it doesn&apos;t do anything else but write XML nodes since it assumes you will not make structure/syntax errors while building your XML, and you will not try to add exotic data or characters.&lt;/li&gt;
&lt;/ol&gt;
&lt;/p&gt;
&lt;p&gt;
For mobile platforms &lt;code&gt;Simple+Small+Fast = GOOD!&lt;/code&gt; So, it may not the best for complex applications, but it is very good for resources-limited devices.
Anyhow, &lt;a href=&quot;https://github.com/menny/Java-very-tiny-XmlWriter/blob/master/XmlWriter.java&quot;&gt;here&lt;/a&gt; is the source code for you to also enjoy (as Henri&apos;s code, my code is also BSD licensed). I&apos;m sure there are many other such &lt;a href=&quot;http://sourceforge.net/projects/txml/&quot;&gt;tiny&lt;/a&gt;-&lt;a href=&quot;http://maol.su-fun.com/Kava_Doc/kava/xml/TinyXML.html&quot;&gt;xml&lt;/a&gt;-like classes.
The source code I found was very out-dated. &lt;a href=&quot;http://www.yandell.org/henri/&quot;&gt;Henri Yandell&lt;/a&gt; (the original coder) has moved the source to &lt;a href=&quot;http://code.google.com/p/osjava/&quot;&gt;Google Code&lt;/a&gt; and continued evolving the code (and many other projects). Jump over there to see if you might need something. I will :)
&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Hello world!</title>
   <link href="https://evendanan.net/p/1"/>
   <updated>2010-11-10T12:00:00+00:00</updated>
   <id>https://evendanan.net/p/hello-world</id>
   <content type="html">&lt;p&gt;` Welcome to WordPress. This is your first post. Edit or delete it, then start blogging! `&lt;/p&gt;

&lt;p&gt;The text above (and post title) is an automatic post done by &lt;a href=&quot;http://wordpress.org&quot;&gt;WordPress&lt;/a&gt; installation (the blogs application used in this site). I find it nice, as a programmer I mean: The first thing you learn in every programming language tutorial is how to create an &lt;a href=&quot;http://en.wikipedia.org/wiki/Hello_world_program&quot;&gt;“Hello world!” program&lt;/a&gt; (whether it teaches anything is questionable, especially in the &lt;a href=&quot;http://symbian.compactbyte.com/2004/06/hello-world.html&quot;&gt;smartphones area&lt;/a&gt; which requires way too many &lt;em&gt;satellite&lt;/em&gt; files just to get started).
Ok, to the point, I’m going to use this blog to share my experiences at &lt;a href=&quot;http://www.speakingpal.com/&quot;&gt;my day job&lt;/a&gt;, my &lt;a href=&quot;http://softkeyboard.googlecode.com&quot;&gt;hobby&lt;/a&gt;, &lt;a href=&quot;http://code.google.com/p/fill-mythvideo-metadata&quot;&gt;side projects&lt;/a&gt;, and to talk about solutions I find for all kind of computer related stuff (e.g. how to configure a decent &lt;a href=&quot;http://mythtv.org/wiki/user:Menny&quot;&gt;MythTV machine&lt;/a&gt;), and possibly things I think are cool (in a &lt;em&gt;geeky&lt;/em&gt; kinda way).&lt;/p&gt;
</content>
 </entry>
 
 
</feed>