Showing posts with label android. Show all posts
Showing posts with label android. Show all posts

Oct 26, 2018

Dumping my Mac for Linux

Dual Booting and Configuring a Dell Alienware 15 R4 with Ubuntu 18.04 for Android Development

Context: New Job. New Laptop.

After starting a new job, I decided to finally pull the trigger on my break up with Macbook Pros. Going steady with MBPs for over 10 years meant I had to carefully weigh my decision to leave but I hope that ultimately means I'm never going back!

My new laptop is a serious upgrade. From the i9 processor to the GTX 1080 graphics card, it's a total babe that's more powerful yet cheaper than my ex. The tough part is rebuilding what I had.

Setup

The goal was to dual boot Windows 10 and Ubuntu 18.10 but it turned out that 18.10 (cosmic cuttlefish) was a little too cutting edge so I had to downshift to the LTS release (18.04.1 bionic beaver). Getting everything working just right has not been easy. In fact, it's so difficult and temperamental that I decided to log everything here just in case I need to do it all again!

Background

Before diving into technical notes, lets reflect a bit. Apple seems to have forgotten developers, making progressively worse changes for us. The final straw for me was learning that the new Macbook "Pro" would have the hard drive AND RAM soldered to the motherboard! Replacing mechanical parts or upgrading RAM should not require a new machine or a trip to the "Genius Bar." Further, I heard rumors that the new machines were so obsessed with being thin that they do a terrible job cooling i9 processors, causing them to throttle down to i7 speeds. All of this made me consider what life would be like if I made the Linux switch. A good friend recommended Dell for their customer service. I called, spoke to an actual person and got several discounts, including FOUR years of premium service. Ultimately, my maxed out Alienware 15 was actually CHEAPER than the lack-luster Macbook Pro equivalent yet beefier in every way.

Switching was not as easy as I thought. From lost work productivity to retraining my muscle memory, I must admit that I severely underestimated how much time I would lose up front. It took me nearly a week to get this machine up and running. In the very first hour, I began learning the hard way that what Apple lacks in hardware, it makes up for with cohesiveness. For instance, in the 10 years that I've been using Macs, I never realized that BIOS had changed and UEFI is a thing! I never needed to know what model of graphics card or WiFi adapter was in my MBP. These are all things Linux forced me to grapple with on day one. With Apple, everything just worked . . . until suddenly it didn't! That's when Apple forces you to come crawling back with several grand in hand to repay them for all the trouble-free enjoyment you had. Sorry, I don't want to buy a new computer every 2 years. My laptop is not a smartphone and AT&T is not subsidizing it.

The moment I fully compiled my Android project, all of my second thoughts immediately faded away. Initial, side-by-side benchmarks showed that my Alienware was nearly 8 times faster than my existing MBP. I literally have never seen any Android project compile so fast. At first, I thought something was broken and deleted/reinstalled multiple times to confirm it was actually doing a clean build. Incremental builds are less than 1 second. Full builds are nearly 5 seconds. That's insane. Meanwhile, my MBP took ages and acted like it was going to overheat.

It's too early to be completely certain of whether I'm delighted with the switch. However, I'm already feeling much more like a hacker than a hipster and that, alone, might be worth it.
This is still a WIP and I may add things as I go.
[Update: 1 year later and I'm delighted and never looking back!]

Hardware Configuration

Trackpad

The trackpad was completely unresponsive. To get around this initially, I just used a corded USB mouse. Ultimately, fixing the issue required blacklisting a module and then rebooting which I found in this gist:
sudo su
echo 'blacklist i2c_hid' >> /etc/modprobe.d/blacklist.conf
depmod -a
update-initramfs -u

Network Adapter

Wifi worked perfectly on Ubuntu 18.10 but, sadly, the LTS version 18.04 did not recognize my wifi adapter, giving this defeating error in settings:


Of course, the laptop does have an adapter, the "Killer 1550 802.11ac 2x2 WiFi and Bluetooth 5.0" so I initially got around this severe limitation by pluggin in an ethernet cable. Then I followed these instructions on AskUbuntu to get the wifi card working:
sudo apt-get install git
git clone https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/backport-iwlwifi.git
cd backport-iwlwifi
make defconfig-iwlwifi-public
sed -i 's/CPTCFG_IWLMVM_VENDOR_CMDS=y/# CPTCFG_IWLMVM_VENDOR_CMDS is not set/' .config
make -j4
sudo make install

I cloned this to a place that I could easily find later because after every update it has to be reinstalled via:
cd backport-iwlwifi
make clean
make defconfig-iwlwifi-public
make -j4
sudo make install
*** The output of this install contains some errors that seem related to this bug in the NVidea-390 driver but it worked so I'm not too worried about it, for now.

NVidea Driver : Fix broken hibernate/suspend/resume

Resuming from hibernate did not work. If the laptop ever went to sleep, then it would never wake up! This is a terrible user experience. Fortunately, the fix for this was pretty easy; just install the Nvidea drivers with:
ubuntu-drivers devices
sudo ubuntu-drivers autoinstall

Other Tweaks During Setup

  • prevent double spaces
    There is a physical hardware problem with the spacebars on several of Dell's computers and this Alienware 15 is one of them. Tapping the spacebar near the edge results in a double space being typed and it is highly reproducible. At first, I thought adjusting the delay for repeating keys would help but I confirmed that it makes no difference. Literally, the only solution is to retrain my hands to hit the spacebar closer to the middle! I'm following this thread for any updates until I get the courage to try removing and cleaning the keys.

Developer Configuration

Eventually, I got the machine to function but that meant I was only at the starting block. Next, began the journey of making it fit for development. Here's a list of some of the things I required:
  • an iTerm2 replacement
    I chose Tilix and I'm VERY happy with it. I was able to recreate my "quake-like" terminal triggered by a hotkey. This required mapping Alt + ' to the command tilix --quake and then updating the settings so that the window closes whenever it loses focus.
  • install github & bitbucket ssh keys and .gitconfig (self-explanatory)
  • bring over all my setup/config files from github
  • apt install xclip to replace pbcopy and pbpaste
  • install oh-my-zsh via:
    sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
  • installed java
  • installed rust & cargo via rustup
  • download android studio and install it into the /opt folder (making sure to include CMAKE and the NDK for rust integration)
  • modify the .desktop launcher file to make certain that ANDROID_HOME is defined in the environment (rather than using local.properties files like a dinosaur)
    found here: ~/.local/share/applications/jetbrains-studio.desktop add: Exec=env ANDROID_HOME=/home/gmale/kg/setup/android/sdk /opt/android-studio-canary/bin/studio.sh %f
  • installed kvm for the android emulator with these instructions
  • installed pats-boostnote for note-taking and linked it to google drive using these instructions. This method worked best:
    sudo add-apt-repository ppa:alessandro-strada/ppa
    sudo apt install google-drive-ocamlfuse
    mkdir  ~/kg/work/google-drive
    google-drive-ocamlfuse ~/kg/work/google-drive
  • find a replacement for diffmerge (doesn't work on latest versions of ubuntu)
    Decided to go back to p4merge and it seems to work well. This post made it even easier.

May 3, 2015

Android: Dynamically Coloring Images with Picasso

I'm working on an app where the server provides certain icon images but only in one color. Needing to tint these images, I checked whether the tinting from Lollipop was backward compatible. Nope. Not in AppCompat. That would be too easy! So I had to find another way.

Spoiler alert: Turns out I didn't technically need Picasso.

The Basic Idea: Use ColorFilters!

Fortunately, next Google pointed me to my friend Paul's awesome blog. He gave an excellent overview on Programmatically Coloring Drawables that's worth a quick read. After adapting his solution to my specific needs, I decided to post about my code so that the next time this comes up, I know exactly what to do:

Picasso.with(viewHolder.itemView.getContext())
 .load(iconUrl)
 .fit()
 .centerInside()
 .into(viewHolder.currentWeatherIcon);

int iconColor = viewHolder.itemView.getResources().getColor(R.color.weather_icon_color);
viewHolder.currentWeatherIcon.setColorFilter(
 new PorterDuffColorFilter(iconColor, PorterDuff.Mode.SRC_IN)
);

The core idea was to apply a ColorFilter to the ImageView but, in my case, since I already had an ImageView, I didn't have to go the route of using a Transformer. Instead, I can just apply the ColorFilter to my ImageView, directly. Also, I could leverage a PorterDuffColorFilter since I knew I always want to use Porter Duff Compositing here.

One Step Further: Leverage the ViewHolder!

Given the way a ColorFilter works, I figured this could also be set on the ViewHolder, itself, during construction. So my final, working solution was along the lines of this:

public static class ViewHolder extends RecyclerView.ViewHolder {
    /* [other view holder stuff here] */
    public ImageView currentWeatherIcon;

    public ViewHolder(View v) {
        super(v);
        /* [other view holder stuff here] */
        currentWeatherIcon = (ImageView) v.findViewById(R.id.current_weather_icon);
        currentWeatherIcon.setColorFilter(
                new PorterDuffColorFilter(v.getResources().getColor(
                    R.color.weather_icon_color), PorterDuff.Mode.SRC_IN)
        );
    }
}

// elsewhere in code...
Picasso.with(viewHolder.itemView.getContext())
        .load(iconUrl)
        .fit()
        .centerInside()
        .into(viewHolder.currentWeatherIcon);

Of course, if I needed different icon colors, based on the data, then I'd have to set the color filter inside of onBindViewHolder but in my case, the icon color is always the same so this worked well. Bonus: this solution ended up not even requiring Picasso. I know I'll be coming back to this post in 8-12 months, after I've forgotten half this stuff...

Good thing I wrote it down!

Apr 26, 2015

Prevent Constant Security Popups on Genymotion with Google Play During ADB Installs

Problem

After installing Google Play services on Genymotion devices, I get a popup every time I try to install an APK. This popup prevents the adb install from completing until I either decline or accept. I've had this happen before but I keep forgetting how to turn it off. Googling doesn't help so I'm making this post for the next time I get into this mess.

Symptoms

A popup with the following message every time you try to install an app:

Allow Google to regularly check device activity for security problems, and prevent or warn about potential harm. Learn more in the Google Settings app.



Solution

Choose decline, then open the Google settings app (i.e. not the regular settings ... the other one. That's the key part I keep forgetting.) and turn off this option:

Scan device for security threats

Just to state the obvious, I think this is fine for an emulator but probably not something you want to disable on an actual phone that contains sensitive information. In that case, it's probably best to just "accept" the scanning, instead.

Apr 25, 2015

Upgrading to Google Play Services 7.0.0 Broke GCM

Problem

After upgrading from Google play services 6.5.87 to 7.0.0 I got compile errors in sections of code I hadn't touched.

Symptoms

I got errors similar to these:

  • error: package com.google.android.gms.gcm does not exist
    import com.google.android.gms.gcm.GoogleCloudMessaging;
  • error: package GoogleCloudMessaging does not exist
    if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
  • error: cannot find symbol
    GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);

Solution

Simply put: add the GCM API to your gradle file:

Due to Dex limit issues, I stopped importing the full/bulky GooglePlayServices dependency the moment Google made that an option. Instead, I selectively compile the APIs that we need. It seems that version 7.0.0 of Google Play Services is a bit more modular than the previous one and more APIs have have been split out into individual packages.

So to fix this problem, all I had to do was add the new GCM api to my gradle build file. Compile errors went away and both the command-line and IDE builds were happy again!

compile 'com.google.android.gms:play-services-gcm:7.0.0'
As an added bonus, the "location" APIs have also been split out. After adding that, I was finally able to get rid of the play-services-base dependency that I previously relied on for the GCM & location packages.

Jun 28, 2013

Installing Genymotion for Mac OS X

Problem

After installing Genymotion, it doesn't work. Running the application gives errors and the shell script fails with segment faults.

Symptoms

  • The application fails with the following error: Unable to load VirtualBox engine
  • Accessing any device information from the shell script gives the following error: Segmentation fault: 11

Solution


UPDATE [7/15/13]: in addition to my initial post below, I just found some good information in the Genymotion docs. Not sure how I missed that the first time around. It covers everything in my post and more.

You need to download and install Oracle's VirtualBox from the VirtualBox site. Once this is installed and present in your Applications folder, Genymotion works!

It's easy to miss but on the Genymotion download page, it actually tells you that you need to download this. Of course, in my haste, I completely overlooked the underlined portion below:

Oops! It's also worth noting that I had problems registering on their website--it refused to send me a confirmation email. Once I switched from Chrome to Safari, it worked just fine. Now, I finally have Genymotion working and all is right with the world.

Mar 30, 2013

Android Robolectric: java.lang.RuntimeException: Stub! while using libraries like JSONObject

Problem

While running a unit test under Robolectric on Android, the following error occurs:
java.lang.RuntimeException: Stub!

Symptoms

  • When you step through the code, portions of it work just fine
  • The exception occurs after returning from a constructor
  • Some library code is being used, like JSONObject

Solution

As stated in the Robolectric help documentation:

Make sure that robolectric and its dependencies (including JUnit) appear before the Android API jars in the classpath.

In other words, assuming you set things up correctly, then from that point forward, all you have to do is be sure that your classes are coming from either Robolectric or pure Java. In my case, the code under test was using JSONObject and that was coming from the android jar. To fix this, I had to:


  1. Add the JSONObject jar to my test project's dependencies. Depending on how your project is setup, that could be as simple as copying it to the lib folder
  2. Re-order the dependencies in the classpath such that Roboelectric and the other Java jars appear before anything Android-related. In Eclipse, that looks like this:

In this case, I pulled Robolectric and JSON up to the top and pushed android and maps down to the bottom. After this change, my test worked perfectly.