Android: Detecting gesture swipes AND taps in a view

I have a custom calendar in one of my activities which should respond to swipe gestures to change the current month, but also respond to tap events on dates.

For some odd reason, my view just wasn't responding to the events triggered by the GestureDetector. It would work with either swipe or tap, but not both.

When both "worked", the tap would be triggered by a ACTION_DOWN at the start and end of the swipe. If I got the tap working, it'd break the swipe functionality.

force
Swipe functionality working the way it's supposed to.

However, if I applied the exact same code to the Activity without the view and it'd work just fine. This was annoying =\

The majority of code samples and tutorials online would only deal with detecting and handling swipes in an activity. Any tutorials about swipes in views weren't about swipes and single tap.

Eventually I figured out that the detection code was meant to go into the Activity, not a combination of both.

This code sample goes into Activity.onCreate().

final GestureDetector gestureDetector = new GestureDetector(new GestureListener());

calendar.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(final View view, final MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
return false;
}

return true;
}
});

And below is the GestureListener class. I also put this code in the Activity class code, but it can easily be decoupled for cleanliness.

class GestureListener extends SimpleOnGestureListener {
@Override
public boolean onSingleTapConfirmed(MotionEvent event) {
// Trigger the touch event on the calendar
calendar.onTouchEvent(event);
return super.onSingleTapUp(event);
}

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
ViewConfiguration viewConfiguration = ViewConfiguration.get(EventsActivity.this);
int minSwipeDistance = viewConfiguration.getScaledPagingTouchSlop();
int minSwipeVelocity = viewConfiguration.getScaledMinimumFlingVelocity();
int maxSwipeOffPath = viewConfiguration.getScaledTouchSlop();

if (Math.abs(e1.getY() - e2.getY()) > maxSwipeOffPath) {
return false;
}

if (Math.abs(velocityX) > minSwipeVelocity) {
// Right to left swipe
if (e1.getX() - e2.getX() > minSwipeDistance) {
calendar.nextMonth();
}
// Left to right
else if (e2.getX() - e1.getX() > minSwipeDistance) {
calendar.previousMonth();
}

// Call some app-related functions to update the display
displayDate(calendar.getMonth(), calendar.getYear());
}

return false;
}
}

This code detects left/right swipes, with consideration to scaling of DPI and screen sizes. It also correctly detects single clicks on the calendar.

It also has some "error detection" in terms of maxSwipeOffPath (accidental swipes in the wrong direction) and minSwipeVelocity/minSwipeDistance (too slow or too short) to be consistent with swipe detection on your whole Android device.

Note: I tend to support API level 4 where possible, but getScaledPagingTouchSlop() requires level 8 (Android 2.2).

Sources

Java/Android: Access private variables in a class using reflection

Android's DatePickerDialog class has a handy little function getDatePicker(), which exposes the private DatePicker mDatePicker.

Problem with that is it's only available on Honeycomb/API 11+. I don't want to raise my damn API level and leave 2.3 users behind! They're the majority of my users (at time of writing)

funny-gifs-table-flip

So, what can I do to get around this design oversight? Extending/subclassing the DatePickerDialog class won't work, because it's still a private variable.

Good thing is that even on Android, reflection is still a valid option. What reflection does is use Java's internal mechanisms to access the variable directly from the object.

public DatePicker getDatePicker() {
try {
Field field = DatePickerDialog.class.getDeclaredField("mDatePicker");
field.setAccessible(true);
return (DatePicker) field.get(datePickerDialog);
}
catch (NoSuchFieldException e) {
e.printStackTrace();
}
catch (IllegalArgumentException e) {
e.printStackTrace();
}
catch (IllegalAccessException e) {
e.printStackTrace();
}

return null;
}

This little helper function works a treat! Unless the variable is renamed in other revisions of Android, it should work fine in all cases.

Sources

Sublime Text 2 / Eclipse: View the same file in two different places simultaneously

One of the more interesting features of Sublime Text 2 was the ability to view a file in two places at once. I saw this in a video tutorial on Lynda and wondered if it was possible to do in Eclipse also.

This becomes VERY useful when editing HTML files where CSS/JS declarations are in the header and the content is in the body.

Sublime Text 2

Since I saw this feature here first, I'll write about it Sublime before Eclipse.

Open up the file you wish to edit. Then click on File > "New view into file".

Eclipse

My old trusty Eclipse. Slow and hefty, but it's the Swiss army knife of development for me.

As before, open up the file you wish to edit. This time, click on Window > "New Editor".

Sources

Expandrive: Automatically connect to server using command line

ExpanDrive (previously known as SftpDrive) has a pretty handy and undocumented feature to connect via CLI. Why they hid this away I do not know, but it's saved me a bit of hassle every morning.

The syntax varies slightly, on different versions.

ExpanDrive v1:

net use \\ExpanDrive\drivename

ExpanDrive v2:

expandrive.exe connect drivename

Source

You can thanks the great customer support from Jeff Mancuso at ExpanDrive!

erYLM
How I picture ExpanDrive staff dealing with issues.

Android: Faster debugging emulator for development with Android x86

If you're doing Android dev, most likely you're working on a computer with an x86 compatible CPU.

Unfortunately the older versions of Android SDK images (including 2.3, still one of the most popular in the play store at time of writing) are optimised for ARM chips. The ARM optimised commands are run in the emulator and then translated into x86 commands. This is very slow, but you since you're reading this you already know that.

Even with the Intel chipset improvements from Icecream Sandwich onwards, the emulator still leaves much to be desired in terms of performance.

Android x86, as the name suggests, was originally made to run Android on x86 computers. A wonderful side-effect of this is that it runs incredibly fast in a virtual machine compared to the standard Android emulator.

Gk1Ew
Amazingly fast!

And getting it to work for debugging isn't that hard either.

Setup

  • Grab a copy of Android X86. There seems to be customised builds, but the EEEPC Asus ones worked fine for me.
  • Download and install VirtualBox.
  • Create a new virtual machine (VM)
  • Give it a name, select "Other" for type and "Other/Unknown" for version.
  • You can match what you want to your phone/tablet specs such as memory/space (1GB memory is nice and zippy)
  • Go to Settings
  • Click on Motherboard
  • Under Bootloader, disable Floppy. Move HDD above CD.
  • Under Storage, click on the "Empty" CD icon and then the other CD icon on the right.

image

  • Find and choose your Android x86 ISO file.
  • OK to save.
  • Fire it up and you'll come to an option screen. Select Installation.

AND1

  • Create/modify devices
  • New, Primary, press enter (to use 100% of the space)
  • Bootable, Write, type "yes" and press Enter
  • When it's done, Quit
  • Select sda1
  • Select EXT3
  • Yes to GRUB
  • Choose yes or no for allowing /system to be writable. It's really up to you and how you wish to use the VM. It doesn't take very long to install anyway.
  • Once you're done, reboot.
  • It should now show you 3 options; HDPI, MDPI and Debug. That's when you know it's working!
  • Select any of them and enjoy developing for Android at a much faster rate!

Once it's up and running, reboot it (just to see how fast it starts). No more waiting for snapshots to load!

Tweaking

Now for some stuff to help you get started with Android dev.

You'll need to connect your Eclipse Android SDK debugger to the VM, and to do that you'll need to set up port forwarding on the VM.

By default it's a fairly generous tablet sized screen. For phone development, you can change the screen orientation to portrait orientation.

Caveats

There's no way of sugar coating this. In trade for the speed and performance you get with this method of debugging, you lose the ability to rotate the screen while the VM is running.

If you've got any way around this, please let me know!

Source

Android x86: How to change the screen orientation to portrait orientation in VirtualBox

This was definitely trickier than expected! There are two things you need to do. First is to create the resolution, second is to edit the grub file.

Create the resolution

Open up command prompt and type in:

cd /d %PROGRAMFILES%\VirtualBox\
vboxmanage setextradata "Android 2.3" "CustomVideoMode1" "320x480x16"

This creates an extra resolution for your Android X86 virtual machine (replace "Android 2.3" with whatever your one is called). Of course, you can replace the resolution with whatever you want to.

Now fire up the VM. Once it loads up to the lock screen (you don't have to swipe to unlock):

  • Press Alt+F1 to enter the terminal
  • Type "su"
  • mkdir /data/fs
  • mount -t ext3 /dev/block/sda1 /data/fs
  • vi /data/fs/grub/menu.lst
  • Find the menu item you want by scanning the titles. I wanted to work on MDPI so I chose the second menu item.
  • On the line starting with "kernel", go to the end and press "i" to begin editing (yes, vi has some crazy conventions)
  • Change DPI=160 and vga=ask
  • (You can also take this chance to edit your default item to load. It's 0-based indexes)
  • Press escape to edit editing mode
  • Without the quotes, press ":wq" to save and quit (yeah, even more crazy vi conventions)

Restart your VM. When it boots, it'll give you an option to press enter to see video modes.

Take note of the "mode" next to your resolution. For me "320x480x16" was "360" (bottom left). Type in 360 and continue loading.

Android 2

This bit took me a while to figure out, but "360" is a hex value and you'll have to convert it value to a decimal first. In this case, 360 hex is 864 in decimal value.

Time to edit the file in vi again.

  • alt+f1 again to get into the console
  • mount -t ext3 /dev/block/sda1 /data/fs
  • vi /data/fs/grub/menu.lst
  • Change vga=ask to vga=864 (or whatever your decimal VGA mode is)
  • :wq to save
  • Reboot

Android 2
You should now see this wonderful phone sized emulator on your screen.

Sources

Android: Automatically change layout for portrait and landscape orientations

One of the nicer things about using layouts for your design is the ability to automatically move things around when the screen rotates.

Normally, layout files go in res/layout/. You can create an extra folder res/layout-land/ (for landscape mode) and res/layout-port/ (for portrait mode) and it should pick the appropriate layout.

When I first gave this a go, I thought something was wrong. Even on portrait orientation, it kept picking the file from /res/layout-land rather than /res/layout/!

I read both Supporting Multiple Screens: Designing alternative layouts and drawables and Providing Resources: How Android Finds the Best-matching Resource but none of them seemed to mention this.

But alas, a little trial and error proved successful. If you've got an alternate layout in /res/layout-land/, you must place the normal portrait layout in /res/layout-port/ rather than /res/layout/.

So before the changes, my incorrect resource structure looked like this:

  • res/
    • layout/
      • auto_rotate.xml
      • normal_layout.xml
    • layout-land/
      • auto_rotate.xml

What it's supposed to look like is:

  • res/
    • layout/
      • normal_layout.xml
    • layout-land/
      • auto_rotate.xml
    • layout-port/
      • auto_rotate.xml


Now stop playing around and get some damn work done!

Sublime Text 2: Subfolders and files not appearing in project folders list

Been toying around with Sublime Text 2 for the past week, a pretty damn good editor so far. Ran into a strange problem which made it impossible to use.

It took a bit of guess work but it was due to the nature of my workflow. The project files are located on a remote folder, mapped to a network drive via ExpanDrive. There are other reports of people with this issue using Samba, so I'm guessing it's mostly related to working remotely.

If you've got issues opening 2nd level directories, remedy this by going to:

  • Preferences
  • Settings - User
  • Add in this line

"ignore_inodes": true

Save and restart ST2. The subfolders SHOULD show up. If not, remove the folder from your project and add them again.

Note: This hidden setting was added to build 2197 (11th of May, 2012) for Windows only.

ibs8zT68d2LKTK
Don't worry Sublime Text, you've only stumbled gracefully

Source

Firefox: Automatically disable addon compatibility checks for good!

As you can see below, I got sick of adding new config extensions.checkCompatibility.* values each release. I've done it enough times since Firefox 3.6 and I'll be damned keep doing it.

image

Here comes a nifty little Firefox addon from Kris Maglione called "Disable Add-on Compatibility Checks".

Download it. Since addon developers can't keep up with the insane release cycle rates that Mozilla has adopted, I can't recommend it enough!

tumblr_lxprn1h0Nh1qdlh1io1_400
Like a passive user gone postal, I'm giving Firefox checks a smackdown!

Android: IllegalArgumentException: Invalid audio buffer size

I was making an app which was streaming audio from the microphone, but for some odd reason it was throwing exceptions.

Here's the initialisation code.

int RECORDER_SAMPLERATE = 8000;
int RECORDER_CHANNELS = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_8BIT;

 

int bufferSizeInBytes = AudioTrack.getMinBufferSize(
    RECORDER_SAMPLERATE,
    RECORDER_CHANNELS,
    RECORDER_AUDIO_ENCODING
);

 

AudioTrack audioTrack = new AudioTrack(
    AudioManager.STREAM_MUSIC,
    RECORDER_SAMPLERATE,
    RECORDER_CHANNELS,
    RECORDER_AUDIO_ENCODING,
    bufferSizeInBytes,
    AudioTrack.MODE_STREAM);

Now it looked completely fine to me, and it took a good while for me to figure out because I never expected AudioTrack.getMinBufferSize() to actually return an error!

The reason was that AudioTrack could not be initialised for the given PCM encoding. I was trying to reduce bandwidth by making it 8bit, but the recording had to be in 16bit PCM.

It's not an obvious one, but it's a bug I could only catch find after testing on multiple devices.

rcTsgPpveU6jbkYyuKpO7w2
Bug caught.

VirtualBox: Connectivity between Guest and Host

While setting up my Android x86 emulator on VirtualBox, I had some issues getting the networking right. VMWare would get this working easily, but it's quite an expensive and heavy handed piece of software if you think about it.

What I wanted was to have my Android x86 guest be able to fetch information from a server running on the host. Also, the host had to be able to connect to the guest for debugging.

After a few searches, I found that some guides only let you connect from host to guest. Others only let you connect from guest to host.

This one says you can connect between the two only if there is a router/internet connectivity. This was a bit of a deal breaker as I do a lot of my work on the train.

After a few hours of tinkering I had great success using "NAT", but only with port forwarding enabled. Two way communication between host/guest, and internet connectivity as well!

To do that:

  • Go to the VM settings
  • Network
  • Ensure that "NAT" is selected
  • Click on "Advanced"
  • Port forwarding
  • Click on the + icon on the right side
  • Name it (in this case Android Emulator), select either TCP or UDP, you can leave the IPs blank and match the ports you want to forward.
  • OK to save

image

Some screenshots of it working

host-to-guest
Host to guest for debugging and guest with internet connectivity.

guest-to-host
Guest to host connectivity.

How to connect?

You still connect from guest to host via 10.0.2.2, but to connect from host to guest you'll have to port forward the request from host to the guest. From host to guest you use "localhost" or 127.0.0.1 instead of the actual guest IP.

For example, before you'd have this command:

adb.exe connect 192.168.56.15

Now it's:

adb.exe connect localhost

 

l.php
Now that it's all connected, GET BUSY!

 
Copyright © Twig's Tech Tips
Theme by BloggerThemes & TopWPThemes Sponsored by iBlogtoBlog