Showing posts with label Eclipse. Show all posts
Showing posts with label Eclipse. 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.

Friday, December 27, 2019

Eclipse Subversive (SVN) plugin installation

A tutorial on how to install Subversive SVN plugin + connectors for those who still use SVN as a source repository for their projects or used to easily pull into your project the Google AAR libraries from GitHub.

  1. Use your exiting Eclipse installation or download and extract the latest version available.
    Eclipse IDE for Java Developers 2019-12
    https://www.eclipse.org/downloads/packages/

  2. Go to Help > Install New Software
    Click Add... button a add a new Subversive repository pointing to
    http://download.eclipse.org/technology/subversive/4.0/update-site/


    Select at least the Subversive SVN Team Provider entry, continue de installation with the Next button, accept the license and Finish the installation.


    Restart now when prompted, to apply the software updates.

  3. Once restararted, go to Windows > Preferences > Team > SVN and select the SVN Connector tab.


    SVN Connector dropdown is empty so let's click the Get Connectors... button to download a connector.

    You can choose between the SVN Kit pure Java connector (SVN 1.8 compatible) and Native JavaHL system binary connectors (SVN 1.8 and 1.9 compatible). I for one prefer the first one as it - read this thread if you want to better understand the differences. https://stackoverflow.com/a/7703748/308836


    Finish the installation following the software updates dialogs presented, accepting the license and choose to Install Anyway if presented wih a dialog telling you the software was not signed.


    Restart now when prompted, to apply the software updates.
* * *

And that's it, you can now pull projects down from SVN using File > Import... > SVN > Projects from SVN




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, 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.


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:

Friday, July 25, 2014

Maven - Checking for dependency updates

If you're using Maven with your Java or Android projects you can leverage its ability to check for new library versions using versions:display-dependency-updates goal, instead of doing that manually through search.maven.org or using services like changedetection.com.

Withing Eclipse, you can create an internal Run Configuration (Run > Run Configurations... > Maven Build):


[INFO] Scanning for projects...
[INFO] 
[INFO] Using the builder org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder with a thread count of 1
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building MyApplication 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- versions-maven-plugin:2.1:display-dependency-updates (default-cli) @ myapplication ---
[INFO] The following dependencies in Dependencies have newer versions:
[INFO]   com.squareup.okhttp:okhttp ........................ 2.0.0-RC1 -> 2.0.0
[INFO]   com.squareup.okhttp:okhttp-urlconnection .......... 2.0.0-RC2 -> 2.0.0
[INFO]   com.squareup.picasso:picasso .......................... 2.3.2 -> 2.3.3
[INFO]   commons-io:commons-io ..................................... 2.2 -> 2.4
[INFO]   org.apache.commons:commons-lang3 ........................ 3.1 -> 3.3.2
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.555 s
[INFO] Finished at: 2014-07-25T21:57:33+00:00
[INFO] Final Memory: 10M/166M
[INFO] ------------------------------------------------------------------------


Resources:

Monday, June 9, 2014

Eclipse: Setting "derived" folders (Groovy Monkey script)

Using Eclipse with Java or Android projects you may have noticed "Open Resource" (CTRL+SHIFT+R) showing files from "bin" folder(s) that you don't want to edit as Eclipse will overwrite them on next build.


Per Eclipse documentation for Derived resources:
A derived resource is a regular file or folder that is created in the course of translating, compiling, copying, or otherwise processing other files. Derived resources are not original data, and can be recreated from other resources. It is commonplace to exclude derived resources from version and configuration management because they would otherwise clutter the team repository with version of these ever-changing files as each user regenerates them.
You could manually update the flag on each resource (Properties) but that is not persisted in a source repository so it is not set automatically when downloading a new project or will get lost when removing and recreating the particular resource.


This Groovy Monkey script will help you set the "derived" flag on chosen resources quickly, especially handy if having multiple projects in the same workspace. Copy and save into a "monkey" folder inside of your project.

/*
 * Menu: Set "derived" folders
 * Script-Path: /Your project/monkey/SetDerivedFolders.gm
 * Kudos: Dan Dar3 <dan.dar33@gmail.com>
 * License: EPL 1.0
 * LANG: Groovy
 * Job: UIJob
 * DOM: http://groovy-monkey.sourceforge.net/update/net.sf.groovyMonkey.dom
 */

import org.eclipse.core.resources.IFolder;

def derivedNames = [ "bin", "gen", "libs", "target" ]

out.println("");
out.println("Derived folders:");
out.println("~~~~~~~~~~~~~~~~");

def projects = workspace.getRoot().getProjects();
for (project in projects) {
  if (project.isOpen()) {
    for (member in project.members()) {
      if (member instanceof IFolder) {
        if (derivedNames.contains(member.getName())) {
          out.println(member.getFullPath().toString()); 
          member.setDerived(true);
        }
      }
    }
  }
}

If you have installed the Groovy Monkey plugin (see links at the end) you will get the script in the Groovy Monkey menu at the top where you can run it from.



Run it again whenever you see generated resources creeping into the "Open Resource" listings (making sure you don't have the "Show derived resources" checked though :-)


Resources:

Sunday, June 23, 2013

Eclipse CDT and Microsoft Visual C++ compiler

Trying to work on this legacy project in Eclipse instead of Visual Studio 2003 I hit a few snags, so I started again by creating a new project to see if possible to use the VC++ compiler in the first place - so here it is sharing this with you. Although proficient with Eclipse for Java development I'm still a C++ newbie, so if any Visual Studio / C++ veterans reading this, feel free to correct me :-)

  • Eclipse 4.3 RC3 (CDT 8.2)
  • Visual Studio 2003 (VC7)

Let's create the project - File > New > C++ Project



The project will have a few compile errors, due to missing includes:


Now to fix this, you can obviously search the VC++ directories for iostream in this case, or if you want to go all the includes you can run vcvars32.bat and show the INCLUDE environment variable, e.g.

C:>"C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\vsvars32.bat"
Setting environment for using Microsoft Visual Studio .NET 2003 tools.
(If you have another version of Visual Studio or Visual C++ installed and wish
to use its tools from the command line, run vcvars32.bat for that version.)

C:>set INCLUDE
INCLUDE=C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\ATLMFC\INCLUDE;...
I'll just add the minimum necessary to fix the project - go to Project (right-click) > Properties > C/C++ Build > Settings > Tool Settings > C++ Compiler > Preprocessor > Add...


Coming back to the Project Explorer you may notice the Includes section - if the errors still persist, right click the project and choose Index > Rebuild or choose Project (top menu) > C/C++ Index > Rebuild and that should clear them.


This concludes the CDT compiler part, now what we want to do is build the project using VC++ compiler and linker. If you chose to Build the project (the "hammer" or right-click build), you will notice the errors in the console where it can't find CL.exe.
01:15:55 **** Incremental Build of configuration Debug for project HelloWorld ****
Info: Internal Builder is used for build
cl /c /EHs /MD /Zi "/IC:\\Program Files\\Microsoft Visual Studio .NET 2003\\Vc7\\include" /nologo "/Fosrc\\HelloWorld.obj" "..\\src\\HelloWorld.cpp" 
Cannot run program "cl": Launching failed

Error: Program "cl" not found in PATH
PATH=[...]

01:15:55 Build Finished (took 46ms)

We're going to fix this by adding VCx\bin location to the PATH environment variable: Project > Properties > C/C++ Build > Environment > PATH... We're going to add it to all configurations, not just debug.


If you're going to build the project again, you will notice that CL.exe now runs correctly, but it ends abruptly and it's not followed by the linking.


Running a ProcMon trace it shows that CL.exe could not find mspdb71.dll that it needs and it just ends there. To resolve this we'll need to add the correct location (..\Common7\IDE) to the PATH environment variable we defined at previous step.


Rebuilding the project looks better, but now linking will fail to find msvcprt.lib.


To fix this, we're going to define the LIB environment variable (VC7\lib), same as we did for PATH earlier - it also also fail for kernel32.lib so we're going to add Vc7\PlatformSDK\Lib as well:


All is working well now and the .exe is generated:


Now that we got an exe we get to run it - either manually create it in Run Configurations or right-click the project > Run As > Local C/C++ Application:


Now if you change the source and click to "run" again, it will kick the build automatically and you'll see your changes in action - you may actually notice the CDT build output for a while, or you can configure to see both console views as the same time.