Saturday, December 30, 2017

How to update ProGuard for Android SDK

If still using Eclipse for Android app development and targeting newer Android SDK versions you may find ProGuard failing with this error message:
Can't process class [junit/runner/Version.class] (Unsupported class version number [52.0] (maximum 51.0, Java 1.7)) 
Proguard returned with error code 1. See console
Proguard Error 1 
Output: 
java.io.IOException: Can't read [U:\Android\android-sdk\platforms\android-27\android.jar] (Can't process class [junit/runner/Version.class] (Unsupported class version number [52.0] (maximum 51.0, Java 1.7))) 
 at proguard.InputReader.readInput(InputReader.java:230) 
 at proguard.InputReader.readInput(InputReader.java:200) 
 at proguard.InputReader.readInput(InputReader.java:178) 
 at proguard.InputReader.execute(InputReader.java:100) 
 at proguard.ProGuard.readInput(ProGuard.java:196) 
 at proguard.ProGuard.execute(ProGuard.java:78) 
 at proguard.ProGuard.main(ProGuard.java:492) 
Caused by: java.io.IOException: Can't process class [junit/runner/Version.class] (Unsupported class version number [52.0] (maximum 51.0, Java 1.7)) 
 at proguard.io.ClassReader.read(ClassReader.java:112) 
 at proguard.io.FilteredDataEntryReader.read(FilteredDataEntryReader.java:87) 
 at proguard.io.JarReader.read(JarReader.java:65) 
 at proguard.io.DirectoryPump.readFiles(DirectoryPump.java:65) 
 at proguard.io.DirectoryPump.pumpDataEntries(DirectoryPump.java:53) 
 at proguard.InputReader.readInput(InputReader.java:226) 
 ... 6 more 
Caused by: java.lang.UnsupportedOperationException: Unsupported class version number [52.0] (maximum 51.0, Java 1.7) 
 at proguard.classfile.util.ClassUtil.checkVersionNumbers(ClassUtil.java:140) 
 at proguard.classfile.io.LibraryClassReader.visitLibraryClass(LibraryClassReader.java:89) 
 at proguard.classfile.LibraryClass.accept(LibraryClass.java:301) 
 at proguard.io.ClassReader.read(ClassReader.java:86) 
 ... 11 more 

Cause

Newer versions of Android platform are compiled as Java 8 binary and ProGuard 4.7 coming with Android SDK Tools doesn't support it (last updated with SDK Tools Revision 17, March 2012).
> cd \android-sdk\tools\proguard\bin
> proguard
ProGuard, version 4.7
Usage: java proguard.ProGuard [options ...]

Solution

Java 8 support is available with Proguard 5.x onwards, so we will update to the latest version available, in my case 5.3.3 from Apr 2017.
  1. Move / rename the old version:
    rename \android-sdk\tools\proguard\ proguard.old
  2. Download the zip file from SourceForce
     
  3. Extract the contents into \android-sdk\tools\ renaming the new folder to proguard
    rename \android-sdk\tools\proguard5.3.3\ proguard
  4. Check the new version:
    > cd \android-sdk\tools\proguard\bin\
    > proguard
    ProGuard, version 5.3.3
    Usage: java proguard.ProGuard [options ...]
  5. Copy the Android configuration files over:
    > cd \android-sdk\tools\
    > copy proguard.old\proguard-*.txt proguard\
    proguard.old\proguard-android-optimize.txt
    proguard.old\proguard-android.txt
    proguard.old\proguard-project.txt
            3 file(s) copied.
Exporting the APK package again should work now.

Monday, December 11, 2017

Fix: Wireless Display "Your display couldn't connect"

 You may find when trying to project to an wireless display Windows will fail with the following error message, even though other similar computers on your network can project just fine:

    Something went wrong.
    Your display couldn't connect.



Intel ProSet/Wireless Software > Modify > Wireless Software Extensions





Windows Defender Security Center > Firewall & network protection > Restore firewalls to default





Try again projecting and confirm it is working fine now.


Sunday, November 19, 2017

Inateck FE2010 USB 3.0 Portable 2.5" HDD Enclosure


Inatek FE2010 HDD Enclosure  (11.99 GBP on Amazon.co.uk):
  • USB 3.0
  • UASP transfer protocol
  • JMS578 chipset
  • supports 2.5" SATA-I, II, III HDDs and SSDs.

Tested with:

1. Internal SATA III



 

2. Inatek FE2010 - USB 3.0 port




3. Inatek FE2010 - USB 2.0 port



4. Kingston SNA-DC/U - USB 2.0 port





Monday, October 16, 2017

Enabling HTTP Compression in GlassFish / Payara

Enable HTTP compression on GlassFish / Payara servers to reduce response time and make web applications faster.

Edit GLASSFISH_HOME//glassfish/domains/domain1/config/domain.xml

<network-config>
  <protocols>
    <protocol name="http-listener-1">
      <http max-connections="250" default-virtual-server="server"
            compression="on" 
            compression-min-size-bytes="128"
            compressable-mime-type="text/html,text/css,text/plain,text/xml,application/javascript,application/json">
        <file-cache></file-cache>
      </http>
      <ssl></ssl>
    </protocol>
    <protocol name="http-listener-2" security-enabled="true">
      <http max-connections="250" default-virtual-server="server"
            compression="on" 
            compression-min-size-bytes="128"
            compressable-mime-type="text/html,text/css,text/plain,text/xml,application/javascript,application/json">
        <file-cache></file-cache>
      </http>
      <ssl classname="com.sun.enterprise.security.ssl.GlassfishSSLImpl" cert-nickname="s1as"></ssl>
    </protocol>

Example with a reduction from 136 KB down to 23 KB (5.9x), considerabily reducing the network traffic.

Before (without HTTP compression)

After (with HTTP compression)

Sunday, January 15, 2017

Eclipse: Integrating Firebase Cloud Messaging into your Android app

Following requests from some of you on the previous article on Firebase Analytics, today we're going to cover Firebase Cloud Messaging integration in your application developed with Eclipse. It is not much different from the Firebase Analytics integration, so here we go.


Firebase Analytics libraries


dandar3/android-google-firebase-README gives you an overview of the Firebase libraries available to use with your Eclipse project. Have a look at the two YouTube videos at the end covering the installation of the Subversive plugin and the steps to easily import entire project sets like dandar3/android-google-firebase-messaging in your workspace.

You will observe compile errors like Tag ... attribute name has invalid character '$' after importing libraries like android-google-firebase-iid or android-google-firebase-common. To resolve the issue you will have to manually replace ${applicationId} with your application id (Java package) in all AndroidManifest.xml files (use CTRL+H > File Search >  Replace...)






Importing google-services.json


Read the Firebase documentation and follow the Processing the JSON File information to manually generate your own values.xml

If you're having trouble with getting this right you could use Android Studio to import firebase/quickstart-android GitHub project, deploy your google-services.json into it and then copy the generated values.xml out.
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="default_web_client_id" translatable="false">{YOUR_CLIENT}/oauth_client/[first client_type 3]</string>
    <string name="gcm_defaultSenderId"   translatable="false">project_info/project_number</string>
    <string name="firebase_database_url" translatable="false">project_info/firebase_url</string>
    <string name="google_app_id"         translatable="false">{YOUR_CLIENT}/client_info/mobilesdk_app_id</string>
    <string name="google_api_key"        translatable="false">{YOUR_CLIENT}/services/api_key/current_key</string>
    <string name="google_storage_bucket" translatable="false">project_info/storage_bucket</string>
</resources>


AndroidManifest.xml changes


This is the undocumented part that can only be observed by looking at the AndroidManifest.xml generated by Google Services Gradle plugin (app\intermediates\manifests\full\debug\AndroidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest>
    [...]

    <!-- Required permission for App measurement to run. -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <permission
        android:name="${applicationId}.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" /> 
 
    <application>
        [...]
 
        <receiver
            android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
            android:enabled="true"
            android:exported="false" >
        </receiver> 

        <receiver
            android:name="com.google.android.gms.measurement.AppMeasurementInstallReferrerReceiver"
            android:enabled="true"
            android:permission="android.permission.INSTALL_PACKAGES" >
            <intent-filter>
                <action android:name="com.android.vending.INSTALL_REFERRER" />
            </intent-filter>
        </receiver>

        <service
            android:name="com.google.android.gms.measurement.AppMeasurementService"
            android:enabled="true"
            android:exported="false" />

        <receiver
            android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
            android:exported="true"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

                <category android:name="${applicationId}" />
            </intent-filter>
        </receiver>

        <!--
            Internal (not exported) receiver used by the app to start its own exported services
            without risk of being spoofed.
          -->
        <receiver
            android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"
            android:exported="false" /> 

        <!-- 
            FirebaseInstanceIdService performs security checks at runtime,
            no need for explicit permissions despite exported="true"
          -->
        <service
            android:name="com.google.firebase.iid.FirebaseInstanceIdService"
            android:exported="true" >
            <intent-filter android:priority="-500" >
                <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
            </intent-filter>
        </service>

        <provider
            android:name="com.google.firebase.provider.FirebaseInitProvider"
            android:authorities="${applicationId}.firebaseinitprovider"
            android:exported="false"
            android:initOrder="100" />

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

        </application>

</manifest>

On top of that you will have to add your own FirebaseMessagingService and FirebaseInstanceIdService implementations to handle messages and registration tokens as detailed in Firebase documentation > Set Up a Firebase Cloud Messaging App on Android.

You can find a working sample on GitHub in firebase/quickstart-android in the messaging module. I would recommend reading the module README.md, app/src/main/AndroidManifest.xml and the related classes in /app/src/main/java/com/google/firebase/quickstart/fcm/


Testing

 

If everything was set up correctly and you have implemented a similar logic to the classes mentioned in the above documentation and working sample, you can subscribe through the app to a topic and have messages sent to those subscribed that way (see Send Tipic Messages with Firebase Console) or you can obtain the registration token and send messages to that one device (see Send a Notification to an Android Device).


Here is a test using the sample app. Push Log Token button to get the registration token in logcat:
D/MainActivity( 1983): InstanceID Token: cNtIOWfDiJY:APA91bFat1GNW7yEAj5HdSzsQsPsn-CzN6KtxxzT0h41lo_w5Hg2p8_PJVAgY7hWx_2RrHSmq8Bc7emA_NDXqHEVfzj8hqnbucnJKSeMU48jS2DEdJLcM5eYha3KLIIRvwOie17voHJJ



which then you can use to send messages from Firebase Console to:
D/MyFirebaseMsgService( 1983): From: 348775509088
D/MyFirebaseMsgService( 1983): Message Notification Body: Hello from Firebase Console!
See sendNotification() and the note at the end of MyFirebaseMessagingService.java if you want to transform the message into a system notification.