Showing posts with label codez. Show all posts
Showing posts with label codez. Show all posts

Friday, 27 May 2011

shortcuts script for common debug tasks

I have been using this script for a while, it allows me to make shortcuts for common android debug tasks (e.g. logcat, shell, installing/overwriting/removing apks). There are probably better ways of doing it for the shell script experts out there, but it suits my purposes... and it's pretty streamlined.

There are aliases for common devices and common apps so i can start stuff with a few characters

So the first parameter denotes the function.
l = logcat
r = replace app (keeping data)
s = shell
i = install
u = uninstall
o = overwrite (uninstall then install)
ks = kill-server
ss = start-server

The second parameter denots the device (which are configured in the first if statement):
a = all connected devices (from 'adb devices')
f = first device (in 'adb devices' list)
e1 = emulator-5554
(the others are my hardware devices)

Then there are app package name shortcuts ... (the 3rd if statement)

To start logcat on ny nexus 1, I type:
ad l n1
And to overwrite an apk on all connected usb devices - it type (the 2nd 'a' param stands for 'all connected devices'), really useful when testing before a release ...
ad r a /path/to/apkfile.apk
Basically it's nothing too high tech, it just makes things much quicker and easier when i am debugging. If you have any suggestions then i am keen to hear them...

So without further ado, here is the script:
#!/bin/bash

# toggle this to test command output without execution 0=no exec
EXEC=1;

DEVICES='f'

if [ $2 ] && [ $2 = 'g1' ] ; then
 DEVICES=("HT848KV04600")
elif [ $2 ] && [ $2 = 'hero' ] ; then
 DEVICES=("HT9BSL901147")
elif [ $2 ] && [ $2 = 'arch' ]  ; then
 DEVICES=("A10-4BE40002-9FF80000-015F2F44-0D01601E")
elif [ $2 ]  && [ $2 = 'n1' ]  ; then
 DEVICES=("HT018P805702")
elif [ $2 ] && [ $2 = 'e1' ]  ; then
 DEVICES=("emulator-5554")
elif [ $2 ] && [ $2 = 's' ]  ; then
 DEVICES=("I5500b2e40b3c")
elif  [ ! $2 ] || [ $2 = 'a' ] ; then
 DEVLIST=`adb devices`
 DEVICES=()
 for D in $DEVLIST; do 
  if  [ $D = "device" ] ; then 
   DEVICES=(${DEVICES[@]} $PREV)
  fi
  PREV=$D
 done
# take the first device if not specified otherwise take all
 if [ ! $2 ] ; then
  DEVICES=${DEVICES[0]} 
 else 
  DEVICES=${DEVICES[@]}
 fi
fi
echo "using devices:" $DEVICES

OP=''
if [ $1 ] ; then
 if [ $1 = 'u' ] ; then
  OP="uninstall"
 elif [ $1 = 'i' ] ; then
  OP="install"
 elif [ $1 = 'r' ] ; then
  OP="install -r"
 elif [ $1 = 'l' ] ; then
  OP="logcat"
 elif [ $1 = 's' ] ; then
  OP="shell"
 elif [ $1 = 'ks' ] ; then
  OP="kill-server"
 elif [ $1 = 'ss' ] ; then
  OP="start-server"
 elif [ $1 = 'z' ] ; then
  OP=""
 elif [ $1 = 'o' ] ; then
  OP=""
 fi
fi
PRG=$3;
if [ $3 ] ; then
 if [ $3 = 'mpp' ] ; then
  PRG="package.name.1"
 elif [ $3 = 'st' ] ; then
  PRG="package.name.2"
 elif [ $3 = 'stp' ] ; then
  PRG="package.name.3"
 elif [ $3 = 'mp' ] ; then
  PRG="package.name.4"
 fi
fi

if [ ! $1 ] ; then
 echo "ad    extra"
 echo "CMD = o (overwrite)  extra=apk | l (logcat) | r (reinstall) extra=apk | i (install) extra=apk | s (shell) | u (uninstall) |  ks (kill-server) | ss (start-server)"
 echo "CMD = z (zipalign )"
 echo "DEV = g1 hero arch n1 s a(all)"
 echo "PRG = mp mpp st stp"
 echo "------devices---------"
 CMD="adb devices"
 echo $CMD
 if [ $EXEC = 1 ] ; then 
  $CMD
 fi 
else 
 if [ $1 = 'z' ] ; then
  NEWFILE=`echo $2 | sed "s/.apk/_z.apk/"`
  rm -fr $NEWFILE
  CMD="zipalign -v 4 $2 $NEWFILE"
  echo $CMD
  if [ $EXEC = 1 ] ; then 
   $CMD
  fi
 else 
  for DEV in $DEVICES ; do
   echo $DEV
   if [ $1 = 'o' ] ; then
    CMD="adb -s $DEV uninstall $PRG"
    echo $CMD
    if [ $EXEC = 1 ] ; then 
     $CMD
    fi
    CMD="adb -s $DEV install $4"
    echo $CMD
    if [ $EXEC = 1 ] ; then 
     $CMD
    fi
   else 
       CMD="adb -s $DEV $OP $PRG $4"
    echo $CMD
    if [ $EXEC = 1 ] ; then 
     $CMD
    fi
   fi
  done;
 fi
fi

Monday, 25 October 2010

Filtering onTouch for light pressure

For the drawing program I am writing, there is a sensitivity problem when the pressure is light on the touchpad. When the finger is pressed down (like when you are in the middle of a drag operation) the touch point is stable as the area of the finger pressed to the touchpad is large and so the centroid of the area is quite stable.

However at the end of the drag operation, as the finger pulls away, the area on the touch sensor decreases and the actual touch point seems to vary a lot. This is a problem for me in my drawing program, as you make fine changes to the drawing, you don't want the changes to be affected by this noise as you pull up from the touchpad.

This solution uses a mean filter to combat the problem - noting new there. But we combine the mean value with the pressure value so as the pressure gets lighter, we us more of the mean value. It works best if the finger pulls off the surface slowly.

The filter class I have used is shown below:
/*
* licensed under CC BY-SA : http://creativecommons.org/licenses/by-sa/3.0/legalcode
*/
class MeanFilter {
 public static final int DEFAULT_LEN = 11;
 float[][] points = new float[2][DEFAULT_LEN];
 int currentPos=0;
 int actualValues = 0;
 int length = DEFAULT_LEN;
 float pressureLimit = 0.2f;
  
 public MeanFilter(int length,float pressureLimit) {
  super();
  this.length = length;
  this.pressureLimit = pressureLimit;
 }
 public void add(float x,float y) {
  points[0][currentPos]=x;
  points[1][currentPos]=y;
  currentPos++;
  currentPos%=length;
  if (actualValues<length) {
   actualValues++;
   actualValues=Math.min(actualValues, length);
  }
 }
 public void clear() {currentPos=0;actualValues=0;}
 public void get(PointF defaultPoint,float pressure){
  if (actualValues==0) {return;}
  if (pressure<pressureLimit) {
   float xmean = getMean(points[0], actualValues);
   float ymean = getMean(points[1], actualValues);
   float pressureRatio = pressure/pressureLimit;
   defaultPoint.x=(pressureRatio)*defaultPoint.x+(1-pressureRatio)*xmean;
   defaultPoint.y=(pressureRatio)*defaultPoint.y+(1-pressureRatio)*ymean;
  }
 }
 public void setLength(int i) {
  length = i;
  clear();
 } 
 private float getMean(float[] arr,int ctr){
  float accum = 0;
  for (int i=0;i<ctr;i++) {
   accum+=arr[i];
  }
  return accum/ctr;
 }
}

To use it we create an instance - this is a mean filter length:7, pressure threshold:0.3
private MeanFilter touchFilter = new MeanFilter(7,0.3f);

Then in onTouch, we can filter the value to be used like so:
/*
* licensed under CC BY-SA : http://creativecommons.org/licenses/by-sa/3.0/legalcode
*/
public boolean onTouch(View v, MotionEvent event) {
 PointF touchPointOnScreen = new PointF(event.getX(),event.getY());
 switch (event.getAction() ) {
  case MotionEvent.ACTION_DOWN: touchFilter.clear();
   break;
  case MotionEvent.ACTION_MOVE: 
   touchFilter.add(event.getX(),event.getY());
   if (event.getPressure()<0.05) {return true;}
   if (event.getPressure()<0.3) {
    touchFilter.get(touchPointOnScreen,event.getPressure());
   } 
   break;
  case MotionEvent.ACTION_UP:
   touchFilter.get(touchPointOnScreen,event.getPressure());
   break;
 }
 // use touchPointOnScreen here...
 return true;
}
So touchPointOnScreen contains the filtered value to use.

We can note that the mean values arent calculated until the pressure drops below the threshold, thus being slightly more efficient than just mean filtering all the time, this also stops touchpoint lag as the mean filtered value will generally lay somewhere behind the current value.

This code is made open under the CC BY-SA licence.

Tuesday, 22 June 2010

Anroid UI setInterval

In most apps I develop, I usually have a need to update the UI within the activity (as services change, or just live time displays). Making these timers is easy in Javascript, you just use setInterval(method,nnn).

On Android it is more complex - you need a Timer and TimerTask, and then you need another Runnable to give to Activity.runOnUiThread(..), and then your actual method. It all adds a lot of code bloat, especially if you have a few activities in your app. AsyncTask is good for updating the UI while a background process is running, but for just a simple update interval it's no use.

This is a reasonably simple solution to the problem - the MyTimer class has a setInterval method that behaves similarly to Javascript. The usage is:
  • Set the timer:
    int timer=MyTimer.setInterval(this, "updateUI", 2000,true);
    (where "updateUI" is a method with the signature: public void updateUI() {..})
  • Cancel the timer:
    timer=MyTimer.cancelTimer(timer);
Some Notes:
  • The cancel operation returns -1 if the cancel was performed successfully, so you can just test if the timer ==-1 to tell if you need to start it again.
  • The code above is called from the activity, the first argument of setInterval should be an Activity object.
  • There is a static debug variable if it doesnt work as expected (MyTimer.debug=true).
  • If there is an exception in the target method -> the timer is cancelled.
  • MAKE SURE YOU CANCEL THE TIMER (in onStop or onPause). If the timer is not cancelled, then it will prevent garbage collection, a possible way around is just to return the Timer object instead of holding it in a Vector.
  • Note that this type of usage can potentially use a lot of battery if its just left running, the updateUI method (or whatever the method name is), should do the minimum processing necessary. It should just update a couple of UI elements or something - not do masses of calculations (store results as variables is a simple solution).
package co.uk.sentinelweb.util;
/*
* licensed under CC BY-SA : http://creativecommons.org/licenses/by-sa/3.0/legalcode
*/
import java.lang.reflect.Method;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import android.app.Activity;
import android.util.Log;

public class MyTimer {
public static Vector timers = new Vector();
public static boolean debug = false;// turn on to check timer activates and cancels
public static int setInterval(Activity object,String method,int interval,boolean isUI) {
Timer timeoutTimer= new Timer();
TimeoutTask timeoutTimerTask;
try {
timeoutTimerTask = new TimeoutTask(object,method,isUI,timeoutTimer.hashCode());
if (debug) {Log.d(MyTimer.class.getSimpleName(), "timer created :");}
} catch (Exception e) {
Log.d(MyTimer.class.getSimpleName(), "error creating timer :"+method,e);
return -1;
} 
timeoutTimer.scheduleAtFixedRate(timeoutTimerTask, 0, interval); 
timers.add(timeoutTimer);
return timeoutTimer.hashCode();
}

public static int cancelTimer(int code) {
Timer timeoutTimer = null;
for (int i=0;i<timers.size();i++) {
if (timers.get(i).hashCode()==code) {
timeoutTimer=timers.get(i);
}
}
if (timeoutTimer!=null) {
timers.remove(timeoutTimer);
timeoutTimer.cancel();
timeoutTimer.purge();
if (debug) {Log.d(MyTimer.class.getSimpleName(), "timer deleted :");}
return -1;
} else {
if (debug) {Log.d(MyTimer.class.getSimpleName(), "timer not found :"+code);}
}
return code;
}

private static class TimeoutTask extends TimerTask {
Activity targetObject;
Method targetMethod;
Runnable invoker;
int code ;
public TimeoutTask(Activity targetObject, String targetMethod,boolean isUI,int code) throws SecurityException, NoSuchMethodException {
super();
this.targetObject = targetObject;
this.targetMethod = targetObject.getClass().getDeclaredMethod(targetMethod, new Class[]{});
this.code=code;
if (isUI) {
invoker = new ThreadRunner(this.targetObject,this.targetMethod);
}
}

@Override
public void run() {
try {
if (debug) {Log.d(MyTimer.class.getSimpleName(), "invoke timer :");}
if (invoker==null) {
targetMethod.invoke(targetObject, new Object[]{});
} else {
targetObject.runOnUiThread(invoker);
}
} catch(Exception e) {
Log.d(MyTimer.class.getSimpleName(), "error invoking:"+targetMethod.getName(),e);
cancelTimer(code);
Log.d(MyTimer.class.getSimpleName(), "timer:"+code+" has been cancelled");
}
}

}

private static class ThreadRunner implements Runnable{
Activity targetObject;
Method targetMethod;

public ThreadRunner(Activity targetObject, Method targetMethod) {
super();
this.targetObject = targetObject;
this.targetMethod = targetMethod;
}

@Override
public void run() {
try {
targetMethod.invoke(targetObject, new Object[]{});
} catch (Exception e) {
Log.d(MyTimer.class.getSimpleName(), "error invoking:"+targetMethod.getName(),e);
}
}
}
}
CAVEAT: While I haven't found anything in the API to do this (easily), it is possible that it is in there somewhere. If it is then let me know, and I'll update this.

This code is made open under the CC BY-SA licence.