By calendar time, this little coding adventure is more than half over. That is largely because of my trip to NYC. The trip itself, plus all of the personal errands I had to do before and afterwards, cost me 14 days. Worth it, I supposed, since I did manage to find a place to live. On the tail end of this thing, I've calculated that I will probably lose 10 days to the move, since I didn't take that account when I came up with my estimated amount of time.
Work continues to go slower than I'd like. When I first began this I thought I had a real shot of getting this feature complete in 84 days. I...under estimated the amount of work. By a long shot.
Now, as far as my performance goes, I have noticed the following two things provide the most benefit, by far:
1. caffeine
2. not working on the stupid fucking database
Caffeine is an incredible, performance enhancing drug for the programmer. It's not good for you...not in the amounts that I'm using. But it sure as hell gets me going. I can plow right through distractions, tangential but pre-requisite tasks, tech debt, design decisions...anything. The obvious downside are there, of course...tachycardia, sleep impairment, nighttime and daytime teeth grinding, a sort of global jittery feeling and the sugar is sabatoging my efforts to get in shape. However...the amount of work I can get done makes it worth it in the short run.
Not working on the stupid fucking database...I don't know what happened. All of the sudden, I just can't stand dealing with relational databases. There's nothing inherently wrong with them--when you have a relational problem you need a relational storage layer--but I just don't like them. Everything is a giant fucking chore. You have to mess with the schema. You have to--sometimes--create a stored procedure to protected the schema. You have to use a database access library in your language of choice, and database access layers are a fucking field day for people who enjoy over-designing things. The greater majority of the times I stop work and start procrastinating are because I realized I have to deal with one of the database layers.
In other news, I sold my TV and while I did buy a ridiculously expensive projector, I haven't really felt like using it yet. So possibly the lack of TV watching is also making me get more work done.
Also, I am currently vetting two more android coders. I found them by posting a small teaser project to craigslist. Learning from my experience last time, I chose not to reveal my personal expectations of what a programmer should be capable of, and left the price open ended. I got about 20 responses, and two of them weren't idiots.
I haven't let them touch the main codebase yet, but I have had them prototype some of the features on the back log. Given the cost of hiring other programmers, I am optimizing for highest efficiency (as opposed to getting as much done as possible). So far it seems like the best thing they can do is track down tangents (e.g. go find an android QR code library that actually works and write test code) so that I don't get distracted from the heavy lifting.
I will probably let at least one of them start editing the main code base. I'd like to see if he is capable of working on bugs/tech debt, and also writing unit/integration tests. I feel like as soon as I have him touch the made codebase, though, I lose "bragging rights" to say I wrote the whole app myself. However, a finished app makes money; bragging rights do not make money. And its not like my ego needs to be fed.
Thursday, March 27, 2014
Saturday, March 22, 2014
[coding] The Time War, Part 2
The easiest fix is to change all SQL code from:
NOW()
to
UTC_TIMESTAMP()
and not even JDBC can fuck that up.
NOW()
to
UTC_TIMESTAMP()
and not even JDBC can fuck that up.
[coding] The Time War, or, Dream Host is a Piece of Shit
It has just come to my attention that Dream Host runs all of their mysql servers in Pacific Time. On purpose.
I just wrote a 5 page blog entry about how retarded that is, and then realized that people who disagree with me on this are probably too irrational to be convinced. So...I'm in the market for a new mysql provider.
I shouldn't have to even say this...in fact, no, I'm not going so say it. Here is what we are going to do. Go get a piece of paper and write down what time zone you think its acceptable for a server to run in. Go ahead, I'll wait.
Ok, look at your piece of paper. It should say UTC, also known colloquially as U to the mother fucking C, because it is the divine, the one and only acceptable time zone for a server on or near planet earth. I will also accept GPS, and GMT, although calling it GMT might cause nearby nerds to correct you; do so at your own risk. In fact, maybe you should think of UTC, GPS, and GMT as the holy trinity of time. If you wrote anything else, get the fuck out of here, you heathen. Throw your stupid ass time zone in the trash, stop reading my stupid ass blog, and stop clicking on posts that have "[coding]" in the title: you temporal plebians can go hang out on stack overflow with the other dweebs.
I liked this blog better when I was telling sanitized versions of drunk escapades or whining about girls, but...dream host runs their servers in pacific time. What the hell are people from the East Coast supposed to do? They're going to see a time that doesn't match local time, and if they have any sense at all, they'll assume its UTC...but it won't be. It will be this other time zone that isn't any more or less significant then their own. Decades later they will gather their grandchildren around the campfile and tell the horror story of the "time zone from nowhere." And daylight savings? Lets not even go there. I just have this unstoppable, inexplicable to tell someone. Dream Host is not running UTC. I can't not do it. I think I might even tell people who aren't programmers. When I walk into Chipotle for breakfast tomorrow the girl is going to ask me how my day is going and I'm just going to shake my head and say "well, last night I just found out that dream host runs their mysql servers in pacific time."
I'm just going to leave this here:
[Edit]
Maybe I should just find a hosting server run from the UK so it will be in the right time zone by accident. Except with my luck they will observe daylight savings time.
I just wrote a 5 page blog entry about how retarded that is, and then realized that people who disagree with me on this are probably too irrational to be convinced. So...I'm in the market for a new mysql provider.
I shouldn't have to even say this...in fact, no, I'm not going so say it. Here is what we are going to do. Go get a piece of paper and write down what time zone you think its acceptable for a server to run in. Go ahead, I'll wait.
Ok, look at your piece of paper. It should say UTC, also known colloquially as U to the mother fucking C, because it is the divine, the one and only acceptable time zone for a server on or near planet earth. I will also accept GPS, and GMT, although calling it GMT might cause nearby nerds to correct you; do so at your own risk. In fact, maybe you should think of UTC, GPS, and GMT as the holy trinity of time. If you wrote anything else, get the fuck out of here, you heathen. Throw your stupid ass time zone in the trash, stop reading my stupid ass blog, and stop clicking on posts that have "[coding]" in the title: you temporal plebians can go hang out on stack overflow with the other dweebs.
I liked this blog better when I was telling sanitized versions of drunk escapades or whining about girls, but...dream host runs their servers in pacific time. What the hell are people from the East Coast supposed to do? They're going to see a time that doesn't match local time, and if they have any sense at all, they'll assume its UTC...but it won't be. It will be this other time zone that isn't any more or less significant then their own. Decades later they will gather their grandchildren around the campfile and tell the horror story of the "time zone from nowhere." And daylight savings? Lets not even go there. I just have this unstoppable, inexplicable to tell someone. Dream Host is not running UTC. I can't not do it. I think I might even tell people who aren't programmers. When I walk into Chipotle for breakfast tomorrow the girl is going to ask me how my day is going and I'm just going to shake my head and say "well, last night I just found out that dream host runs their mysql servers in pacific time."
I'm just going to leave this here:
UTC
[Edit]
- Hostgator runs their servers in CST (WTF????? thats not even a coastal time zone!)
- justhost.com is MST (FUCKING HELL)
- ipage.com is EST
- bluehost.com is MST FUCK
- hostmonster.com is...I forget but its not UTC because "we're in Utah." Dumbasses
- ixwebhosting.com is GMT-5 (which I believe is EST)
- hostmetro.com is CDT ...if that D stands for daylight I hope they all burn alive
- fatcow.com is EST
Maybe I should just find a hosting server run from the UK so it will be in the right time zone by accident. Except with my luck they will observe daylight savings time.
Wednesday, March 19, 2014
[coding] JDBC bullshit
Me: Ok Mysql Driver, lets connect to a database! I made a test table just for you!
Mysql Driver: no you have to use me from the Driver Manager
Me: Uh, ok, hey Driver Manager, please give me an instance of...
Driver Manager: class not found exception! class not found exception! we got a class not found exception here!
Me: Oh fuck. Ok, Maven, here is a new jar file that the jetty server needs to access
Maven: Ok. Wait, what are you doing? Only the doctor touches me there. Ow!
Maven: That's a hardcoded reference in the system namespace! You shouldn't put your libraries in an easy to find place like source control! Oh the humanity. Maven devs are going to stop supporting this soon because we know better than you. You should put that jar in a repository. Is it even open source?
Maven: If you aren't using open source you have to spend 6 hours setting up your own maven repository.
Maven: All software should be open source.
Maven: ...
Maven: You see, a long time ago, Richard Stallman tried to print something, and-
Driver Manager: class not found exception!
Me: what the hell?
Driver Manager: you have to tell the Mysql Driver to create an instance of itself so I can find him. And dont just load the class; create an instance that you'll never use. You didn't think that just because we were using reflection I would actually search the classpath for you, did you?
Me: Ok, Mysql Driver, create an instance of yourself.
Mysql Driver: I'M ALIIIIIIVE.
Me: I don't suppose you could just go ahead and connect to my-
Mysql Driver: NO
Me: Driver Manager, please get Mysql Driver for me
Driver Manager: Allow me to present, dear sir, a driver class fit for a mysql database! By the laws of the divine factory pattern, I've gone to the trouble of dynamically loading it for you, so that you don't have to load it yourself and you don't have to know which class is being loaded! Here you go thank you come again.
Me: Now, Mysql Driver, will you please just connect me to my stupid fucking test database?
Mysql Driver: SURE I WOULD LOVE TO
Me: mother fucking cuntbags
[Edit]
Also...JDBC indexes are 1 based. Who the fuck came up with that?
Mysql Driver: no you have to use me from the Driver Manager
Me: Uh, ok, hey Driver Manager, please give me an instance of...
Driver Manager: class not found exception! class not found exception! we got a class not found exception here!
Me: Oh fuck. Ok, Maven, here is a new jar file that the jetty server needs to access
Maven: Ok. Wait, what are you doing? Only the doctor touches me there. Ow!
Maven: That's a hardcoded reference in the system namespace! You shouldn't put your libraries in an easy to find place like source control! Oh the humanity. Maven devs are going to stop supporting this soon because we know better than you. You should put that jar in a repository. Is it even open source?
Maven: If you aren't using open source you have to spend 6 hours setting up your own maven repository.
Maven: All software should be open source.
Maven: ...
Maven: You see, a long time ago, Richard Stallman tried to print something, and-
Driver Manager: class not found exception!
Me: what the hell?
Driver Manager: you have to tell the Mysql Driver to create an instance of itself so I can find him. And dont just load the class; create an instance that you'll never use. You didn't think that just because we were using reflection I would actually search the classpath for you, did you?
Me: Ok, Mysql Driver, create an instance of yourself.
Mysql Driver: I'M ALIIIIIIVE.
Me: I don't suppose you could just go ahead and connect to my-
Mysql Driver: NO
Me: Driver Manager, please get Mysql Driver for me
Driver Manager: Allow me to present, dear sir, a driver class fit for a mysql database! By the laws of the divine factory pattern, I've gone to the trouble of dynamically loading it for you, so that you don't have to load it yourself and you don't have to know which class is being loaded! Here you go thank you come again.
Me: Now, Mysql Driver, will you please just connect me to my stupid fucking test database?
Mysql Driver: SURE I WOULD LOVE TO
Me: mother fucking cuntbags
[Edit]
Also...JDBC indexes are 1 based. Who the fuck came up with that?
[coding] The Best Punching Bags are Made of Straw Men
I've been accused of hating my "own kind" by various people. In my defense...its completely true. At the moment, the group of people I'd like to bitch about are Java programmers, who, based on useless answers I've been reading on Stack Overflow, are ostensibly obsessed with trying to make Java a non-turing-complete language. Let me try to describe, with an analogy, the kind of frustration I'm dealing with at the moment:
Me: "I'm trying to build a calculator, but when I run a Servlet inside [thing1] on [thing2], the 'Add' feature doesnt work."
Enterprise Java Dev: "Well, what numbers to do you want to add?"
Me: "Well I was gonna start with 5 and 8, but-"
Enterprise Java Dev: "No problem! Just edit your spring configuration--Spring is way better than Jetty because it has more xml--and add this:
<resources>
<resource>
<directory>${basedir}/src/main/resources/META-INF</directory>
<includes>
<include><add name="fivePlusEight"><operand which="left" type="five"><operand which=right type="nine"></include>
</includes>
<targetPath>${project.build.directory}/dependency</targetPath>
</resource>
</resources>
...and that way you don't have to write any code!"
Me: "mother fucking cuntbag"
Me: "I'm trying to build a calculator, but when I run a Servlet inside [thing1] on [thing2], the 'Add' feature doesnt work."
Enterprise Java Dev: "Well, what numbers to do you want to add?"
Me: "Well I was gonna start with 5 and 8, but-"
Enterprise Java Dev: "No problem! Just edit your spring configuration--Spring is way better than Jetty because it has more xml--and add this:
<resources>
<resource>
<directory>${basedir}/src/main/resources/META-INF</directory>
<includes>
<include><add name="fivePlusEight"><operand which="left" type="five"><operand which=right type="nine"></include>
</includes>
<targetPath>${project.build.directory}/dependency</targetPath>
</resource>
</resources>
...and that way you don't have to write any code!"
Me: "mother fucking cuntbag"
Sunday, March 16, 2014
The State of Piracy
I just found an oatmeal comic that describes the exact process I go through over and over again when trying to watch movies.
In conclusion, fuck amazon for blocking linux users and bittorrent is awesome.
In conclusion, fuck amazon for blocking linux users and bittorrent is awesome.
Thursday, March 13, 2014
[coding] Nancy Drew and the Case of the Fucked Up Camera Orientation
That Android devices may be held in different orientations, and that the users of those devices don't want upside down pictures is probably obvious to everyone. For some reason, the Android SDK developers chose not to include right-side-up rotation code in the SDK. It is clear that they knew there would be a nearly universal need for it because the put the alleged algorithm for it directly into the comments of the Camera API. However they apparently couldn't be bothered to write the method to calculate it for us.
Which leaves me with The Problem. I'm going to skip the intermediate steps because I don't remember them. Here is what I know now:
1. my camera rotation code is wrong according to the API documentation
2. my camera rotation code is most definitely wrong according to common sense
3. my camera rotation code works perfectly on: the Nexus 7, the Acer Iconia A, the Acer Iconia B, the Digital2, and the lenovo Yoga 8.
4. my camera rotation code DOES NOT work on the Galaxy Tab 3
4b. The preview does work, but the final picture doesn't, and the preview and the final picture are using the same rotation angle.
5. the inputs and outputs of my camera rotation code are identical on the Galaxy Tab 3 and the Acer Iconia A. That is to say, the camera angle is the same on both devices, and my resulting rotation calculations are the same value on both devices, AND I'm setting the same rotation parameters on the camera, but its working on the Iconia A and not on the Galaxy Tab.
I don't know whats going on. The fucking code I wrote shouldn't work on any of the devices (I switched the front and back camera algorithms), and the behavior should be consistent, especially between the preview and the final picture. Even worse, no one on stack overflow appears to be having this problem--that, or everyone asking the right questions are just being drowned out by people asking how to use the camera.
Once thing I'm doing differently is that I am choosing not to handle orientation changes myself--the sample camera code appears to assume that people would do that. Another thing I can try is to look at the code for the camera app, if its open source.
[Edit]
Quick update: android orientation handling is fucked up. I still prefer it over windows phone though.
[Edit]
First of all, to get the source for the camera app apparently now you have to use git:
git clone https://android.googlesource.com/platform/packages/apps/Camera.git
Fixed my code...the problem is now occurring only on samsung devices.
[Edit]
The windows picture viewer ALSO ignores the EXIF orientation.
GIMP detects the orientation, and prompts you to "change" it if you want to see the image in the correct orientation.
What I'm gathering from this, is that EXIF orientation is bullshit, and also the Samsung camera device driver developers were fucking lazy.
[Edit]
Android provides an EXIF parser for you.
The EXIF parser provided by android cannot read from a Stream. Only a file. (the source code of the Camera app uses some 3d library that is not part of the SDK)
The Camera code callback gives you a stream, not a file.
The core design of Java can be accused of being bloated but it has, from the beginning, made a giant fucking deal about file vs stream abstractions.
You don't need to memorize Code Complete to avoid these types of design mistakes.
[Final Edit ?]
Yeah, so cam orientation is still messed up on this piece of shit Digital Pad 2. I'm going to write that one off, partly because my code is consistent with the official camera app and partly because the manufacturer's website did not come up in web searches.
Ladies and Gentlemen, I give you fuckers the Glorious Arrow of Orientation:
That picture was actually taken on my Samsung Galaxy S 2 with the official Camera app, which means that since I was holding the phone ostensibly right side up, that file actually has the rotation wrong--if you open it in the wrong program, the arrow is pointing to the left.
So.
Conclusions:
There are four ways to get device orientation:
1. toDegrees(getWindowManager().getDefaultDisplay().getRotation())
2. toDegrees(getResources().getConfiguration().orientation)
3. onConfigurationChanged(Configuration)
4. OrientationEventListener
1 & 2 are equivalent, so lets revise our list for impact:
1. toDegrees(getWindowManager().getDefaultDisplay().getRotation()) / toDegrees(getResources().getConfiguration().orientation)
2. onConfigurationChanged(Configuration)
3. OrientationEventListener
Each of those methods gives a DIFFERENT FUCKING ANSWER. One of them actually only seems to give the values 90 and 180 regardless of which orientation.
The one that works with the Camera is #3. Naturally, it is the most inconvenient to use. You can't pull the value (and in fact, when your activity is getting created, the value might be wrong anyway so you have to update later), you have to receive it in the listener. The value you get will be in the range [0-359], but the camera only takes {0, 90, 180, 270} so you have to:
lastReadOrientation = (orientation + 45) / 90 * 90 % 360;
Then you set the orientation on the camera. The API code turned to be right, however you'll note that for the Front Camera, the preview is mirrored while the camera is not, so you need two different algorithms.
Next, some cameras actually give you a messed up picture. That is to say the camera didn't actually rotate the image; it just put a "hey this image is rotated" note in the EXIF header.
I don't give a fuck how well the data matches the EXIF standard; the fact that the platforms own bitmap decoder can't fix the rotation automatically and probably half of the image editing programs out there ignore the EXIF orientation means the data is fucked up.
The official Camera app actually doesnt fix the orientation. The android developers and most of the posters on stack overflow appear to believe it is acceptable to just save the corrupt picture and deal with the rotation every fucking time an image is loaded. I consider that unacceptable; so I'm rotating the image in memory.
If you were paying attention, you'll note that I just decided to do something the API devs didn't specifically anticipate. As always, this means we're in for a big fucking headache. First, the stupid ass EXIF tools in the API can't read bytes in memory. So I used a third party lib:
http://javadoc.metadata-extractor.googlecode.com/git/2.6.4/index.html
public static int getExifOrientation(byte[] jpg) {
// http://stackoverflow.com/questions/12944123/reading-android-jpeg-exif-metadata-from-picture-callback
if(jpg == null) return 0;
try{
//uses metadata-extractor-2.6.4.jar and xmpcore.jar
Metadata metadata = JpegMetadataReader.readMetadata(
new BufferedInputStream(new ByteArrayInputStream(jpg)), false);
// Get the EXIF orientation.
final ExifIFD0Directory exifIFD0Directory = metadata.getDirectory(ExifIFD0Directory.class);
if(exifIFD0Directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION))
{
return exifIFD0Directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
}
else
{
return 0;
}
}catch(Exception e){
log("cant read exif orientation", e);
return 0;
}
}
public static Matrix getMatrixForExifOrientation(int exifOrientation){
// from http://stackoverflow.com/questions/12944123/reading-android-jpeg-exif-metadata-from-picture-callback
final Matrix bitmapMatrix = new Matrix();
switch(exifOrientation)
{
case 1: break; // top left
case 2: bitmapMatrix.postScale(-1, 1); break; // top right
case 3: bitmapMatrix.postRotate(180); break; // bottom right
case 4: bitmapMatrix.postRotate(180); bitmapMatrix.postScale(-1, 1); break; // bottom left
case 5: bitmapMatrix.postRotate(90); bitmapMatrix.postScale(-1, 1); break; // left top
case 6: bitmapMatrix.postRotate(90); break; // right top
case 7: bitmapMatrix.postRotate(270); bitmapMatrix.postScale(-1, 1); break; // right bottom
case 8: bitmapMatrix.postRotate(270); break; // left bottom
default: break; // Unknown
}
return bitmapMatrix;
}
That code will probably look like shit...I don't care.
Next, we have to deal with the fact that bitmaps can be transformed in place. You have to create a new bitmap in the process, which invariably causes an Out of Memory Error.
To mitigate this, first put this in the application tag in your manifest:
android:largeHeap="true"
Secondly, ONLY do the transform if the EXIF orientation != 1. One means no orientation change, and--of the devices I found in Best Buy--samsung appears to be the only manufacturer I found that uses the EXIF orientation, so by limiting the transformation code to only those devices we greatly reduce the chances of Out of Memory Errors.
Fun fact: The Samsung Galaxy S 2 cannot do reverse portrait.
Also, it is important to be constantly setting the camera orientation when the orientation listener fires. You can't rely on having your activity recreated, or even on onConfigurationChanged() being fired when doing orientation changes manually; the value will be wrong at those points in time, especially when you physically rotate the devices more than 135 degrees in one shot. Also, the Acer Iconia A series seems to have the completely wrong orientation when the activity starts, so you need the listener to fire to fix that.
Which leaves me with The Problem. I'm going to skip the intermediate steps because I don't remember them. Here is what I know now:
1. my camera rotation code is wrong according to the API documentation
2. my camera rotation code is most definitely wrong according to common sense
3. my camera rotation code works perfectly on: the Nexus 7, the Acer Iconia A, the Acer Iconia B, the Digital2, and the lenovo Yoga 8.
4. my camera rotation code DOES NOT work on the Galaxy Tab 3
4b. The preview does work, but the final picture doesn't, and the preview and the final picture are using the same rotation angle.
5. the inputs and outputs of my camera rotation code are identical on the Galaxy Tab 3 and the Acer Iconia A. That is to say, the camera angle is the same on both devices, and my resulting rotation calculations are the same value on both devices, AND I'm setting the same rotation parameters on the camera, but its working on the Iconia A and not on the Galaxy Tab.
I don't know whats going on. The fucking code I wrote shouldn't work on any of the devices (I switched the front and back camera algorithms), and the behavior should be consistent, especially between the preview and the final picture. Even worse, no one on stack overflow appears to be having this problem--that, or everyone asking the right questions are just being drowned out by people asking how to use the camera.
Once thing I'm doing differently is that I am choosing not to handle orientation changes myself--the sample camera code appears to assume that people would do that. Another thing I can try is to look at the code for the camera app, if its open source.
[Edit]
Quick update: android orientation handling is fucked up. I still prefer it over windows phone though.
[Edit]
First of all, to get the source for the camera app apparently now you have to use git:
git clone https://android.googlesource.com/platform/packages/apps/Camera.git
Fixed my code...the problem is now occurring only on samsung devices.
- http://stackoverflow.com/questions/18203369/camera-capture-orientation-on-samsung-devices-in-android
- http://www.impulseadventure.com/photo/exif-orientation.html
[Edit]
The windows picture viewer ALSO ignores the EXIF orientation.
GIMP detects the orientation, and prompts you to "change" it if you want to see the image in the correct orientation.
What I'm gathering from this, is that EXIF orientation is bullshit, and also the Samsung camera device driver developers were fucking lazy.
[Edit]
Android provides an EXIF parser for you.
The EXIF parser provided by android cannot read from a Stream. Only a file. (the source code of the Camera app uses some 3d library that is not part of the SDK)
The Camera code callback gives you a stream, not a file.
The core design of Java can be accused of being bloated but it has, from the beginning, made a giant fucking deal about file vs stream abstractions.
You don't need to memorize Code Complete to avoid these types of design mistakes.
[Final Edit ?]
Yeah, so cam orientation is still messed up on this piece of shit Digital Pad 2. I'm going to write that one off, partly because my code is consistent with the official camera app and partly because the manufacturer's website did not come up in web searches.
Ladies and Gentlemen, I give you fuckers the Glorious Arrow of Orientation:
That picture was actually taken on my Samsung Galaxy S 2 with the official Camera app, which means that since I was holding the phone ostensibly right side up, that file actually has the rotation wrong--if you open it in the wrong program, the arrow is pointing to the left.
So.
Conclusions:
There are four ways to get device orientation:
1. toDegrees(getWindowManager().getDefaultDisplay().getRotation())
2. toDegrees(getResources().getConfiguration().orientation)
3. onConfigurationChanged(Configuration)
4. OrientationEventListener
1 & 2 are equivalent, so lets revise our list for impact:
1. toDegrees(getWindowManager().getDefaultDisplay().getRotation()) / toDegrees(getResources().getConfiguration().orientation)
2. onConfigurationChanged(Configuration)
3. OrientationEventListener
Each of those methods gives a DIFFERENT FUCKING ANSWER. One of them actually only seems to give the values 90 and 180 regardless of which orientation.
The one that works with the Camera is #3. Naturally, it is the most inconvenient to use. You can't pull the value (and in fact, when your activity is getting created, the value might be wrong anyway so you have to update later), you have to receive it in the listener. The value you get will be in the range [0-359], but the camera only takes {0, 90, 180, 270} so you have to:
lastReadOrientation = (orientation + 45) / 90 * 90 % 360;
Then you set the orientation on the camera. The API code turned to be right, however you'll note that for the Front Camera, the preview is mirrored while the camera is not, so you need two different algorithms.
Next, some cameras actually give you a messed up picture. That is to say the camera didn't actually rotate the image; it just put a "hey this image is rotated" note in the EXIF header.
I don't give a fuck how well the data matches the EXIF standard; the fact that the platforms own bitmap decoder can't fix the rotation automatically and probably half of the image editing programs out there ignore the EXIF orientation means the data is fucked up.
The official Camera app actually doesnt fix the orientation. The android developers and most of the posters on stack overflow appear to believe it is acceptable to just save the corrupt picture and deal with the rotation every fucking time an image is loaded. I consider that unacceptable; so I'm rotating the image in memory.
If you were paying attention, you'll note that I just decided to do something the API devs didn't specifically anticipate. As always, this means we're in for a big fucking headache. First, the stupid ass EXIF tools in the API can't read bytes in memory. So I used a third party lib:
http://javadoc.metadata-extractor.googlecode.com/git/2.6.4/index.html
public static int getExifOrientation(byte[] jpg) {
// http://stackoverflow.com/questions/12944123/reading-android-jpeg-exif-metadata-from-picture-callback
if(jpg == null) return 0;
try{
//uses metadata-extractor-2.6.4.jar and xmpcore.jar
Metadata metadata = JpegMetadataReader.readMetadata(
new BufferedInputStream(new ByteArrayInputStream(jpg)), false);
// Get the EXIF orientation.
final ExifIFD0Directory exifIFD0Directory = metadata.getDirectory(ExifIFD0Directory.class);
if(exifIFD0Directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION))
{
return exifIFD0Directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
}
else
{
return 0;
}
}catch(Exception e){
log("cant read exif orientation", e);
return 0;
}
}
public static Matrix getMatrixForExifOrientation(int exifOrientation){
// from http://stackoverflow.com/questions/12944123/reading-android-jpeg-exif-metadata-from-picture-callback
final Matrix bitmapMatrix = new Matrix();
switch(exifOrientation)
{
case 1: break; // top left
case 2: bitmapMatrix.postScale(-1, 1); break; // top right
case 3: bitmapMatrix.postRotate(180); break; // bottom right
case 4: bitmapMatrix.postRotate(180); bitmapMatrix.postScale(-1, 1); break; // bottom left
case 5: bitmapMatrix.postRotate(90); bitmapMatrix.postScale(-1, 1); break; // left top
case 6: bitmapMatrix.postRotate(90); break; // right top
case 7: bitmapMatrix.postRotate(270); bitmapMatrix.postScale(-1, 1); break; // right bottom
case 8: bitmapMatrix.postRotate(270); break; // left bottom
default: break; // Unknown
}
return bitmapMatrix;
}
That code will probably look like shit...I don't care.
Next, we have to deal with the fact that bitmaps can be transformed in place. You have to create a new bitmap in the process, which invariably causes an Out of Memory Error.
To mitigate this, first put this in the application tag in your manifest:
android:largeHeap="true"
Secondly, ONLY do the transform if the EXIF orientation != 1. One means no orientation change, and--of the devices I found in Best Buy--samsung appears to be the only manufacturer I found that uses the EXIF orientation, so by limiting the transformation code to only those devices we greatly reduce the chances of Out of Memory Errors.
Fun fact: The Samsung Galaxy S 2 cannot do reverse portrait.
Also, it is important to be constantly setting the camera orientation when the orientation listener fires. You can't rely on having your activity recreated, or even on onConfigurationChanged() being fired when doing orientation changes manually; the value will be wrong at those points in time, especially when you physically rotate the devices more than 135 degrees in one shot. Also, the Acer Iconia A series seems to have the completely wrong orientation when the activity starts, so you need the listener to fire to fix that.
Tuesday, March 11, 2014
[coding] sudo vim /etc/motd
The programs included with The Linux Operating System are "free" software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright ...all you need to know is
they they all come with strings attached.
The Linux Operating System comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
The raspberry device is pretty cool, but the recommended linux flavor is based on one of those douchebag versions that tack that weird shit on the front of the name linux. Since none of the "free" software zealots are ever going to log into my print server, I thought I would save this message here in case a good opportunity ever presents itself.
Saturday, March 8, 2014
Philly / NYC 2014 Part 2
First things first. I've accomplished my primary mission: to find an apartment in NYC. It was more difficult that I thought it would be, but luck, it would seem, had my back.
I literally booked a flight, threw my checkbook in a bag, and headed to NYC to find apartments. I'm not joking; that was my plan. In hindsight, this turned out a lot like the time I hopped on a plane to Kristy's wedding with no real plans and arrived to my surprise at a tiny airport with whicker chairs and, literally, no public transport. I'm using the old definition of literally; there wasn't even a taxi stand.
Anyway, turns out I showed up here way to early to get an apartment for May. Most of the apartments on StreatEasy and craigslist were for either Right Now, or April. Also, brokers want more paperwork than a checkbook. They dont even take personal checks. They want consecutive pay stubs, W2s, bank statements, and letters of recommendation. Lots of letters of recommendation. Like they thought my current apartment complex was going to bother writing a letter for me.
The thing that saved me was that I found a 'housing wanted' section of craigslist where people who need apartments are the ones who post the ad. Seemed kind of backwards but I threw something up there on a whim. Turns out that saved my ass, because exactly one of the responses ended up being perfect. I got a temporary sublet on a two bedroom apartment in an area with young people where I can commute to work on the subway in a reasonable amount of time and I can keep my car in an off-street space.
It was rather amazing how much the entire trip changed for me as soon as I wired the deposit. Previously I was quite stressed and questioning my overall game plan here. I started to wonder if I really liked new york for itself, or if I just liked the fact that it wasn't seattle. I mean, so far my favorite things about it are the simple facts that a subway system exists and people know how to cross the fucking street. I'm not even joking; those are my two favorite things right now. Anyway. The moment my living space was secured Everything became Awesome. All the fun I've had since was just gravy.
So now that all that is solved, what to do with my last night here? I tried going to a comedy show last night, but it was a very small venue, and apparently in venues that small all of the comics make crowdwork (making fun of people in the audience) most of their show. I narrowly escaped being ridiculed because I ended up at the same table with a nice Venezuelen couple and the comics all assumed I was their light skinned Venezuelan friend, and apparently couldn't think of anything funny about that. My real Venezuelen friend is going to be quite amused when he finds out about that.
Tonight...idk. I think I'm going to go find some pho, and then find another comedy show.
I literally booked a flight, threw my checkbook in a bag, and headed to NYC to find apartments. I'm not joking; that was my plan. In hindsight, this turned out a lot like the time I hopped on a plane to Kristy's wedding with no real plans and arrived to my surprise at a tiny airport with whicker chairs and, literally, no public transport. I'm using the old definition of literally; there wasn't even a taxi stand.
Anyway, turns out I showed up here way to early to get an apartment for May. Most of the apartments on StreatEasy and craigslist were for either Right Now, or April. Also, brokers want more paperwork than a checkbook. They dont even take personal checks. They want consecutive pay stubs, W2s, bank statements, and letters of recommendation. Lots of letters of recommendation. Like they thought my current apartment complex was going to bother writing a letter for me.
The thing that saved me was that I found a 'housing wanted' section of craigslist where people who need apartments are the ones who post the ad. Seemed kind of backwards but I threw something up there on a whim. Turns out that saved my ass, because exactly one of the responses ended up being perfect. I got a temporary sublet on a two bedroom apartment in an area with young people where I can commute to work on the subway in a reasonable amount of time and I can keep my car in an off-street space.
It was rather amazing how much the entire trip changed for me as soon as I wired the deposit. Previously I was quite stressed and questioning my overall game plan here. I started to wonder if I really liked new york for itself, or if I just liked the fact that it wasn't seattle. I mean, so far my favorite things about it are the simple facts that a subway system exists and people know how to cross the fucking street. I'm not even joking; those are my two favorite things right now. Anyway. The moment my living space was secured Everything became Awesome. All the fun I've had since was just gravy.
So now that all that is solved, what to do with my last night here? I tried going to a comedy show last night, but it was a very small venue, and apparently in venues that small all of the comics make crowdwork (making fun of people in the audience) most of their show. I narrowly escaped being ridiculed because I ended up at the same table with a nice Venezuelen couple and the comics all assumed I was their light skinned Venezuelan friend, and apparently couldn't think of anything funny about that. My real Venezuelen friend is going to be quite amused when he finds out about that.
Tonight...idk. I think I'm going to go find some pho, and then find another comedy show.
Thursday, March 6, 2014
The Potential Milestone
For this start up thing...our first beta tester received a production version of the app and apparently likes it.
I'm pretty sure I should be celebrating but I am too busy waiting for something to go wrong.
In other news...in NYC presently...I have no idea what I'm doing.
Sunday, March 2, 2014
Funny Hats
It has been a long, eventful, and interesting weekend. I have continued my policy of, when being presented with two choices (left or right, to do or not to do), I always choice the option that, even if its the wrong choice, will at least bring me perfect clarity regarding which one I should have taken.
The highlight of the weekend was skiing. I bought a silly hat, because my tiger hat is kind of broken. This new hat was made for skiing, and it consists of hundreds of black and yellow dreadlock-like cloth ribbons that hang down to my waste. I became somewhat of a spectacle because I made a habit of skiing backwards to the cliff edge and then bombing the run with a friend, my fake dreads flying in the wind. It was awesome.
Sadly, my silly hat did not bring me into the arms of a cute girl. I will wear it again tomorrow.
Everything about the East Coast seems amazing. Every turn I make on the roads makes me wish I had my WRX here to tear them up. Every bridge I take makes me wish I had my sportbike to ride on. Every familiar face I see makes me wish I never left.
In a strange way, it seems like I left very recently. Despite all thats changed certain things have stayed very much the same. Can I rekindle old connections? Maybe. Either way I'm going to have to make lots of new ones.
The highlight of the weekend was skiing. I bought a silly hat, because my tiger hat is kind of broken. This new hat was made for skiing, and it consists of hundreds of black and yellow dreadlock-like cloth ribbons that hang down to my waste. I became somewhat of a spectacle because I made a habit of skiing backwards to the cliff edge and then bombing the run with a friend, my fake dreads flying in the wind. It was awesome.
Sadly, my silly hat did not bring me into the arms of a cute girl. I will wear it again tomorrow.
Everything about the East Coast seems amazing. Every turn I make on the roads makes me wish I had my WRX here to tear them up. Every bridge I take makes me wish I had my sportbike to ride on. Every familiar face I see makes me wish I never left.
In a strange way, it seems like I left very recently. Despite all thats changed certain things have stayed very much the same. Can I rekindle old connections? Maybe. Either way I'm going to have to make lots of new ones.
Subscribe to:
Posts (Atom)

