A 5-year-old phone is not going to the kindergarden, it is more likely to be found on the phone graveryard. One important reason for this is that its manufacturer stopped caring for it at least three years ago. Fortunately, there are other caretakers – people that maintain the latest android version also for five-year-old phone grannies – in form of custom ROMs.
So, is it possible to run the latest Android version on a device that’s 5 years old? Hell, yes! This article shows how.
My old HTC One S (aka ville, the older S4 processor not the “C2” with S3 processor), launched April 2012 runs terribly laggy on its latest stock anroid 4 firmware, last update 2013 – four years ago! That’s why it was retired a long time ago. At the beginning of 2017, when the first unofficial builds of lineage OS turned up, I was surprised to see that HTC One S was among them. I was wondering, how can such an old device run the latest android version? I had to see for myself.
TLDR; Android Nougat works surprisingly well, way better than the latest Stock version but HTC makes it a hell of a way to get there. Lets see what has to be done.
Note that all files on your device will be deleted during the process!
Unlock Bootloader
To be able to flash software to our HTC devices, HTC forces us create an account on htcdev.com and register our device there. Then we receive an unlock code as zip file which we can flash on our device.
For all Windows 8 (and also Windows 10, apparently) users who have problems getting the fastboot driver to work, whereas ADB works, this little registry edit did the trick for me.
In the bootloader make sure to remember your HBoot version, it will be required during the process.
I had to find out the hard way by flashing lineage 14.1 that there are requirements regarding the HBoot (HTC Bootloader) version: HBoot 2.16 is required.
So better check in advance. If you’re on HBoot 2.16 flash lineage and enjoy. If not, let the games beginn.
S-OFF & root
Upgrading HBoot needs S-OFF (Security off, enables writing to /system directory). On top of unlocking our bootloader at htcdev.com we need to risk bricking our phone to enalbe writing in the /system directory of our device.
Those two things are the reasons why I most likely won’t ever buy an HTC device again.
Anyway, with the One S, we’re lucky! S-OFF takes a bit of time but not much effort and comes at almost no risk, thanks to rumrunner (a good overview of other S-OFF Mechanisms is described here). Rumrunner makes it easy to get S-OFF, but it needs root access.
What? Why are we doing this again? We want to Upgrade HBoot, that’s why we need S-OFF, in order to get S-OFF, we need root. That’s the last unpleasant surpise, promise!
Lucky again, root access can be gained easily on HTC One S. I used Superboot: Just Download, execute Script on PC while Phone phone is in bootloader mode. Root done. More detailed description here.
Then just download rumrunner follow the instructions, wait for about 10 minutes (don’t worry, your phone is restarting about one million times) and you’re S-OFF!
Upgrade HBoot
We’re getting closer. Now, with S-OFF we can flash HBoot 2.16. I followed these instructions, and found the firmware here.
Caution: After flashing HBoot 2.16, the internal memory is really small, only 50 MB left. Obviously Android 4 and Hboot 2.16 are not good friends. Don’t worry, this is solved once Android 7 is flashed.
Flash Android 7.1.2
It’s finally gonna happen. We’ll flash Lineage 14.1, Android 7.1.2 (maintained by moozon, thanks so much!). But how, the internal memory is only 50MB, but the image is almost 300MB? I copied the image to an USB thumb drive, connected it to the phone via an USB OTG adapter and flashed via TWRP Recovery. TWRP also offers ADB sideloading, which might be an alternative, it you don’t have an USB OTG Adapter at hand.
Once flashed, you finally can enjoy the latest android version on your “antique” phone. 🥂
The One S with Android 7.1.2 almost works better than my HTC One M8 with Android 6! Why must HTC make it so hard to get there? And why are manufacturers not able to support their devices longer than 2 years, while the community or even single indivudals are able to do so for more than 5 years??
[EDIT 2017/06/13: Google just published the guaranteed updates for its devices: two years of upgrades and another year of security updates. While this is at least some formal guarantee (other manufacturer just don’t guarantee anything) it still is far from the five years provided by the community for my HTC One S… ]
I’m still waiting for a manufacturer that guarantees support for its devices for many years. That would really be a unique selling point! I can’t believe no manufacturer uses this USP on the highly competitive market for mobile devices. In addition, this would be so much more sustainable, and contribute to a green(er) IT. While the whole world complaints about planned obsolescence, why is there no manufacturer that uses this fact for positive marketing?
[EDIT 2017/07/01: HMD Global (owner of the Nokia brand) just announced that they intend to provide plain vanilla android phones with support “even after two years“. I looking forward to their next anouncements. My calls might just have been heard 👂]
What is Google doing, by the way? They introduce Safety Net, providing the oportunity for developers to hide their apps on Google Play from specific devices, such as the ones running custom ROMs. So now, no more Netflix, etc. for custom ROMs. Effectively, this is another punch for the custom ROM scene after the death of CyanogenMod.
Lets just hope custom ROM developers will not be discouraged by these facts and continue their imporant work, making success stories such as the one of my HTC One granny possible.
Thank you android community, XDA forums, custom ROM developers, etc. for sharing your work and never giving up! 👍
Earlier this year, while working on a project for TRIOLOGY GmbH, I once again used maven to write a version name into an application, using the mechanism described in my post Maven: Create a simple build number. As a more sophisticated version name was required for this project, we expanded it by a time stamp, SCM information (branch and commit), build number and a also created a special name for releases. You can find a how-to here – Version names with Maven: Creating the version name – which is the first part of a small series of blog posts on this topic.
The second part shows how the version name can be read from within the application. While writing the examples for the post, I wondered how many times I must have implemeted reading a version name from a file in Java. Way too often! So I decided that this would be the very last time I had to do it, and extracted the logic into a small library: versionName, availble on GitHub. What it does and how to use it is described in the second part of the post: Version names with Maven: Reading the version name.
Hopefully, this will be useful for someone else. Funny enough, in the new project I’m on, I’m about to reuse it once again. I’m glad I don’t have to write it again. Here’s to reusability 🍺
SonarQube will update it’s database during the process.
If you followed this post to set up your SonarQube instance and therefore use an SSH tunnel to access the SonarQube database, note that you can now get rid of this workaround. From SonarQube 5.2 the analyses can be run without direct contact to the database.
That is, you can also remove the database connection from your the configuration of the SonarQube plugin in jenkins.
Install new SonarQube instance
To install SonarQube 5.2, execute the following steps on your machine:
Note that the initial setup may take some minutes. So be patient.
The default login and passwords are admin / admin.
You might want to change the password right away!
Setup sonar plugin (the following bases on SonarQube Plugin 2.3 for Jenkins)
On the Jenkins frontend, go to Configure System
https://jenkins-.rhcloud.com/configure
Global properties,
tick Environment variables
Click Add name=SONAR_USER_HOME value=$OPENSHIFT_DATA_DIR
See here for more information.
Setup the Runner:
Navigate to SonarQube Runner
Click Add SonarQube Runner Name=
Then set up the plugin itself
Navigate to SonarQube
tick Enable injection of SonarQube server configuration as build environment variables
and set the following Name= Server URL:
Branch Specifier (blank for 'any'): origin/master Build Triggers: Tick Build when a change is pushed to GitHub Build Environment: Tick Prepare SonarQube Scanner environment Build | Execute Shell
cd $WORKSPACE
# Start the actual build
mvn clean package $SONAR_MAVEN_GOAL --settings $OPENSHIFT_DATA_DIR/.m2/settings.xml -Dsonar.host.url=$SONAR_HOST_URL
I’d also recommend the following actions Post-build Actions| Add post-build action| Publish JUnit test result report Test report XMLs=target/surefire-reports/TEST-.xml* Post-build Actions| Add post-build action| E-mail Notification Recipients=
Hit Apply.
Finally, press Save and start you first build. Check Jenkins console output for errors. If everything succeeds you should see the result of the project’s analysis on SonarQube’s dashboard.
At the time of writing OpenShift features Maven 3.0.4 and OpenJDK Server 1.7.0_85. Why would you want to change those? Best example is a Java8 project to be build on Jenkins. Can we just advise Jenkins to download the newest Oracle JDK and we’re good to go? Nope, it’s not that simple on OpenShift! Jenkins does download the new JDK, sets the JAVA_HOME variable and the correct PATH, but maven is always going to use the stock JDK. Why? Running this command provides the answer
The stock maven is setting its own environment variables that cannot be overridden by Jenkins!
So, in order to exchange the JDK, we need to exchange maven first.
SSH to the machines where your builds are executed (e.g. your slave node). The following example show what to do for maven 3.3.3:
cd $OPENSHIFT_DATA_DIR
mkdir maven
cd maven
wget http://apache.lauf-forum.at/maven/maven-3/3.3.3/binaries/apache-maven-3.3.3-bin.tar.gz
tar -xvf apache-maven-3.3.3-bin.tar.gz
rm apache-maven-3.3.3-bin.tar.gz
Edit maven config
vi $OPENSHIFT_DATA_DIR/maven/apache-maven-3.3.3/conf/settings.xml
Add the following to the tag (replace by your OpenShift UID first)
/var/lib/openshift//app-root/data/.m2
(press i button for edit mode, insert, then press esc button, enter :wq, finally press return button)
Browse to
https://jenkins-.rhcloud.com/configure
Set Environment variables PATH=$OPENSHIFT_DATA_DIR/maven/apache-maven-3.3.3/bin:$PATH M2_HOME=$OPENSHIFT_DATA_DIR/maven/apache-maven-3.3.3
And that’s it, your builds are now running on the custom maven!
This allows for using a specific JDK in Jenkins. You could just choose a specific JDK via Jenkins console. This is comfortable, but has one disadvantage: It takes a lot of memory (approx. 600MB per JDK), because the JDK is stored twice – compressed in cache to be sent to slave and again uncompressed to be used on the master. If you got enough memory, you’re done here.
However, In case you’re running a small gear with only 1GB of memory, you might want to save a bit of your precious memory. The following example shows how to do so for JDK 8 update 51 build 16.
On SSH:
cd $OPENSHIFT_DATA_DIR
mkdir jdk
cd jdk
wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u51-b16/jdk-8u51-linux-x64.tar.gz
tar -xvf jdk-8u51-linux-x64.tar.gz
rm jdk-8u51-linux-x64.tar.gz
This post showed how to build GitHub projects with Jenkins, Maven and SonarQube 4 on OpenShift. For starters, it used the Jenkins master node for running build jobs. However, when running on a small gear, the master node might run out of memory pretty fast, resulting in a reboot of the node during builds.
In order to resolve this issue, there are two options:
limitting the memory of the build or
running the build on a slave node.
As spawning additional nodes is easy in a PaaS context such as OpenShift and provides a better performance than running builds with small memory, the slave solution seems to be the better approach.
This post shows how.
Create new DYI app as a slave node (a how-to can be found here), name the node e.g. slave
Create node in Jenkins
Go to Jenkins web UI and create new node:
https://jenkins-.rhcloud.com/computer/new
Set the following values: Remote FS root:/app-root/data folder on slave. Typically this is /var/lib/openshift//app-root/data/jenkins, you can find out by SSHing to the slave node and calling
echo $OPENSHIFT_DATA_DIR/app-root/data/jenkins
Labels: Some label to use within builds to refer the node, e.g. OS Slave #1 Host: the slave’s hostname, e.g. slave-.rhcloud.com
As the different cartridges (jenkins and DIY) have different environment variables for their local IP addresses ($OPENSHIFT_JENKINS_IP vs $OPENSHIFT_DIY_IP) we’ll have to improvise at this point. There are two options: Either
Replace all occurrences of $OPENSHIFT_JENKINS_IP
In all builds and in
https://jenkins-.rhcloud.com/configure
Sonar | Sonar installations Database URL:jdbc:postgresql://$OPENSHIFT_DIY_IP:15555/sonarqube
or
Create an $OPENSHIFT_JENKINS_IP environment variable on your slave machine
rhc env set OPENSHIFT_JENKINS_IP=<value of $OPENSHIFT_DIY_IP> -a slave
You can find out the value of $OPENSHIFT_DIY_IP by SSHing to the slave and execute
echo $OPENSHIFT_DIY_IP
I’d love to hear suggesstions that do better 😉
Adapt Build
Easiest way is to not use the master at all.
To do so, go to
https://jenkins-.rhcloud.com/configure
and set # of executors to 0.
Hit Apply
Limit memory usage.
Make sure the slave does not run out of memory (which leads to a restart of the node): Global properties | Environment variables name:MAVEN_OPTS value:-Xmx512m
Hit Save.
Now run a build. It should run on the slave and hopefully succeed 🙂
After finishing the migration from Actionbarsherlock to appcompat described in the first Post on modernizing android UIs, it turns out there are even more things to modernize in terms of android UIs.
Tabs and design support library
If you used Tabs within the ActionBar, after migrating to appcompat-v7 API 22 you might recognize a warning, that tells you that they are now deprecated.
In order to modernize those, you should use the design support library that was added to the Android SDK with API level 22. Similar to the appcompat -v7, the design support library provides backports of material design components of Android lollipop (5.x) for older versions of Android.
Here’s the steps that rid you of the deprecation warning
Add the design library to your eclipse workspace and link it with your project in pretty much the same way as appcompat described in the first Post. The library can be found on the following path: <sdkdir>/extras/android/support/design.
Import it into eclipse as Existing Android Code Into Workspace, change the build target to level 22, and link the appcompat project with it.
Once you’re done with this and have the design support library up an running, you could modernize your app by using more of the library’s features like navigation drawers, floating labels and buttons, snackbars, collapsing toolsbars, etc. See this blog post for more features.
The screenshots bellow show a before-after comparison – deprecated tabs vs material design tabs.
Tabs: design support library, API-22
Tabs: AppCompat-v7, API-22, deprecated
Checkboxes to switches
Android API level 14 introduces the switch component, that according to google should be used when only one option is available. For API levels < 14 there’s no such thing as switches. So we’ll have to rely on checkboxes there. Here’s how to replace checkbox preferences by switches for devices running API level 14 and above
However, If you want to keep compatibility with API levels < 14, you’re only choice is to keep redundant copies of the same preferences.xml in
res/xml/ that contains the preferences with CheckBoxPreferences and
res/xml-v14/ that contains the same file with SwitchPreferences.
Another before-after comparison is shown bellow – checkboxes vs switches.
Preferences: AppCompat-v7, with checkboxes
AppCompat-v7, API-22 with switches
Using Action Buttons
Action Buttons are icons that realize the most important actions on the actionBar if there is enough room to display them. These could have been used in Actionbarsherlock already, but if you still didn’t modernized them then it’s about time 🙂
One reason for not using action buttons might be that you don’t have suitable icons. Here’s the solution: Google provides a huge amount of material design icons under open source (CC-BY) license. They can be found on this site, or you can just clone their git repository.
So it’s as easy as that
Choose proper icons for your actions and copy them to your res/drawable-xyz folders
This post shows by example the steps that are necessary for migrating an android application from Actionbarsherlock to Material Design (introduced in android KitKat/Version 5.x/API level 21/22), while keeping compatibility with at least android Gingerbread/Version 2.3 /API level 9). It uses the appcompat-v7 library on API level 22. The app that was migrated (nusic) in this example is developed using Eclipse, Maven and RoboGuice.
Before-after comparison
Let’s begin with some before-after screenshots:
ActionBarSherlock
AppCompat-v7, API-22
Preferences: ActionBarSherlock
Preferences: AppCompat-v7, API-22
Basic migration (appcompat-v7)
The following lists the steps that were implemented in order to change the app as depicted in the screenshots above.
RoboSherlockPreferenceActivity -> Write your own RoboAppCompatPreferenceActivity that looks like this (or as described here) but is derived from RoboPreferenceActivity(see here for the class that was used in the example).
Then derive your class from it as before with RoboSherlockPreferenceActivity.
Replacing methods
getSherlockActivity() -> getActivity()
getSupportMenuInflater() -> getMenuInflater()
Fixing imports
android.view.Menu
android.support.v7.app.ActionBar
Updating proguard.cfg
Remove actionbarsherlock
Add
# support4, appcompat-v7, design support
-dontwarn android.support.**
-keep class android.support.** { *; }
-keep interface android.support.** { *; }
TL;DR; A way of exporting iTunes playlists with relative paths is described here.
iTunes Export
Once upon a time I wrote a little tool for exporting playlists from Songbird/Nightinale. After migrating to iTunes, I was looking for a tool that provides the same functionality for iTunes. Fortunately, there already is one: iTunes Export. It’s twofold – you can either use a UI or a console version. I’m more the console type of guy, so my choice is clear. The latest release, version 2.2.2, was released in 2010, almost 5 years ago… and it still works with iTunes 12.1.2 – it’s a miracle! And it’s fast – my approximately 100 playlists are exported in less than 2 minutes.
Relative Playlist workaround
Among all those parameters of iTunes export, there is none for creating relative playlists, though. However, we can use a workaround for achieving this, by combining the musicPath and musicPathOld parameters. Here’s what the doc says:
musicPath
iTunes Export will use the absolute location of your music files in the playlist. iTunes Export accepts a command line parameter that will override this default. Example:
java -jar itunesexport.jar -musicPath="c:\My Music Directory"
musicPathOld
Tunes Export will only apply the prefix to tracks stored in the directory configured in iTunes as the iTunes Music Folder location. Files stored in a different directory will not have the prefix applied.If you only wish to override a portion of the music path you can specifi the musicPathOld parameter. iTunes Export will replace this path with the musicPath parameter instead of replacing the default music path.
java -jar itunesexport.jar -musicPathOld="c:\My Old Path"
A bit complicated, eh?!
Exporting playlists with relative paths by example
I’ll point out how we can use those parameters by a small example. Imagine the following folder structure
If we had a playlist call playlistX that contained Song1 and Song2 (anyone remember a band called Blur? 🙂 ) and we would export it without further parameters to the Playlists folder, it would look like this:
What we’re going to do is replace the absolute part with a relative one (in respect to the destination folder for playlists). In our example: Replace “D:\Music” by “..” because it’s the parent folder of “D:\Music\Playlists“. That’s exactly what the parameters mentioned above are for! musicPathOld is the the part that is going to be replaced by musicPath. Speaking of which, our call to iTunesExport looks like this: