Best android questions in November 2011

How to find absolute position of click while zoomed in

13 votes

Please see each section below for a description of my problem described in three separate ways. Hopefully should help people to answer.

Problem: How do you find a pair of coordinate expressed in canvas/userspace when you only have the coordinate expressed in terms of a zoomed image, given the original scale point & scale factor?

Problem in practice:

I'm currently trying to replicate the zoom functionality used in apps such as the gallery / maps, when you can pinch to zoom/zoom out with the zoom moving towards the midpoint of the pinch.

On down I save the centre point of the zoom (which is in X,Y coordinates based on the current screen). I then have this function act when a "scale" gesture is detected:

class ImageScaleGestureDetector extends SimpleOnScaleGestureListener {
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        if(mScaleAllowed) 
            mCustomImageView.scale(detector.getScaleFactor(), mCenterX, mCenterY);
        return true;
    }
}   

The scale function of the CustomImageView look like this:

public boolean scale(float scaleFactor, float focusX, float focusY) {
    mScaleFactor *= scaleFactor;

    // Don't let the object get too small or too large.
    mScaleFactor = Math.max(MINIMUM_SCALE_VALUE, Math.min(mScaleFactor, 5.0f));

    mCenterScaleX = focusX;
    mCenterScaleY = focusY;

    invalidate();

    return true;
}

The drawing of the scaled image is achieved by overriding the onDraw method which scales the canvas around the centre ands draw's the image to it.

@Override
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    canvas.save();
    canvas.translate(mCenterScaleX, mCenterScaleY);
    canvas.scale(mScaleFactor, mScaleFactor);
    canvas.translate(-mCenterScaleX, -mCenterScaleY);
    mIcon.draw(canvas);
    canvas.restore();
}

This all works fine when scaling from ScaleFactor 1, this is because the initial mCenterX and mCenterY are coordinates which are based on the device screen. 10, 10 on the device is 10, 10 on the canvas.

After you have already zoomed however, then next time you click position 10, 10 it will no longer correspond to 10, 10 in the canvas because of the scaling & transforming that has already been performed.

Problem in abstraction:

The image below is an example of a zoom operation around centre point A. Each box represents the position and size of the view when at that scale factor (1, 2, 3, 4, 5).

Example

In the example if you scaled by a factor of 2 around A then you clicked on position B, the X, Y reported as B would be based on the screen position - not on the position relative to 0,0 of the initial canvas.

I need to find a way of getting the absolute position of B.

So, after redrawing the problem I've found the solution I was looking for. It's gone through a few iteration's but here's how I worked it out:

enter image description here

It's pretty easy to find the values for each between A and B (both X and Y) since you can convert known distances by dividing by the scale factor in order to find it's distance in canvas/userspace. This along with the fixed values (in blue) give you the distance between A and it's furthest edge. Using these two values you know where A is in userspace.

public float[] getAbsolutePosition(float Ax, float Ay) {

    float toCurrentCenterX  = (mCenterScaleX - Ax) / mScaleFactor;
    float toEdgeFromCenterX = mOriginalWidth - mCenterScaleX;
    float x = mOriginalWidth - toCurrentCenterX - toEdgeFromCenterX;

    float toCurrentCenterY  = (mCenterScaleY - Ay) / mScaleFactor;
    float toEdgeFromCenterY = mOriginalHeight - mCenterScaleY;
    float y = mOriginalHeight - toCurrentCenterY - toEdgeFromCenterY;

    return new float[] { x, y };
}

The above code and image describe when you're clicking to the top left of the original centre. I drew these to their logical conclusion to get the following code which works perfectly:

public float[] getAbsolutePosition(float Ax, float Ay) {

    float x = getAbsolutePosition(mCenterScaleX, Ax, mOriginalWidth);
    float y = getAbsolutePosition(mCenterScaleY, Ay, mOriginalHeight);  

    return new float[] { x, y };
}

private float getAbsolutePosition(float oldCenter, float newCenter, float dimension) {
    if(newCenter > oldCenter) {
        return oldCenter + ((newCenter - oldCenter) / mScaleFactor);
    } else {
        return oldCenter - ((oldCenter - newCenter) / mScaleFactor);
    }
}

Video clarity with my code is bit low as compared to recording with Android camera

10 votes

In my application I recorded a video and then playing. For recording a video I used the below code

    mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
    mediaRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);

    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); 
    mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); 
    mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);



    File MyMedia=new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM),"Directory");
    mediaRecorder.setOutputFile(MyMedia.getAbsolutePath()+"/filename.mp4");

It is working fine but video clarity is bit low compare with another video which is recorded by native recorder. Please tell me the solution for how to set video clarity same as native recorder.

Set frameRate and videoSize.

recorder.setVideoFrameRate();
recorder.setVideoSize();

Debugging android apps on the kindle fire

10 votes

Is deBugging android applications from eclipse supported on the kindle fire?

According to Amazon's FAQ on Kindle Fire, Kindle Fire has USB debugging enabled by default. To connect Kindle Fire to your Android Debug Bridge (ADB) follow the instructions in this PDF.If you are working with Windows 7 you will need to download and use this driver. They are also providing USB drivers for download for Windows 7 PC. Moreover, we can also emulate Kindle Fire specs on Android emulator by using the following parameters.

*Width: 600px

*Height: 1024px

*Abstracted LCD Density: 169

*Target: Android 2.3.4 - API Level 10

*RAM: 512 MB

They don't offer separate developer devices. You can use the Kindle Fire devices which you can buy online.

How to write dead simple native SERVER program (not app) for Android devices?

9 votes

First of all, I believe, it's not a duplicate question. I don't want to write a native app with NDK, I just want to use Android device as a cheap Linux server. Server, in this context, means a program, which has no UI. Also, the server is written in bare C++, but uses no libraries at all, so that's why should that question differ from others.

The story

We've written a small server for Linux, it has a webserver interface, so you connect to it with a vanilla browser, and you can play with it. We've compiled it to Linux/x86, Linux/ARM (GuruPlug), Linux/PPC (some kind of NAS), and Mac OS X Darwin platforms.

The Linux/ARM version also runs well on my Palm Pré smartphone. I've just copied the executable onto my phone (in webOS scene, there's no such thing like jailbreak, there's an official "developer mode", which if you activate by typing a secret key combination, you can log in to your phone with ssh or use local ssh app). So, our small server program (daemon, may say) is running on even the smallest Linux devices. (It's funny, when connect with your giga-powered desktop machine's browser to a smartphone server.)

The server program is designed to run on even the smallest Linux (as I said, it runs on NAS, router, smartphone), it requires only STDLIB and PTHREADS.

The question

What't the straight way to get that simple server program run on an Android device?

  • We have no Android device yet. But I assume, any ARM-based one will do. The server's memory footprint is so small, that it will be no problem.
  • I assume, the device must be rooted to copy executable to it and run.
  • I don't want to install a whole new Linux on the device. It should keep running Android, while our server works in the background. The Linux distro which enough for Dalvik, is enough for our server, I'm pretty sure.
  • The server should use no external things (USB, touchscreen, audio, camera, mic - thanks, not) only a port for webserver (e.g. 8080), which is not allocated by Android.
  • I have ARM GCC toolchain, the installation of it is not the part of the question (except if there're tricks).
  • The installation of the program should be manual (scp, whatever). Also, I don't want to make a package or any automated deployment.
  • The problem of the launching of the server program is another question, we'd ignore now.

So, I want just do the same thing with an Andrid device (phone, tablet), just as I did on my webOS device: copy the server program and run on it. Computers are for that, running programs, aren't they? ;)

You definitely need a rooted android device. I recommend that you install busybox to get a bunch of nice command line tools such as wget, tar, gzip and many more. You can get shell access to your device by using adb shell and then su to get root access. You can either use adb push or wget to put your server on the device.
The /system partition is mounted as read only and depending on the manufacturer there are a lot of protections that restrict you from writing there so you should copy your server somewhere on the /data partition (e.g. mkdir /data/myserver). After this, everything should be pretty straight forward.

Amazon Marketplace app rejected for containing Eclipse settings file

9 votes

I'm currently trying to submit my app to the Amazon Marketplace. Today I received the following email from the App Testing team:

Your recent submission of RSS Alarm is pending due to the following reason(s):

We are having trouble publishing your app to test. Your APK appears to have the following file included in it: .settings/org.eclipse.core.resources.prefs Please remove this file and resubmit your APK.

Please correct the issue(s) we found with your app submission so we may continue working to get it in the Appstore.

Thing is, I can't see how the .settings folder made it into the APK build in the first place. It's not listed in either my Android settings nor my Java build path settings. So where am I going wrong with this?

Screenshots: enter image description here enter image description here enter image description here enter image description here

Many thanks.

From working in a team environment I've made it a habit of keeping my application source and resources in a separate directory tree from my workspace and project settings. For example:

/home/workspaces/projectname

/home/sources/projectname

I don't know if this is your exact problem but I think it might help keep Eclipse meta-data from being built with your code.

How can I use Eclipse's new Xtend language in my Android project?

8 votes

I'd like to write Java classes in the Xtend language (simply because its way more terse), and have it compile back down into Java classes of which I can use in my Java project. Just like coffeescript. How can I do this?

I tried creating an Xtend file just as I would do with a new class, however I get this error:

Mandatory library bundle 'org.eclipse.xtext.xbase.lib' not found on the classpath.

This disables intellisense (autocompletion). Also, even if I do get that working, how can I have it compile to a Java class?

Having tried the same thing, I can confirm that enabling the Xtend Nature and adding the three Xtend libraries (mentioned earlier, 'org.eclipse.xtext.xtend2.lib', 'org.eclipse.xtext.xbase.lib' and 'com.google.inject') to the project's libraries makes the Xtend code compile, at least. However, I am also having trouble with the R class.

On closer inspection, it looks like the problem with the R class is not with it being located in a different directory. Copying the file to the main source dir with a different name doesn't change anything. Rather, it looks like the problem is with the R class being a static final class, containing several static final subclasses. If I create a simple plain-Java wrapper class that wraps a reference to R.layout.main (for example) inside a normal method, and call that from my Xtend code, then it does accept it and happily compiles.

After that, the next issue I came across was the Android compiler complaining about duplicate about.html and plugin.properties files in 'org.eclipse.xtext.xtend2.lib', 'org.eclipse.xtext.xbase.lib' and 'com.google.inject'. That is relatively easy to fix, by removing those files from two of the three .jar files. I'm not sure if it breaks anything later on, but now at least I have a working snippet of Xtend code running on the Android emulator.

push notification from UrbanAirship not working with live server (i.e. with production key ) in android

8 votes

In one of my android app, I am using push Notification from Urban Airship.

The problem with the app is that when I am using the development key for the push notification its working perfect (The app get registered & APID generated) but when I am using the production key , It does not work at all(The APID not generated).

However, I have configured the app for production key properly (Like in airshipconfig.properties, 1) set key for production key 2) making inProduction = true.

Still its not working .

I am getting the error :

App name- UAlib Stop connnecting . In a holding pattern.

on logcat, everytime I am trying to connect it with the live server (production key).

Any idea or help on this will be highly appreciated.

I think your free trail period(as you are using the helium transport) for the push notification from Urban airship have been finished. So you have two options: 1) Either you take some paid plan from urban airship 2) Or go for c2dm transport.

The c2dm transport does not need to have any plan.

Hope this will help you.

How do I connect to Kindle Fire for development?

8 votes

What do I need to do to use my Kindle Fire for android development? (Specifically for testing my apps on the device.)

You can find the instructions for connecting Kindle Fire to the ADB in a PDF of instructions provided by Amazon.

Paraphrased from the document:

  1. Edit the adb_usb.ini file (located in ~/.android/)

  2. Add the lines:

    0x1949
    0x0006
    
  3. Save the file.

  4. Run these commands to restart adb:

    adb kill-server
    adb start-server 
    adb devices  
    

NOTE: For Windows 7 users you need to download an additional driver.

Set/Get Java List<> from C code

8 votes

Java Code

In Java code I have class called IdentificationResult which has 3 members:

  1. enrollmentID
  2. enrollmentSettings
  3. identParams.

Here is the class:

package com.vito.android.framework.service;

class IdentificationResult
{
    class IdentParams {
        byte[] otp;
        String seedId;
    }

    String enrollmentID;
    String enrollmentSettings;
    List<IdentParams> identParams;
}

In the main class I have function IdentificationResult GetAuthenticationStatus( ), here is the main Class:

public class TokenManager 
{
    /* Some code goes here ... */

    public IdentificationResult GetAuthenticationStatus( )
    {
        /* Function do some actions here ... */
        return new IdentificationResult;
    }
}

C++ Code

I call Java method from my C++ code in this way

void GetAuthenticationStatus( )
{
    // Attach current thread.
    JNIEnv *env = NULL;
    m_javaVM->AttachCurrentThread( env, NULL );
    if( env == NULL ) {
        return -1;
    }

    jclass clazz = NULL;
    clazz = env->GetObjectClass( m_classObject );
    if( clazz == NULL ) {
        return -1;
    }

    // Get class method.
    jmethodID clazzMethod = NULL; 
    env->GetMethodID( clazz, "GetAuthenticationStatus", "(V;)Lcom/vito/android/framework/service/IdentificationResult;" );
    if( clazzMethod == NULL ) {
        return VCS_RESULT_ERROR;
    }

    // Call Java 'GetAuthenticationStatus' function.
    jobject methodReturnObj = env->CallObjectMethod( m_classObject, clazzMethod );

    // Get IdentificationResult Class from Object.
    jclass identifyResultClass = env->GetObjectClass( methodReturnObj );
    if( identifyResultClass == NULL ) 
    {
        return -1;
    }

    // Get identParams.
    jfieldID fieldID = env->GetFieldID( identifyResultClass , "identParams", "***1. Question***");
    if( fieldID == NULL ) {
        return -1;
    }
    else
    {
        *** 2. Question *** 
    }

}

Questions

  1. What I must write here to get List<IdentParams> field ID?

  2. How I can Get or Set field value?

Okay, I have solve the problem and want to share result with you, here is solution:

    fieldID = env->GetFieldID( identifyResultClass , "identParams", "Ljava/util/List;" );
    if( fieldID != NULL ) 
    {
        // Find "java/util/List" Class (Standard JAVA Class).
        jclass listClass = env->FindClass( "java/util/List" );
        if( listClass == NULL ) {
            DBG_WARNING(DBG_CTX, ("Can't Find Class \"java/util/List\".\n"));
            return -1;
        }

        // Get List object field.
        jobject listObject = env->GetObjectField( methodReturnObj, fieldID );
        if( listObject == NULL ) {
            DBG_WARNING(DBG_CTX, ("Can't get ObjectField for \"List\".\n"));
            return -1;
        }

        // Get "java.util.List.get(int location)" MethodID
        jmethodID getMethodID = env->GetMethodID( listClass, "get", "(I)Ljava/lang/Object;" );
        if( getMethodID == NULL ) {
            DBG_WARNING(DBG_CTX, ("Can't get MethodID for \"java.util.List.get(int location)\".\n"));
            return -1;
        }

        // Get "int java.util.List.size()" MethodID
        jmethodID sizeMethodID = env->GetMethodID( listClass, "size", "()I" );
        if( sizeMethodID == NULL ) {
            DBG_WARNING(DBG_CTX, ("Can't get MethodID for \"int java.util.List.size()\".\n"));
            return -1;
        }

        // Call "int java.util.List.size()" method and get count of items in the list.
        int listItemsCount = (int)env->CallIntMethod( listObject, sizeMethodID );
        DBG_DISPLAY(DBG_CTX,("List has %i items\n", listItemsCount));

        for( int i=0; i<listItemsCount; ++i )
        {
            // Call "java.util.List.get" method and get IdentParams object by index.
            jobject identParamsObject = env->CallObjectMethod( listObject, getMethodID, i - 1 );
            if( identParamsObject == NULL )
            {
                DBG_WARNING(DBG_CTX, ("Can't get Object from \"identParamsObject\" at index %i.\n", i - 1));
            }


        }

Thanks to @Joop Eggen he gives me great idea !!!