<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Max Baumann</title>
    <description>The latest articles on DEV Community by Max Baumann (@fosefx).</description>
    <link>https://dev.to/fosefx</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F256402%2Fe6e5c9de-58e3-4e2c-8275-f75041989af0.jpg</url>
      <title>DEV Community: Max Baumann</title>
      <link>https://dev.to/fosefx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fosefx"/>
    <language>en</language>
    <item>
      <title>I wrote an open source mod of an Android App</title>
      <dc:creator>Max Baumann</dc:creator>
      <pubDate>Mon, 04 Oct 2021 17:08:29 +0000</pubDate>
      <link>https://dev.to/fosefx/i-wrote-an-open-source-mod-of-an-android-app-5d16</link>
      <guid>https://dev.to/fosefx/i-wrote-an-open-source-mod-of-an-android-app-5d16</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Before we start: Acting on some of the information shared in this article may or may not violate the terms of service of third party apps. You are responsible for any damages, not me.&lt;/p&gt;

&lt;p&gt;This article assumes familiarity with Android App Development&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The last few month I have spend working on an &lt;a href="https://github.com/bttv-android/bttv#bttv-android" rel="noopener noreferrer"&gt;open source mod of the Twitch Android App&lt;/a&gt;. In this post I want to present you the challenges I have faced, the solutions I came up with and we will also learn how Android Apps work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anatomy of an apk file
&lt;/h2&gt;

&lt;p&gt;Before we can start thinking about how we can modify an Android App we have to understand the structure of an &lt;code&gt;.apk&lt;/code&gt; file, the installation format for Android apps.&lt;/p&gt;

&lt;p&gt;These application files are just signed zip archives which contain everything the app needs to run properly. This first and foremost includes the compiled byte-code of the code, but also resources like images, layouts, native libraries, fonts and other assets.&lt;/p&gt;

&lt;p&gt;Just rename any &lt;code&gt;.apk&lt;/code&gt; file to a &lt;code&gt;.zip&lt;/code&gt; file and have a look inside.&lt;/p&gt;

&lt;p&gt;Your results may vary quite a lot, as some applications make use of some techniques others do not.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AndroidManifest.xml&lt;/code&gt; is pretty much the same &lt;a href="https://developer.android.com/guide/topics/manifest/manifest-intro" rel="noopener noreferrer"&gt;AndroidManifest.xml&lt;/a&gt; you already know, just in the xml binary format. It also includes information that is not explicitly added to it by a developer, but is derived from their gradle settings.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;resources.arsc&lt;/code&gt; contains all resources used in the app in an Android specific format, but don't worry there are tools to extract it's contents.&lt;/p&gt;

&lt;p&gt;If you find a &lt;code&gt;lib/&lt;/code&gt; directory, that is because the app uses the &lt;a href="https://developer.android.com/ndk/" rel="noopener noreferrer"&gt;NDK&lt;/a&gt; and it contains native libraries as ELF Shared Objects.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;classes.dex&lt;/code&gt; contains the Android byte code of the app and is thus the most important result of the compilation process. You likely find files like &lt;code&gt;classes2.dex&lt;/code&gt; or &lt;code&gt;classes3.dex&lt;/code&gt; as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Split apks
&lt;/h3&gt;

&lt;p&gt;Some vendors split their apks into so called splits; They release a &lt;code&gt;base.apk&lt;/code&gt; file which includes most of the app and multiple additional splits for different devices. There is no point in delivering xxxhdpi images to small devices or French strings to Arabic users. Fortunately it is a near trivial task to merge them back together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reverse Engineering
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Apktool
&lt;/h3&gt;

&lt;p&gt;The avid reader noticed nothing is human readable, which makes sense as apk files should be as small as possible. A tool called &lt;a href="https://ibotpeaches.github.io/Apktool/" rel="noopener noreferrer"&gt;Apktool&lt;/a&gt; comes to the rescue.&lt;/p&gt;

&lt;p&gt;You give it an apk file and it will&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;extract all resources,&lt;/li&gt;
&lt;li&gt;disassemble the &lt;code&gt;.dex&lt;/code&gt; files into directories of &lt;code&gt;.smali&lt;/code&gt; files and&lt;/li&gt;
&lt;li&gt;convert the AndroidManifest.xml back into a readable format. (&lt;code&gt;java -jar apktool.jar d file.apk&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;If you deal with a split apk you can run apktool on all of them and merge the directories. Make sure you keep the AndroidManifest and public.xml of the base and update the latter to reference all external resources (apktool will create stubs form them). I wrote a &lt;a href="https://github.com/bttv-android/bttv/blob/master/disassemble" rel="noopener noreferrer"&gt;bash script&lt;/a&gt; and a simple &lt;a href="https://github.com/bttv-android/build-companion/blob/master/fix.go" rel="noopener noreferrer"&gt;go tool&lt;/a&gt; for this.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The great thing about this is that all of these steps can be reverted again, so we can make changes and produce a new &lt;code&gt;.apk&lt;/code&gt; file using &lt;code&gt;java -jar apktool.jar b /path/to/target-directory&lt;/code&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can't install it yet, as it needs to be signed first, more on this later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  JADX
&lt;/h3&gt;

&lt;p&gt;But before we take a dive into the smali code, let's look at some good ol' Java code. Android byte-code is an optimized form of Java byte-code, which is very easily and reliably decompilable. Don't get me wrong, the result will contain lots of issues and won't be compilable. The tool of choice for Android decompilation is called &lt;a href="https://github.com/skylot/jadx" rel="noopener noreferrer"&gt;JADX&lt;/a&gt;. Just hand it an apk file and it will produce an entire Android Studio project for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jadx &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nt"&gt;--show-bad-code&lt;/span&gt; &lt;span class="nt"&gt;--no-imports&lt;/span&gt; &lt;span class="nt"&gt;--deobf&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; decompiled ./your.apk&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;I added --no-imports as IDEA had some issues with resolving imports for me, you can try out jadx without this flag, it will produce far more friendlier code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Actually doing it
&lt;/h3&gt;

&lt;p&gt;When it comes to the actual task of reverse engineering there is little I can tell you, you just read code, lots of it.&lt;br&gt;
Here are a few tips though:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Have a goal in mind.&lt;/strong&gt;
Most of the time you will not work with small hobby apps, but huge applications with thousands of classes. You don't want to (and probably can't) read all of it. You objective is not to understand everything about the way your sample works. You want to achieve something.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learn your tools&lt;/strong&gt;
Android Studio (IntelliJ IDEA) is incredibly complex and also incredibly helpful if you know what it can do. You should spend some time getting to know it better.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep it simple&lt;/strong&gt;
You always want to find the easiest, most simple way of achieving the functionality you want. This makes rebasing to new versions easier (and is easier to debug).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Follow the strings&lt;/strong&gt;
Most of the time when something happens on your screen some text will appear &lt;em&gt;somewhere&lt;/em&gt;. If you look for usages of those strings you can quickly find the parts of the codebase that are of interest for you.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Modding
&lt;/h2&gt;

&lt;p&gt;So you now 1. have an idea for a modification and 2. have read the relevant code to 3. know what you need to change and now want to do it. But how? There are two ways you can add custom code to an app, which I call &lt;em&gt;patching&lt;/em&gt; and &lt;em&gt;including&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Patching
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The app you want to modify opens a dialog bar to trick the user into giving away their data and you want to modify it to automatically click the reject button.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After reverse engineering you found this code snippet in a file called &lt;code&gt;CookiesLauncher.java&lt;/code&gt; and had an idea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class CookiesLauncher {
  public void showBarToUser(final LinearLayout layout) {
    final CookieBanner banner = new CookieBanner(layout.getContext());
    banner.setOnReject(new OnClickListener() {
      public void onClick(View v) {
        CookiesLauncher.this.dontStealData();
        layout.removeView(banner);
      }
    });
    layout.addView(banner);
    // &amp;lt;-- Idea: call the reject handler here
  }

  private void dontStealData() {
    this.stealDataAnyway();
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a simple task and can be done by simply adding a patch.&lt;/p&gt;

&lt;p&gt;The first step is always to find the &lt;code&gt;smali&lt;/code&gt; version of your java code. For this you simply look for the class name and add a &lt;code&gt;.smali&lt;/code&gt; to it. If it is an anonymous class embedded somewhere you it will be called something like like: &lt;code&gt;ParentClass$1.smali&lt;/code&gt; or &lt;code&gt;ParentClass$OnClickListener.smali&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In our example we simply edit the &lt;code&gt;CookiesLauncher.smali&lt;/code&gt; which will be in one of the &lt;code&gt;smali_classes&lt;/code&gt; directories.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.class public Lcom/test/app/CookiesLauncher;
.super Ljava/lang/Object;
.source "CookiesLauncher.java"


.method public showBarToUser(Landroid/widget/LinearLayout;)V
    .locals 2

    .line 10
    new-instance v0, Lcom/test/app/CookieBanner;

    invoke-virtual {p1}, Landroid/widget/LinearLayout;-&amp;gt;getContext()Landroid/content/Context;

    move-result-object v1

    invoke-direct {v0, v1}, Lcom/test/app/CookieBanner;-&amp;gt;&amp;lt;init&amp;gt;(Landroid/content/Context;)V

    .line 11
    new-instance v1, Lcom/test/app/CookiesLauncher$1;

    invoke-direct {v1, p0, p1, v0}, Lcom/test/app/CookiesLauncher$1;-&amp;gt;&amp;lt;init&amp;gt;(Lcom/test/app/CookiesLauncher;Landroid/widget/LinearLayout;Lcom/test/app/CookieBanner;)V

    invoke-virtual {v0, v1}, Lcom/test/app/CookieBanner;-&amp;gt;setOnReject(Landroid/view/View$OnClickListener;)V

    .line 17
    invoke-virtual {p1, v0}, Landroid/widget/LinearLayout;-&amp;gt;addView(Landroid/view/View;)V

    .line 19
    return-void
.end method

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, smali is much lower level than Java is. We don't work with variables, but with registers, we don't just call a method but &lt;code&gt;invoke&lt;/code&gt; them and it matters whether we invoke a static method (&lt;code&gt;invoke-static&lt;/code&gt;), a method on an interface (&lt;code&gt;invoke-interface&lt;/code&gt;) or a virtual one (&lt;code&gt;invoke-virtual&lt;/code&gt;). The whole instruction set is available here: &lt;a href="https://source.android.com/devices/tech/dalvik/dalvik-bytecode#instructions" rel="noopener noreferrer"&gt;https://source.android.com/devices/tech/dalvik/dalvik-bytecode&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We need to call the &lt;code&gt;onClick(View v)&lt;/code&gt; interface method on the &lt;a href="https://developer.android.com/reference/android/view/View.OnClickListener" rel="noopener noreferrer"&gt;android.view.View.OnClickListener&lt;/a&gt; which is in register &lt;code&gt;v1&lt;/code&gt; and pass the &lt;code&gt;LinearLayout&lt;/code&gt; in &lt;code&gt;p1&lt;/code&gt; to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.local v0, "banner":Lcom/test/app/CookieBanner;
new-instance v1, Lcom/test/app/CookiesLauncher$1;

invoke-direct {v1, p0, p1, v0}, Lcom/test/app/CookiesLauncher$1;-&amp;gt;&amp;lt;init&amp;gt;(Lcom/test/app/CookiesLauncher;Landroid/widget/LinearLayout;Lcom/test/app/CookieBanner;)V

invoke-virtual {v0, v1}, Lcom/test/app/CookieBanner;-&amp;gt;setOnReject(Landroid/view/View$OnClickListener;)V

.line 17
invoke-virtual {p1, v0}, Landroid/widget/LinearLayout;-&amp;gt;addView(Landroid/view/View;)V

invoke-interface {v1, p1}, android/view/View$OnClickListener;-&amp;gt;onClick(Landroid/view/View;)V

.line 19
return-void
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.bmn.dev%2Fimages%2Fbttv-android%2Fmonke.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.bmn.dev%2Fimages%2Fbttv-android%2Fmonke.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can rebuild the apk file using apktool and have a patched application file. There is one more issue though:&lt;/p&gt;

&lt;h3&gt;
  
  
  Signing
&lt;/h3&gt;

&lt;p&gt;The original app was signed using the developer's private key. Before installing the apk the Android operating system will confirm that the signature is still valid, and thus whether the apk has not been tempered with. If this verification fails, Android will refuse to install it.&lt;/p&gt;

&lt;p&gt;So we need to sign the apk ourselves. For security purposes Android will refuse any signature that was not made by the same authors of previous installed version of the app, so we need to uninstall the original app first.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pro tip: You can get around this by changing the app's id. This might come with side effects though.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I used a tool called &lt;a href="https://github.com/patrickfav/uber-apk-signer/releases" rel="noopener noreferrer"&gt;Uber apk signer&lt;/a&gt; for this.&lt;/p&gt;

&lt;p&gt;Now you can install your patched app. Congrats!&lt;/p&gt;

&lt;h3&gt;
  
  
  Including
&lt;/h3&gt;

&lt;p&gt;If you want to add logic that is more complex that a simple if-else or two, you probably want to write the code in Java (or kotlin), compile it, add it to the smali directories and then simply patch in a call to your methods. This is what I call &lt;em&gt;including&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The following figure roughly illustrates this process:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.bmn.dev%2Fimages%2Fbttv-android%2Finteraction.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.bmn.dev%2Fimages%2Fbttv-android%2Finteraction.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While this takes the burden of writing proper smali code off you just made another enemy: The Java Compiler.&lt;/p&gt;

&lt;h4&gt;
  
  
  Mocks
&lt;/h4&gt;

&lt;p&gt;The compiler has no idea about the classes that exist in your App, so when you try to call them from your code you are out of luck, you have to mock them.&lt;/p&gt;

&lt;p&gt;This means you copy the call signatures of the methods you want to call. Of course you don't want to actually build the mocks. I fixed this by putting the mocks in a separate Android Studio Module.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.bmn.dev%2Fimages%2Fbttv-android%2Fmodule.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.bmn.dev%2Fimages%2Fbttv-android%2Fmodule.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This module is then imported by the other module as an Android library and thus not build by the compiler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
  // ...
  implementation project(path: ':othermodulesnamehere')
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Using all of these techniques this is how the mod will be created:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use apktool to convert the apk to editable files&lt;/li&gt;
&lt;li&gt;Build any new classes you may want to add&lt;/li&gt;
&lt;li&gt;Apply your patches&lt;/li&gt;
&lt;li&gt;Rebuild the apk file&lt;/li&gt;
&lt;li&gt;Sign it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.bmn.dev%2Fimages%2Fbttv-android%2Fbuild.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.bmn.dev%2Fimages%2Fbttv-android%2Fbuild.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Open source and its constrains
&lt;/h2&gt;

&lt;p&gt;Ok, now that you know &lt;em&gt;how&lt;/em&gt; to mod an Android application it is time to talk about why there are pretty much no open source mods.&lt;br&gt;
Whatever I do must be &lt;strong&gt;shareable and reproducible&lt;/strong&gt;. At the same time I &lt;strong&gt;can't share the original code&lt;/strong&gt;, as it is not my IP. At the heart of open source software is always the idea of &lt;strong&gt;collaborative work&lt;/strong&gt;, so that must be possible. The solution is simple: Git.&lt;/p&gt;

&lt;p&gt;Instead of sharing the modified code we can simply track the changes made using &lt;code&gt;git diff&lt;/code&gt; and share the result of that.&lt;/p&gt;

&lt;p&gt;For this to work we initialize a git repository and make a commit tagged &lt;code&gt;base&lt;/code&gt; right after running &lt;code&gt;apktool&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;java &lt;span class="nt"&gt;-jar&lt;/span&gt; apktool.jar d file.apk &lt;span class="nt"&gt;-o&lt;/span&gt; disass &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="c"&gt;# disassemble&lt;/span&gt;
  &lt;span class="nb"&gt;cd &lt;/span&gt;disass &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; 
  git init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="c"&gt;# init git repo&lt;/span&gt;
  git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"base"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="c"&gt;# create commit&lt;/span&gt;
  git tag base &lt;span class="c"&gt;# and tag it&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To save our changes we generate a &lt;code&gt;.patch&lt;/code&gt; file like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;disass &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="c"&gt;# make sure all changes are tracked&lt;/span&gt;
  git diff base &lt;span class="nt"&gt;--minimal&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ../ourmod.patch &lt;span class="c"&gt;# generate .patch file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;.patch&lt;/code&gt; file can then be published.&lt;/p&gt;

&lt;p&gt;Your project structure will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;workspace/
- .git/ &lt;span class="c"&gt;# of course we track our changes to the workspace using git&lt;/span&gt;
- .gitignore
- disass/ &lt;span class="c"&gt;# (in .gitignore) contains our result of `apktool d`&lt;/span&gt;
  - .git/
- ourmod.patch &lt;span class="c"&gt;# our changes&lt;/span&gt;
- project/ &lt;span class="c"&gt;# android studio project used for inclusion&lt;/span&gt;

- a.sh
- bunch.sh
- of.sh
- scripts.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;git apply&lt;/code&gt; it is possible for another dev to apply those changes after disassembling the app themselves (as long as they use the same version of the app, more on updates later).&lt;/p&gt;

&lt;p&gt;This is not a perfect solution though, as the output of &lt;code&gt;git diff&lt;/code&gt; moves quite a lot sometimes and having all patches in one file is also a lot of fun to merge. &lt;sup&gt;/s&lt;/sup&gt;. This is why I wrote a &lt;a href="https://github.com/bttv-android/bttv/blob/master/genmonke" rel="noopener noreferrer"&gt;script&lt;/a&gt; that generates patch files for each changed file, but that only helps so much.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As you can guess bash scripts are the duck-tape holding this project together&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Updates
&lt;/h3&gt;

&lt;p&gt;Due to the usage of git it is easy to apply the changes to a new version of the Twitch app, when it comes out. Of course there are always merge conflicts but most of the patches can be merged surprisingly easily. What is a problem though are the thousands of classes in the Twitch app. I can't verify that nothing significant has changed and update the mocks by hand. That's why I wrote a tool that checks for changes in methods and properties of classes between updates: &lt;a href="https://github.com/bttv-android/ubi" rel="noopener noreferrer"&gt;ubi&lt;/a&gt;. It is quite verbose and a lot of the reports it generates can be ignored, but it does improve the quality of the mod as I can act on changes statically, before running into crashes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I was able to put the years of picking up little skills on the side, like bash scripting or git magic to use in solving a problem where you (unfortunately) could not simply read the docs and holy sh#t I'm proud of that. Every version of bttv-android gets 3k+ downloads now and I am amazed by the incredible support of the Twitch Community, which helps with feature requests, bug reports and translations.&lt;br&gt;
The solutions I came up with are probably not the best way of producing a somewhat high quality mod and heavily relies on bash-duck-tape-f#ckery and if you have better Ideas please let me know!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.bmn.dev/I-wrote-an-open-source-mod-of-an-Android-App" rel="noopener noreferrer"&gt;https://blog.bmn.dev/I-wrote-an-open-source-mod-of-an-Android-App&lt;/a&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>androidmodding</category>
      <category>modding</category>
    </item>
    <item>
      <title>Testing an Akita-Angular Application with Cypress</title>
      <dc:creator>Max Baumann</dc:creator>
      <pubDate>Mon, 20 Apr 2020 17:48:17 +0000</pubDate>
      <link>https://dev.to/fosefx/testing-an-akita-angular-application-with-cypress-3c63</link>
      <guid>https://dev.to/fosefx/testing-an-akita-angular-application-with-cypress-3c63</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Note: This post was &lt;a href="https://gist.github.com/FoseFx/a34cd86376e64e547f21320361391f63"&gt;written seven months ago&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cypress is one of the easiest ways to test your Angular application. But because it is not tied to any Angular API it is hard to look "under the hood" of your tested app. However directly manipulating the internal state of it can make testing even easier. This post will show you a way to achieve this.&lt;/p&gt;

&lt;p&gt;Unfortunately, we need to add a bit of overhead to our Application, this is marginal though.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get state in Cypress
&lt;/h2&gt;

&lt;p&gt;To write a binding for Cypress we need to create a function that needs to be called in the constructor of each of our Akita Queries. Make sure to pass the query itself to it using &lt;code&gt;this&lt;/code&gt;. Cypress provides a global &lt;code&gt;window.Cypress&lt;/code&gt; variable we can use to determine whether we are in a Cypress testing environment. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;cypressBinding.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function queryCypressBinding(query) {
    if (window.Cypress) { ...  } else { ... }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;app.query.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export class AppQuery extends Query&amp;lt;AppState&amp;gt; {
    constructor(protected store: AppStore) {
        super(store);
        queryCypressBinding(this); // &amp;lt;-- Add this line to every Query
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our goal is to provide a field that allows access from Cypress. I decided to use the Class name for that. Every time the State changes this field should get updated. We can do this the Akita way using &lt;code&gt;query.select()&lt;/code&gt; which will listen for every state-change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function queryCypressBinding(query) {
    const name = query.constructor.name; // e.g. AppQuery
    // @ts-ignore
    if (window.Cypress) { 
        // @ts-ignore
        query.select().subscribe(_ =&amp;gt; window[name] = query.getValue()); // updates the field with new state
    } else {
        delete window[name]; // to make sure we dont leak state in production
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice! Using this we can test our state in Cypress like this:&lt;br&gt;
&lt;code&gt;sometest.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
it('should to sth', () =&amp;gt; {
    cy.visit('http://localhost:4200/');
    // do stuff
    cy
        .window() // get app's window variable
        .its('AppQuery') // get store
        .its('somevalue') // this depends on your store
        .should('exist') // do whatever testing you want here
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Manipulate state in Cypress
&lt;/h2&gt;

&lt;p&gt;We now have read-access to our state. But how can we dispatch actions from our testing suite? You might have guessed it, we expose our service to Cypress. So let's write another function to do so and call it in every constructor of our services.&lt;br&gt;
&lt;code&gt;cypressBinding.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function serviceCypressBinding(service) {
    const name = service.constructor.name;
    // @ts-ignore
    if (window.Cypress) {
        console.log('testing environment detected adding ' + name);
        // @ts-ignore
        window[name] = service;
    } else {
        delete window[name];
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;app.service.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export class AppService {
  constructor(private store: AppStore, private query: AppQuery) {
    serviceCypressBinding(this);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put this to use like this:&lt;br&gt;
&lt;code&gt;anothertest.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;it('should manipulate stuff', () =&amp;gt; {
    cy
        .window() // get app's window variable
        .its('AppService')
        .invoke('update', {
            somevalue: 'Hello World'
        });
    // obvserve changes
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or call a function on your Service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;it('should manipulate more stuff', () =&amp;gt; {
    cy
        .window() // get app's window variable
        .its('AppService')
        .invoke('removeAllTodos'); // call your function
    // obvserve changes
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Thank you for reading
&lt;/h3&gt;

&lt;p&gt;If this was helpful or you find any better solution let me know!&lt;/p&gt;




&lt;p&gt;Twitter: &lt;a href="https://twitter.com/FoseFx"&gt;@fosefx&lt;/a&gt;&lt;br&gt;
Github: &lt;a href="https://github.com/Fosefx"&gt;Fosefx&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cypress</category>
      <category>angular</category>
      <category>akita</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
