Showing posts with label Android. Show all posts
Showing posts with label Android. Show all posts

Tuesday, April 2, 2024

Eclipse ADT plugin with D8 dexer

If still developing with Eclipse for Android using Google ADT plugin you may encounter the error below when dexing some newer libraries:

processing archive U:\Workspaces\AndroidX Browser\github\android-androidx-browser\libs\androidx-browser.jar...
processing META-INF/androidx.browser_browser.version...
processing androidx/browser/trusted/splashscreens/SplashScreenVersion.class...
... Uncaught translation error: com.android.dx.cf.code.SimException: ERROR in androidx.browser.customtabs.CustomTabsService$1.newSessionInternal:(Landroid/support/customtabs/ICustomTabsCallback;Landroid/app/PendingIntent;)Z: invalid opcode ba - invokedynamic requires --min-sdk-version >= 26 (currently 13) Uncaught translation error: com.android.dx.cf.code.SimException: ERROR in androidx.browser.trusted.ConnectionHolder.getServiceWrapper:()Lcom/google/common/util/concurrent/ListenableFuture;: invalid opcode ba - invokedynamic requires --min-sdk-version >= 26 (currently 13) Uncaught translation error: com.android.dx.cf.code.SimException: ERROR in androidx.browser.trusted.TokenContents.createToken:(Ljava/lang/String;Ljava/util/List;)[B: invalid opcode ba - invokedynamic requires --min-sdk-version >= 26 (currently 13) Uncaught translation error: com.android.dx.cf.code.SimException: ERROR in androidx.browser.trusted.TrustedWebActivityServiceConnectionPool.connect:(Landroid/net/Uri;Ljava/util/Set;Ljava/util/concurrent/Executor;)Lcom/google/common/util/concurrent/ListenableFuture;: invalid opcode ba - invokedynamic requires --min-sdk-version >= 26 (currently 13)

Let's try to understand how Google ADT plugin works:
DexWrapper.java (ADT plugin) looks for ANDROID_SDK\build-tools\xx.yy.zz\lib\dx.jar and loads it, then calls run(Arguments) method in com.android.dx.command.dexer.Main through reflection.

Since those classes do not exist in newer D8 dexer you won't be able to use directly ADP with D8.

Unknown error: Unable to build: the file dx.jar was not loaded from the SDK folder!

But what if we create an adapter that sits in between and knows how to receive the dx.jar call and transfer it to d8.jar?
And that is exactly what dandar3/android-dx-d8-adapter does. 

1. Install `build-tools` version 33.0.3, that will come with D8 (and without DX) and is the last version that is built with Java 8 (runtime version used to run Eclipse and JDT):

> tools\bin\sdkmanager "build-tools;33.0.3"
done
> dir build-tools\33.0.3\lib\d*.jar
03.04.2024 00:03 8.190.549 d8.jar

2. Clone dandar3/android-dx-d8-adapter and build from sources (see README.md) or download the latest build from Releases page (right side).

3. Save as `dx.jar` next to `d8.jar` in build-tools\33.0.0\lib\

4. Restart Eclipse.

5. Rebuild your project, look in `Console` > `Android` view:

Android DX to D8 adapter
Version 1.0 (Feb 2023
https://github.com/dandar3/android-dx-d8-adapter

DX called with:
 verbose    = false
 forceJumbo = false
 jarOutput  = true
 outName    = U:\Workspaces\Android\[...]\demo\bin\dexedLibs\androidx-arch-core-common-95b383e1e508bb35b6c34b9ff46ba8ce.jar
 fileNames  = [U:\Workspaces\Android\[...]\android-androidx-arch-core-common\bin\androidx-arch-core-common.jar]

D8 called with:
  jar       = U:\Android\android-sdk-eclipse\build-tools\33.0.3\lib\d8.jar
  arguments = [--output, U:\Workspaces\Android\Eclipse [ADT]\__TESTS\AndroidX Browser\demo\bin\dexedLibs\androidx-arch-core-common-95b383e1e508bb35b6c34b9ff46ba8ce.jar, U:\Workspaces\Android\Eclipse [ADT]\__TESTS\AndroidX Browser\github\android-androidx-arch-core-common\bin\androidx-arch-core-common.jar]
  elapsed  = 00:00:00.794

Thanks to iRootS and Bolfish.

Wednesday, June 8, 2022

Updating `dx.jar` library to fix `Dx unsupported class file version 52.0`

If you're still developing with Eclipse & Google ADT plugin you may encountered this error when trying to use Java libraries compiled with JDK 1.8:

[2022-05-11 08:55:53 - demo] Using default Build Tools revision 25.0.3
[2022-05-11 08:55:53 - demo] Starting full Post Compiler.
[2022-05-11 08:55:53 - demo] Dx Using Pre-Dexed androidx-versionedparcelable-2b1eb8b8b275a1d7c02af96379c6a5af.jar <- R:\Workspace\androidx-versionedparcelable\libs\androidx-versionedparcelable.jar
[2022-05-11 08:55:53 - demo] Dx Using Pre-Dexed androidx-lifecycle-common-eb8ba09572c04143f806576a1b143ada.jar <- R:\Workspace\androidx-lifecycle-common\libs\androidx-lifecycle-common.jar
[2022-05-11 08:55:53 - demo] Dx Pre-Dexing R:\Workspace\androidx-annotation-experimental\libs\androidx-annotation-experimental.jar -> androidx-annotation-experimental-b634477a1038eeb097daf1a0fd5faecf.jar
[2022-05-11 08:55:53 - demo] Dx processing archive R:\Workspace\androidx-annotation-experimental\libs\androidx-annotation-experimental.jar...
[2022-05-11 08:55:53 - demo] Dx processing META-INF/androidx.annotation_annotation-experimental.version...
[2022-05-11 08:55:53 - demo] Dx processing META-INF/annotation-experimental_release.kotlin_module...
[2022-05-11 08:55:53 - demo] Dx processing androidx/annotation/OptIn.class...
[2022-05-11 08:55:53 - demo] Dx
PARSE ERROR:
[2022-05-11 08:55:53 - demo] Dx unsupported class file version 52.0
...while parsing androidx/annotation/OptIn.class
[2022-05-11 08:55:53 - demo] Dx 1 error; aborting
[2022-05-11 08:55:53 - demo] Conversion to Dalvik format failed with error 1
 

Looking at DexWrapper.java (Google ADT Plugin) it calls classes from android-sdk\build-tools\25.0.3\lib\dx.jar through reflection. 

public final class DexWrapper {

private final static String DEX_MAIN = "com.android.dx.command.dexer.Main"; //$NON-NLS-1$
private final static String DEX_CONSOLE = "com.android.dx.command.DxConsole"; //$NON-NLS-1$
private final static String DEX_ARGS = "com.android.dx.command.dexer.Main$Arguments"; //$NON-NLS-1$
...
public synchronized IStatus loadDex(String osFilepath) {
...
// get the classes.
Class<?> mainClass = loader.loadClass(DEX_MAIN);
Class<?> consoleClass = loader.loadClass(DEX_CONSOLE);
Class<?> argClass = loader.loadClass(DEX_ARGS);
...
// now get the fields/methods we need
mRunMethod = mainClass.getMethod(MAIN_RUN, argClass);

mArgConstructor = argClass.getConstructor();
mArgOutName = argClass.getField("outName"); //$NON-NLS-1$
mArgJarOutput = argClass.getField("jarOutput"); //$NON-NLS-1$
mArgFileNames = argClass.getField("fileNames"); //$NON-NLS-1$
mArgVerbose = argClass.getField("verbose"); //$NON-NLS-1$
mArgForceJumbo = argClass.getField("forceJumbo"); //$NON-NLS-1$

mConsoleOut = consoleClass.getField("out"); //$NON-NLS-1$
mConsoleErr = consoleClass.getField("err"); //$NON-NLS-1$

That dx.jar is an old version of Google Dex library (compiled 3 Apr 2017, version 1.12 if you decompile dx.jar\com\android\dx\Version.class) which didn't support Java 1.8 compiled classes back then. The support was introduced in 21 Feb 2017 but wasn't included in that release back then:

Commit: Allow version 52.0 class files (21 Feb 2017)

-   private static final int CLASS_FILE_MAX_MAJOR_VERSION = 51;
+ private static final int CLASS_FILE_MAX_MAJOR_VERSION = 52;

/**
* Does the actual parsing.
*/
private void parse0() {
...
if (!isGoodVersion(getMinorVersion0(), getMajorVersion0())) {
throw new ParseException("unsupported class file version " +
getMajorVersion0() + "." +
getMinorVersion0());
}

Since 25.0.3 is the last version of Android SDK Build Tools to work with Google ADT plugin for Eclipse in terms of having libraries and structure in a way that the plugin knows and understands, we update dx.jar in-place with a newer version that does support Java 1.8 binaries. Now this will not solve all the problems with newer Java 1.8 language constructs, but at least will give us a try at using newer Android library, maybe there have parts that we need and don't use newer language features.

Google DEX library sources are available on GitHub at aosp-mirror/platform_dalvik/dx/ and did gain support for version 52 class files, so we can in theory just compile it and drop it in. Unfortunately as support for Eclipse was dropped around 2017 some classes needed by Google ADT plugin (e.g. DxConsole) were shed with this commit: Make dx code re-entrant. Remove static state, clear biggest caches after running.

To reintroduce some of the support for the Google ADT plugin we forked the project on GitHub ( dandar3/platform_dalvik) and made some small changes to bring back DxConsole which is called from ADT plugin through reflection to supply stdout and stderr streams.
https://github.com/dandar3/platform_dalvik/commit/64e9c9b6dbb5606d2d61d0c512f3f7cc80b6788f    

You can clone and build the project yourself or download the JAR from the Release tab and place into your android-sdk\build-tools\xxx\lib

git clone https://github.com/dandar3/platform_dalvik.git
mvn clean package copy "target\dx-1.16-ADT.jar" "%ANDROID_HOME%\build-tools\25.0.3\lib\dx.jar*" 

Simply rebuild your project in Eclipse, it should work now.

Sunday, June 7, 2020

Android 8.1 Oreo on Nexus 7 2012 WiFi

Thanks to ud4, LineageOS team, TeamWin and everyone else that made this build possible.
  1. Backup / transfer personal data (texts, photos, videos, apps, etc)

  2. Read [ROM][grouper] [8.1.0] [UNOFFICIAL] LineageOS 15.1 [2020/05/22]
    • download lineage-15.1-20200522-UNOFFICIAL-grouper.zip (270 MB)
    • download MindTheGapps-8.1.0-arm-20180808-grouper.zip (53 MB)

  3. Download Android SDK / platform-tools

  4. Install TWRP (Team Win Recovery Project) for Nexus 7 2012 Wifi
    (twrp-3.3.1-0-grouper.img, 9.6 MB)

    Follow Fastboot Install Method (No Root Required) section.

  5.  Boot into recovery mode (TWRP) with:
    adb reboot-bootloader
    Use Volume Up or Down keys to select "Recovery Mode", then press "Power".

  6. (Recommended) In TWRP, choose Wipe > Advanced Wide > Change File System for cache and  Data from EXT4 to F2FS


  7. Back to TWRP main screen > Advanced > ADB Sideload > Swipe to Start Sideload


  8. Sideload new ROM from your PC:
    adb sideload lineage-15.1-20200522-UNOFFICIAL-grouper.zip

  9. Sideload GApps ROM from your PC:
    adb sideload MindTheGapps-8.1.0-arm-20180808-grouper.zip

  10. Reboot system




Friday, April 3, 2020

Android emulator with Windows Hypervisor Platform (WHPX)

You can run the Android emulator with Windows Hypervisory Platform (WHPX) starting with Windows 10 April 2018 Update (build 1803).

Check whether you have support for Windows WHPX by going to Control Panel > Programs and Features (appwiz.cpl) > Turn Windows features on and off (left):


If so, you have to uninstall Intel HAXM that you must likely used before for hardware acceleration and go ahead with Windows WHPX.

You can see all the details on Android's Configure hardware acceleration for the Android Emulator page > Configure VM acceleration on Windows.

You can run the AVD from command line to see what it uses:

> ANDROID-SDK\emulator\emulator.exe -list-avds
...

> ANDROID-SDK\emulator\emulator.exe -avd <avd_name>
...
Windows Hypervisor Platform accelerator is operational
...

~~~

Resources:


Sunday, December 8, 2019

Eclipse Android Manifest merger

The Android manifest merger is available starting with SDK Tools, Revision 20 (June 2012) and it merges your application AndroidManifest.xml with the AndroidManifest.xml's defined in the libraries added to your application.
General notes:
  • Build system
    • Added automatic merging of library project manifest files into the including project's manifest. Enable this feature with the manifestmerger.enabled property.

Also checkout the Google I/O 2012 - What's New in Android Developers' Tools starting at 48:59




Here is an example with dandar3/android-google-play-services-ads 18.3.0 library added to your application.

1. Your application.properties:
...
target=android-28
manifestmerger.enabled=true
android.library.reference.1=../google-play-services-ads

2. Your AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    package="com.example.googleplayservices.ads"
    android:versioncode="1"
    android:versionname="1.0">

    <uses-sdk
        android:minsdkversion="15"
        android:targetsdkversion="29">

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE">
    <uses-permission android:name="android.permission.INTERNET">

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name">
        <activity
            android:label="@string/app_name"
            android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- Sample AdMob App ID: ca-app-pub-3940256099942544~3347511713 -->
        <meta-data
            android:name="com.google.android.gms.ads.APPLICATION_ID"
            android:value="ca-app-pub-3940256099942544~3347511713" />

    </application>   

</manifest>

3. Check out the resulting bin\AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.example.googleplayservices.ads">

    <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="29"/>

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.INTERNET"/>

    <application android:allowBackup="false" android:icon="@drawable/ic_launcher" android:label="@string/app_name">
        <activity android:label="@string/app_name" android:name="com.example.googleplayservices.ads.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <!-- Sample AdMob App ID: ca-app-pub-3940256099942544~3347511713 -->
        <meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-3940256099942544~3347511713"/>

        <!-- Include the AdActivity and InAppPurchaseActivity configChanges and themes. -->
        <activity android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize" android:exported="false" android:name="com.google.android.gms.ads.AdActivity" android:theme="@android:style/Theme.Translucent"/>

        <provider android:authorities="com.example.googleplayservicesads.mobileadsinitprovider" android:exported="false" android:initOrder="100" android:name="com.google.android.gms.ads.MobileAdsInitProvider"/>
        <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/>

    </application>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>    

</manifest>

Tuesday, September 17, 2019

ADB.exe crashing 0xc000005 on winusb.dll

An issue I've had on my work laptop with adb.exe crashing with 0xc000005 for last couple of months, I finally managed to fix it today after several attempts in various ways.


Monitoring the process with Process Monitor would reveal that is crashes after working with WINUSB.dll (ignore the highlight in blue, that was on the first attempt checking, the file should not be there)


SFC scan shows no problems with the file.
sfc /SCANFILE=C:\Windows\SysWOW64\winusb.dll

Trying the same Android SDK installation in the Windows Sandbox VM shows it working correctly, so it must be something on my physical system. The winusb.dll files on both systems show the same version, although on copying to the phyical system in te mporary location and comparing with FC /B clearly shows differences.

Now with the new version of SysWow64\winusb.dll at hand, I would try to replace the original on the physical system without success as it is owned and protected by Trusted Installer service.

Searching the web for a tool that would allow to run a process (e.g. cmd.exe or explorer.exe) as TrustedInstaller user found ExecTI from WinAero.


This would allow me to rename the old file and copy the new one in from the clean Sandbox image.

cd /D %WINDIR%\SysWow64
rename winusb.dll winusb.dll.BROKEN
copy WHERE_YOU_COPIED_IT_FROM_SANDBOX\winusb.dll winusb.dll

Running ADB again would show it working succesfully now!

adb --version
Android Debug Bridge version 1.0.41
Version 29.0.4-5871666
Installed as D:\Program Files\Android\android-sdk\platform-tools\adb.exe

Tuesday, April 17, 2018

Android SDK for development with Eclipse ADT

If you are still using Eclipse ADT for Android development, this is a tutorial to help you with installing the latest possible Android SDK / Tools that would work with last Eclipse ADT plugin.

  1. Eclipse IDE for Java Developers 4.7.3a
    http://www.eclipse.org/downloads/packages/eclipse-ide-java-developers/oxygen3a
  2. Eclipse ADT plugin - 23.0.7.2120684 (Aug 2015)
    https://dl-ssl.google.com/android/eclipse/
  3. Java JDK 1.8.0_172
    http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
Now onwards to Android SDK & Tools.

Standard choice 

  1. SDK Tools 25.2.5 (Jan 2017)

    Extract into your chosen location e.g. android-sdk (note: do not extract on top of an existing installation, you can have as many Android SDK installations you want - create a new location or backup the old and remove / rename).
    Open a command line / shell and test by calling sdkmanager:
    tools\bin\sdkmanager --list | more
    
  2. Build Tools 25.0.3 (Apr 2017)

    tools\bin\sdkmanager "build-tools;25.0.3"
    Accept? (y/N): y
    
  3. Platform Tools 28.0.0 (May 2018)

    tools\bin\sdkmanager "platform-tools"
    Accept? (y/N): y 
  4. Platform(s) and sources

  5. tools\bin\sdkmanager "platforms;android-25"
    tools\bin\sdkmanager "sources;android-25"
    
    tools\bin\sdkmanager "platforms;android-27"
    tools\bin\sdkmanager "sources;android-27"
  6. Emulator 27.2.9 (May 2018)

  7. tools\bin\sdkmanager "emulator"
    Accept? (y/N): y
    
  8. Emulator images

    Downloading the image with sdkmanager may produce a 'broken' unusable image. Dowload using the the graphical version of Android SDK Manager. Additionally, API 27 images did not seem to work for me (emulator image stuck at boot with a blank screen). Recommending to try the API 25 images (or whichever versions you need).
    tools\android

  9. Check installed components

    tools\bin\sdkmanager --list | more
    
    Installed packages:
      Path                              | Version | Description                       | Location
      -------                           | ------- | -------                           | -------
      build-tools;25.0.3                | 25.0.3  | Android SDK Build-Tools 25.0.3    | build-tools\25.0.3\
      emulator                          | 27.2.9  | Android Emulator                  | emulator\
      patcher;v4                        | 1       | SDK Patch Applier v4              | patcher\v4\
      platform-tools                    | 28.0.0  | Android SDK Platform-Tools        | platform-tools\
      platforms;android-25              | 3       | Android SDK Platform 25           | platforms\android-25\
      platforms;android-27              | 1       | Android SDK Platform 27           | platforms\android-27\
      sources;android-25                | 1       | Sources for Android 25            | sources\android-25\
      sources;android-27                | 1       | Sources for Android 27            | sources\android-27\
      system-images;a...s_playstore;x86 | 9       | Google Play Intel x86 Atom Sys... | system-images\a..._playstore\x86\
      tools                             | 25.2.5  | Android SDK Tools 25.2.5          | tools\
  10. Intel HAXM 7.2.0 (May 2018)


    Read installation guide, download & install from:
    https://software.intel.com/en-us/articles/intel-hardware-accelerated-execution-manager-intel-haxm

  11. Start Eclipse

    To be detailed
  12. Create AVD

    To be detailed


     
Publishing early as a draft for my friend Enzo :-) to be continued tomorrow.

Tuesday, January 2, 2018

AppCompat runtime error: "android.content.res.Resources$NotFoundException: File res/drawable/abc_vector_test.xml from drawable resource ID #0x7f020052"

Developing an Android app with the latest Support Library versions you may encounter this crash at startup on devices running Android < 5.0 (API 21):
FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.googleplayservicesdemo/com.example.googleplayservicesdemo.MainActivity}: android.content.res.Resources$NotFoundException: File res/drawable/abc_vector_test.xml from drawable resource ID #0x7f020052
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2180)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
       at android.app.ActivityThread.access$600(ActivityThread.java:141)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loop(Looper.java:137)
       at android.app.ActivityThread.main(ActivityThread.java:5041)
       at java.lang.reflect.Method.invokeNative(Native Method)
       at java.lang.reflect.Method.invoke(Method.java:511)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
       at dalvik.system.NativeStart.main(Native Method)
Caused by: android.content.res.Resources$NotFoundException: File res/drawable/abc_vector_test.xml from drawable resource ID #0x7f020052
       at android.content.res.Resources.loadDrawable(Resources.java:1953)
       at android.content.res.Resources.getDrawable(Resources.java:660)
       at android.support.v7.widget.VectorEnabledTintResources.superGetDrawable(VectorEnabledTintResources.java:74)
       at android.support.v7.widget.AppCompatDrawableManager.onDrawableLoadedFromResources(AppCompatDrawableManager.java:435)
       at android.support.v7.widget.VectorEnabledTintResources.getDrawable(VectorEnabledTintResources.java:67)
       at android.support.v4.content.ContextCompat.getDrawable(ContextCompat.java:353)
       at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:200)
       at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:188)
       at android.support.v7.widget.AppCompatDrawableManager.checkVectorDrawableSetup(AppCompatDrawableManager.java:755)
       at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:193)
       at android.support.v7.widget.TintTypedArray.getDrawableIfKnown(TintTypedArray.java:87)
       at android.support.v7.app.AppCompatDelegateImplBase.<init>(AppCompatDelegateImplBase.java:128)
       at android.support.v7.app.AppCompatDelegateImplV9.<init>(AppCompatDelegateImplV9.java:149)
       at android.support.v7.app.AppCompatDelegateImplV11.<init>(AppCompatDelegateImplV11.java:29)
       at android.support.v7.app.AppCompatDelegateImplV14.<init>(AppCompatDelegateImplV14.java:54)
       at android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:202)
       at android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:183)
       at android.support.v7.app.AppCompatActivity.getDelegate(AppCompatActivity.java:519)
       at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:70)
       at com.example.googleplayservicesdemo.MainActivity.onCreate(MainActivity.java:19)
       at android.app.Activity.performCreate(Activity.java:5104)
       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)
       ... 11 more
Caused by: org.xmlpull.v1.XmlPullParserException: Binary XML file line #17: invalid drawable tag vector
       at android.graphics.drawable.Drawable.createFromXmlInner(Drawable.java:881)
       at android.graphics.drawable.Drawable.createFromXml(Drawable.java:822)
       at android.content.res.Resources.loadDrawable(Resources.java:1950)
       ... 33 more
Force finishing activity com.example.googleplayservicesdemo/.MainActivity

Cause

Android Developers documentation (Vector Drawables Backward Compatibility Solution) mentions having to add --no-version-vectors AAPT parameter for older versions of Gradle plugin.

Running android-sdk/build-tools/xx.y.z/aapt with no parameters tells us more about the parameter:
   --no-version-vectors
       Do not automatically generate versioned copies of vector XML resources.
If we look at the AppCompat library it contains only one res/drawable/abc_vector_test.xml, but if you dissamble your APK with Apktool (apktool d your.apk) you will notice that during preparing and packaging the Android tools have created two versions from the original: res/drawable/abc_vector_test.xml and res/drawable-v21/abc_vector_test.xml.

Eclipse Andmore

Eclipse Andmore brought support for --no-version-vectors with version 0.5.1 (bug 509663)
  1. Go to your app project > Properties
  2. Check the '--no-version-vectors' option
  3. Project > Clean

Eclipse ADT

Eclipse ADT plugin is no longer supported by Google, but if you are still using it and you haven't migrated to Eclipse Andmore or Android Studio yet there is a solution that involves replacing aapt with a wrapper tool that will add --no-version-vectors when called with package option.

That is because if you enable verbose (Preferences > Android > Build > Build output = Verbose) the path and name of aapt tool is pretty much hardcoded so for this to work we will need to rename the original and replace it with this wrapper program I wrote for this.

  1. Download the latest from Github dandar3/android-aapt-wrapper releases
  2. Go to android-sdk\build-tools\xx.y.z\
  3. Rename aapt.exe to aapt-original.exe (Windows) and aapt to aapt-original (Linux) 
  4. Save aapt-wrapper.exe as aapt.exe (Windows) and aapt-wrapper as aapt (Linux) 
  5. Verify the pass through by running aapt - you should see the wrapper info followed by the real aapt output:
    Android Asset Packaging Tool - Wrapper
    Version 1.0 (31 Dec 2017)
    https://github.com/dandar3/android-aapt-wrapper
    
    Command:
      aapt-original.exe
    
    Android Asset Packaging Tool
    
    Usage:
     aapt l[ist] [-v] [-a] file.{zip,jar,apk}
       List contents of Zip-compatible archive.
    
     aapt d[ump] [--values] [--include-meta-data] WHAT file.{apk} [asset [asset ...]]
       strings          Print the contents of the resource table string pool in the APK.
    ... 
  6. Project > Clean

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.

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.


Tuesday, December 6, 2016

Android 7.1 Nougat on Nexus 7 2012 WiFi

  1. Backup / transfer personal data (texts, photos, videos, apps, etc)

  2. Read [ROM][7.1.0_r5][NDE63U][Grouper|Tilapia][F2FS/EXT4]Android 7.x AOSP by AndDiSa
    • download aosp_grouper-7.1-ota-eng-20161127.ds.zip (219 MB)
    • check MD5 signature (FCIV or a similar tool):
      fciv -add aosp_grouper-7.1-ota-eng-20161127.ds.zip -md5
  3. Install Android SDK / platform-tools

  4. Install TWRP (Team Win Recovery Project) for Nexus 7 2012 Wifi
    (twrp-3.0.2-0-grouper.img, 2016-04-04, 93 MB)

    Follow Fastboot Install Method (No Root Required) section.

  5.  Boot into recovery mode (TWRP) with:
    adb reboot-bootloader
    Use Volume Up or Down keys to select "Recovery Mode", then press "Power".

  6. Now in TWRP, choose Wipe > Swipe to fatory reset


  7. Back to TWRP main screen > Advanced > ADB Sideload > Swipe to Start Sideload


  8. Sideload new ROM from your PC:
    adb sideload aosp_grouper-7.1-ota-eng-20161127.ds.zip
    Total xfer: 1.00x


    Note:  E: unknown error [log] causes or implications are yet unknown, ignoring for now.

  9. Go to Open GApps, choose ARM + Android 7.1  + Pico.
    • download open_gapps-arm-7.1-pico-20161205.zip, 5 December 2016, 121 MB
    • check MD5 signature (FCIV or a similar tool):
      fciv -add aosp_grouper-7.1-ota-eng-20161127.ds.zip -md5
  10. Sideload Open GApps:
    • go back and reselect Sideload option
    • select Wipe Dalvik Cach
    • Swipe to Start Sideload
    adb sideload open_gapps-arm-7.1-pico-20161205.zip
    Total xfer: 1.19x


  11. Reboot (TWRP Main > Reboot > System).

Saturday, November 5, 2016

Eclipse: Integrate Firebase Analytics into your Android app

With official support moving away from Eclipse towards Android Studio and Gradle, new things like Firebase Analytics have become unavailable to developers wanting to continue using Eclipse for their app development. In this post, I will go through the libraries and the configuration required so you can integrate it in your Eclipse project.


Firebase Analytics libraries

 

dandar3/android-google-firebase-README project (GitHub) gives you an overview of the available Firebase libraires ready 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 an entire project set like Firebase Analytics into 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

 

There is no Eclipse plugin to do the work that the Google Services Gradle plugin does for Android Studio (I have started working on one, but I can't say when this will be available), so you would have to read the Firebase documentation and follow the Processing the JSON File information to manually create 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.
 
You can also try the web conversion tool I wrote, the conversion is done in your browser through JavaScript like you would do it manually. You need to download your google-services.json from Firebase Console, then use the tool to open it and will generate your values.xml just like you would manually do it.
<?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" />

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

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

        <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>
    </application>
</manifest>

Without these changes you would get all sorts of errors in logcat when running your app, some of them listed  below:
11-04 15:56:22.970 E/FA      ( 1539): AppMeasurementReceiver not registered/enabled
11-04 15:56:22.971 E/FA      ( 1539): AppMeasurementService not registered/enabled
...
11-04 16:01:23.188 W/FA      ( 1694): Failed to retrieve Firebase Instance Id
...
11-04 16:01:36.391 E AndroidRuntime: Process: com.google.firebase.quickstart.analytics, PID: 1718
11-04 16:01:36.391 E AndroidRuntime: java.lang.RuntimeException: Unable to start receiver com.google.android.gms.measurement.AppMeasurementReceiver: 
                                     java.lang.SecurityException: Neither user 10067 nor current process has android.permission.WAKE_LOCK.


Testing

 

Enable verbose / debug logging for Firebase Analytics (docs):
adb logcat -c 
adb shell setprop log.tag.FA VERBOSE
adb shell setprop log.tag.FA-SVC VERBOSE
adb logcat -v time -s FA FA-SVC

If everything looks fine you may notice the upload scheduling is nothing close to Google Analytics (1h to 12h) - Steve Ganem, Product Manager at Firebase Analytics, confirms on StackOverflow he is aware of the need for quicker reporting.
11-05 21:01:21.560 V/FA      ( 1418): Upload scheduled in approximately ms: 3599998

One way to trigger the initial upload found by Yapaxi user on the same thread, is to Clear data for your app. That will not make the information immediately on the Firebase Console web app, it could take up to an hour or more before the reports are made available.
--------- beginning of system
--------- beginning of main
11-05 21:25:17.413 I/FA      ( 1761): App measurement is starting up, version: 9877
11-05 21:25:17.413 I/FA      ( 1761): To enable debug logging run: adb shell setprop log.tag.FA VERBOSE
11-05 21:25:17.413 D/FA      ( 1761): Debug-level message logging enabled
11-05 21:25:17.413 D/FA      ( 1761): AppMeasurement singleton hash: 96720111
11-05 21:25:17.418 V/FA      ( 1761): Collection enabled
11-05 21:25:17.418 V/FA      ( 1761): App package, google app id: com.google.firebase.quickstart.analytics, 1:348775509088:android:a9b2141120408d37
[...]
11-05 21:25:27.931 V/FA      ( 1761): Uploading data. app, uncompressed size, data: com.google.firebase.quickstart.analytics, 445,
11-05 21:25:27.931 V/FA      ( 1761): batch {
11-05 21:25:27.931 V/FA      ( 1761):   bundle {
11-05 21:25:27.931 V/FA      ( 1761):     protocol_version: 1
11-05 21:25:27.931 V/FA      ( 1761):     platform: android
11-05 21:25:27.931 V/FA      ( 1761):     gmp_version: 9877
11-05 21:25:27.931 V/FA      ( 1761):     uploading_gmp_version: 9877
11-05 21:25:27.931 V/FA      ( 1761):     config_version: 1477992761690000
11-05 21:25:27.931 V/FA      ( 1761):     gmp_app_id: 1:348775509088:android:a9b2141120408d37
11-05 21:25:27.931 V/FA      ( 1761):     app_id: com.google.firebase.quickstart.analytics
                                          [...]
11-05 21:25:27.931 V/FA      ( 1761):   }
11-05 21:25:27.931 V/FA      ( 1761): }
11-05 21:25:27.931 V/FA      ( 1761): Uploading data. size: 431
11-05 21:25:28.039 V/FA      ( 1761): Upload scheduled in approximately ms: 3599999
11-05 21:25:28.042 V/FA      ( 1761): Successful upload. Got network response. code, size: 204, 0

That's it - check your data up on Firebase Console an hour or so later.

Friday, September 30, 2016

How to import Android libraries from GitHub/dandar3 into Eclipse workspace

Showing you today an easier way of importing Android Support and Google Play Services libraries for Android from github.com/dandar3 repositories into your Eclipse workspace.



Prerequisites:

Navigate to:

Choose the project you want to import (e.g. android-support-v7-appcompat).

Scroll down to SVN Checkout section, right-click the Team Project Set URL and choose Copy link to copy it to clipboard.

Switch to Eclipse then navigate to File > Import > Team > Team Project Set.

Paste the address into the URL field (if not already pre-populated from clipboard), then click Finish.

The download (import) may take a short while dependening on your Internet connection.

Once finished, wait for Eclipse to rebuild all projects or manually choose Project > Clean > Clean all projects and check the Start a build immediately both with Build the entire workspace option.

Then you can add the library (or libraries) as a dependency to your application.

Tuesday, September 6, 2016

Eclipse: Setting "derived" folders - AutoDeriv plugin

Using Eclipse with Java or Android projects you may notice that "Open Resource" (CTRL+SHIFT+R on Windows) is showing files from bin (or gen or other) folders, that were copied or generated by the build process, that you really don't want to edit as they will get overwritten on next build, resulting only in frustration and wasted time if you do.


Eclipse documentation for Derived resources:
Many resources get created in the course of translating, compiling, copying, or otherwise processing files that the user creates and edits. Derived resources are resources that are not original data, and can be recreated from their source files.  It is common for derived files to be excluded from certain kinds of processing.
You can manually update derived the flag on each resource in Properties page but as the resource will not be persisted in a source repository the flag won't as well. So that when you are checking out or downloading the project it will not be set, or when manually removing and recreating the particular resource in the case of a folder.


AutoDeriv plugin comes to help as it relies on configuration stored in a .derived file in your project that can be commited in a source repository. When the project is created the plugin will automatically mark as derived the resources matched in the configuration file.



The plugin also has a preferences page where you can change certain things like presentation.


References: