Header image alt text

Floppie's Blag

Entertainment, technology, and the occasional shenanigans

While developing my first couple Android apps (which didn’t make it to market), and my first one that actually did make it to market (shameless plug: Should I Wear Pants?), I noticed what is, in my opinion, a significant issue: No easy way to look at least halfway decent on all devices. We’re supporting a massive variety of different screen sizes and densities, and the best the Android SDK can provide us natively are:

What we were able to find was a PDF white paper from Vanteon Electronic Design about this very subject – auto-scaling.  They explain in further detail the two problems I outlined above, and start going into detail on a solution.  The advantages of auto-scaling include:

  • Considerably easier support for many devices.  You probably still need to make separate layouts for tablets and phones – it depends what makes sense for your app – but that’s really it.  For each form factor, you don’t have much to worry about as far as screen size/density.
  • In most cases, you only need to include one drawable – the highest resolution version – because the image will be scaled to fit.  The only exception to this is when you’re concerned about the amount of memory your activity consumes – I’m not sure how Android handles memory with scaled images, if it’ll store the full-size version in memory or just what’s displayed.

We took the solution one step further – rather than having the Activity run the scaling code, we used it and created a ScalingLinearLayout view that automatically scales itself and its contents when it’s drawn or redrawn.  We used, and tweaked slightly, the code from that PDF, in a file called Scale.java (to copy and paste this, use the “View Raw Code” button):

package com.icantbelieveitsnotcloud.lib.util;

import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

public class Scale {
	public static void scaleContents(View rootView, View container) {
		Scale.scaleContents(rootView, container, rootView.getWidth(), rootView.getHeight());
	}

	// Scales the contents of the given view so that it completely fills the given
	// container on one axis (that is, we're scaling isotropically).
	public static void scaleContents(View rootView, View container, int width, int height) {
		Log.d("notcloud.scale", "Scale::scaleContents: container: " + container.getWidth() + "x" + container.getHeight() + ".");

		// Compute the scaling ratio
		float xScale = (float)container.getWidth() / width;
		float yScale = (float)container.getHeight() / height;
		float scale = Math.min(xScale, yScale);

		// Scale our contents
		Log.d("notcloud.scale", "Scale::scaleContents: scale=" + scale + ", width=" + width + ", height=" + height + ".");
		scaleViewAndChildren(rootView, scale, 0);
	}

	// Scale the given view, its contents, and all of its children by the given factor.
	public static void scaleViewAndChildren(View root, float scale, int canary) {
		// Retrieve the view's layout information
		ViewGroup.LayoutParams layoutParams = root.getLayoutParams();

		// Scale the View itself
		if(layoutParams.width != ViewGroup.LayoutParams.MATCH_PARENT && layoutParams.width != ViewGroup.LayoutParams.WRAP_CONTENT) {
			layoutParams.width *= scale;
		}
		if(layoutParams.height != ViewGroup.LayoutParams.MATCH_PARENT && layoutParams.height != ViewGroup.LayoutParams.WRAP_CONTENT) {
			layoutParams.height *= scale;
		}

		// If the View has margins, scale those too
		if(layoutParams instanceof ViewGroup.MarginLayoutParams) {
			ViewGroup.MarginLayoutParams marginParams = (ViewGroup.MarginLayoutParams)layoutParams;
			marginParams.leftMargin *= scale;
			marginParams.topMargin *= scale;
			marginParams.rightMargin *= scale;
			marginParams.bottomMargin *= scale;
		}
		root.setLayoutParams(layoutParams);

		// Same treatment for padding
		root.setPadding(
			(int)(root.getPaddingLeft() * scale),
			(int)(root.getPaddingTop() * scale),
			(int)(root.getPaddingRight() * scale),
			(int)(root.getPaddingBottom() * scale)
		);

		// If it's a TextView, scale the font size
		/*
		if(root instanceof TextView) {
			TextView tv = (TextView)root;
			tv.setTextSize(tv.getTextSize() * scale); //< We do NOT want to do this.
		}
		*/

		// If it's a ViewGroup, recurse!
		if(root instanceof ViewGroup) {
			ViewGroup vg = (ViewGroup)root;
			for(int i = 0; i < vg.getChildCount(); i++) {
				scaleViewAndChildren(vg.getChildAt(i), scale, canary + 1);
			}
		}
	}
}

You’ll notice that we have the TextView font scaling parts shut off.  That’s code that came in from Vanteon’s scaling, but in the apps we’ve used it in, we’ve found it undesirable.  I left it in the article because you may want to use it – if so, find the comment that says “If it’s a TextView, scale the font size” and uncomment the if() {} block that follows.

Using that Scale class, we built ScalingLinearLayout, which extends LinearLayout, as follows:

package com.icantbelieveitsnotcloud.lib.view;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.LinearLayout;
import com.icantbelieveitsnotcloud.lib.util.Scale;

public class ScalingLinearLayout extends LinearLayout {
	int baseWidth;
	int baseHeight;
	boolean alreadyScaled;
	float scale;
	int expectedWidth;
	int expectedHeight;

	public ScalingLinearLayout(Context context) {
		super(context);

		Log.d("notcloud.view", "ScalingLinearLayout: width=" + this.getWidth() + ", height=" + this.getHeight());
		this.alreadyScaled = false;
	}

	public ScalingLinearLayout(Context context, AttributeSet attributes) {
		super(context, attributes);

		Log.d("notcloud.view", "ScalingLinearLayout: width=" + this.getWidth() + ", height=" + this.getHeight());
		this.alreadyScaled = false;
	}

	public void onFinishInflate() {
		Log.d("notcloud.view", "ScalingLinearLayout::onFinishInflate: 1 width=" + this.getWidth() + ", height=" + this.getHeight());

		// Do an initial measurement of this layout with no major restrictions on size.
		// This will allow us to figure out what the original desired width and height are.
		this.measure(1000, 1000); // Adjust this up if necessary.
		this.baseWidth = this.getMeasuredWidth();
		this.baseHeight = this.getMeasuredHeight();
		Log.d("notcloud.view", "ScalingLinearLayout::onFinishInflate: 2 width=" + this.getWidth() + ", height=" + this.getHeight());

		Log.d("notcloud.view", "ScalingLinearLayout::onFinishInflate: alreadyScaled=" + this.alreadyScaled);
		Log.d("notcloud.view", "ScalingLinearLayout::onFinishInflate: scale=" + this.scale);
		if(this.alreadyScaled) {
			Scale.scaleViewAndChildren((LinearLayout)this, this.scale, 0);
		}
	}

	public void draw(Canvas canvas) {
		// Get the current width and height.
		int width = this.getWidth();
		int height = this.getHeight();

		// Figure out if we need to scale the layout.
		// We may need to scale if:
		//    1. We haven't scaled it before.
		//    2. The width has changed.
		//    3. The height has changed.
		if(!this.alreadyScaled || width != this.expectedWidth || height != this.expectedHeight) {
			// Figure out the x-scaling.
			float xScale = (float)width / this.baseWidth;
			if(this.alreadyScaled && width != this.expectedWidth) {
				xScale = (float)width / this.expectedWidth;
			}
			// Figure out the y-scaling.
			float yScale = (float)height / this.baseHeight;
			if(this.alreadyScaled && height != this.expectedHeight) {
				yScale = (float)height / this.expectedHeight;
			}

			// Scale the layout.
			this.scale = Math.min(xScale, yScale);
			Log.d("notcloud.view", "ScalingLinearLayout::onLayout: Scaling!");
			Scale.scaleViewAndChildren((LinearLayout)this, this.scale, 0);

			// Mark that we've already scaled this layout, and what
			// the width and height were when we did so.
			this.alreadyScaled = true;
			this.expectedWidth = width;
			this.expectedHeight = height;

			// Finally, return.
			return;
		}

		super.draw(canvas);
	}
}

When calling this in a layout, you’ll want to do something along these lines:

<com.icantbelieveitsnotcloud.lib.view.ScalingLinearLayout
	android:id="@+id/sllInventory"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:background="@drawable/dark_rock_tile"
	android:gravity="center">
		<RelativeLayout android:id="@+id/rlyInventory"
			android:layout_width="120px"
			android:layout_height="165px">
		</RelativeLayout>
</com.icantbelieveitsnotcloud.lib.view.ScalingLinearLayout>

And that’s it. The resulting effect is that it will scale to fill either the width or the height, whichever ends up being a smaller scale – this way it doesn’t overflow the other dimension. It operates by exploiting the fact that, before being drawn, the ScalingLinearLayout‘s size will only be enough to contain the stuff inside it. During the draw() function, though, the match_parent width and height directives will cause the full size to be returned by getWidth() and getHeight(). It then performs some simple math on the new and original dimensions, and calls the scaleViewAndChildren() method from our Scale class above.

There’s no need for any additional XML files for styleable attributes, because ScalingLinearLayout accepts exactly the same attributes as LinearLayout. You could easily adapt this to make (for example) a ScalingRelativeLayout that extends RelativeLayout, if you had such a need. If we do this for anything in the future, I’ll be sure to post it on here – but given that we’re just wrapping the ScalingLinearLayout around a RelativeLayout in this case, I don’t anticipate such a need.

Since this is published on the Internet, feel free to use it in your work – no copyright. Any questions, feel free to leave them in a comment. I’m happy to answer them.

It looks like my first real post since resurrecting this blog (aside from the Pep Boys thing) will be a DIY guide for the car.  Click here if you want to skip the intro and get straight to how to do this.

My starter’s been acting up for quite a while now.  The first time was probably nine months or so ago…I was riding around on a bad clutch master cylinder, backing out of a parking spot, when someone came speeding up the aisle in the parking lot.  Had to slam on the brakes, but hadn’t gotten the clutch system back up to pressure, so I couldn’t disengage it.  Car stalled, car wouldn’t start back up – the relay would click once and that was it.  Shaking the front end up and down worked, I assume this followed the same principle as the tap-it-with-a-hammer trick and just unstuck it – that was the logic that made me decide to do it.

All was good for several months, until one day this summer, I had the same problem when trying to leave for work.  I took the motorcycle that day, and the next day just tapping the key over and over unstuck it.  Again, all was good for several months.  Fast forward to Friday (December 14, 2012), me and my boss went to leave for lunch and the relay just clicked.  He drove, and at the end of the day I was able to go to Frisbee (and then home afterward) because a friend was willing to push start me.  Thanks, Doug.  I got home, found someone who could get me the starter the next day (Pep Boys), took Ashley’s car to pick it up, got home, and started ripping and tearing.

If you’re here for a DIY guide on doing the starter in this car, this is where it starts.  My first piece of advice, don’t do it yourself if you’re just a back-yard mechanic like me.  It requires pulling the intake manifold, which is really pretty shitty for a DIY.  If you’re adamant about doing it yourself though, read on. You will need:

  • A new starter (this is the starter I used)
  • A socket wrench with 10mm, 12mm, and 14mm sockets
  • A 12mm crescent wrench
  • Pliers
  • An empty cup
  • The new starter
  • A new throttle body gasket
  • A new intake manifold gasket, unless you already have a reusable gasket in place

Step one: Remove battery.. Seriously. Getting shocked sucks; do it now.

RSX Throttle Body, Intake Elbow Off

Elbow off

RSX Throttle Body, Intake Elbow On

Elbow on. Yeah, I should redo the silicone spray on that intake elbow before putting it back on.

Step two: Pull the intake.  If you have the stock intake, you’ll need to pull the whole airbox; if you have a short-ram intake, take that off; if, like me, you have a cold air intake, just take the elbow off.  Turbo guys, you know your piping better than I do. If you’re not sure how to pull the stock airbox, I’d consult the service manual or Google it.  ClubRSX probably has a DIY guide on how to remove that somewhere on the board; either that, or an aftermarket intake install guide will probably include stock airbox removal.

Next, you’ll need to start getting the throttle body out.  I’d start with the coolant lines.  Get a cup handy to catch some coolant if you don’t want it to get all over the ground.  Now, I didn’t need to worry about this because I have my throttle body coolant tracts bypassed, and as a result I don’t have a picture.  In the following marked picture, C and D are on the throttle body and if I remember correctly, A goes to C and B goes to D.  Comments welcome if I’m mistaken.

RSX Throttle Body Coolant Lines

A and B are on the engine; C and D are on the throttle body.

Disconnect the hoses from C and D, and have a cup ready to dump their contents in.  You’ll probably end up getting coolant on the ground. Next, disconnect the plugs from the sensors on top of the throttle body, as follows:

RSX Throttle Body, Top Sensors Disconnect

Throttle body with the sensors on top disconnected

RSX Throttle Body Vacuum Hose

That same hose, disconnected but resting in place

You’ll also want to disconnect that hose in the middle of the photo.  You can see in the picture that I already had the clamp off.  Disconnect it at the other end too to get it out of the way; the picture at the right shows it disconnected at both ends.  Put some tape over both ends of the hose and both ports to make sure no dust/debris gets in there, put it somewhere safe, and make sure you don’t lose the clamps.

Next, you’ll need to get your throttle linkage disconnected.  Grab your 12mm wrench and loosen the nuts holding them into the brass bracket, then pull the bolts out of the bracket itself and remove it.  After you get the bracket off, open the throttle the whole way using the swivel linkage the cables are connected to and pull the cables out.  With it open all the way, you’ll be able to see how it comes out; it will make more sense visibly than trying to explain it in text here.  Make a note of which cable went in which side of the swivel link.  Mark them somehow if you need to.

RSX Throttle Linkage Bracket

Throttle linkage, loosened from the brass bracket. You can see one of the two bolts that holds the bracket in is already removed.

RSX Bottom-Left Throttle Body Bolt

Bottom left bolt

RSX Bottom-Right Throttle Body Nut

Bottom right nut

At this point, you should have everything disconnected from the top of the throttle body.  It’s time to pull the nuts and bolts holding it on – two of each.  These are all the same size; 14mm, I believe.  There’s one at each corner – top left and bottom right are nuts, while top right and bottom left are studs.  The two at the top are easily visible, but the ones at the bottom are harder to see.  You might be better off just feeling your way to them with your fingers, but in case you want a visual, there are photos of the bottom ones to the right of this paragraph.

Once you have all four nuts off, grab the throttle body and pull.  It may not come off willingly; if it’s been on there for quite a while, it will be stuck there.  It has a little bit of play in the space around the studs, so you can try wiggling it up and down.  This worked for me just this past time I was removing it.  If that doesn’t work for you, you’ll need to gently get a flathead or other prybar in between the throttle body and the intake manifold to get them apart.  This will definitely ruin the throttle body gasket, but you’re not supposed to reuse that anyway; just be careful not to damage the manifold or the throttle body too much.

As you get the throttle body pulled off of the manifold, you’ll notice two plugs on the front/bottom part, which you wouldn’t have been able to get to before.  Unplug those.  The one on the bottom is tough; it’s got a rubber cover that makes it difficult to squeeze the clip enough to remove it.  In this photo, you can see the one on the front, but not really the one on the bottom – it’s also black, and obscured by the one you can see.

RSX Throttle Body, Partially Removed Showing Bottom Plugs

Throttle body partially removed, showing bottom plugs

So now, you have the throttle body removed.  Set it aside, carefully.  Tape off all the vacuum ports to make sure nothing gets in there.  Time to get started ripping and tearing on the manifold.  First, pull the vacuum hose off the port on the right.  This picture only shows it disconnected from the manifold, but I’d just pull it off completely.  Don’t forget to tape off both ends of the hose and both nipples.

RSX Intake Manifold, Right-Side Vacuum Port

Vacuum port on the right-hand side of the manifold

Next, take care of the PCV port.  You can see in the photo below that, because I have a breather tank installed, that wasn’t a concern for me – but you can simply remove the PCV hose and tape things off.  I did have to disconnect the other end of the long hose coming from the PCV valve from the breather tank in order to move that hose out of the way, so if you have a similar setup, you’ll probably want to do that.

RSX PCV System With Breather Tank

My PCV system; you can see that I just have a cap on the manifold port.  You can also see a 14mm nut that you’re going to have to remove before this manifold will come off.

Next you’ll want to remove the two black brackets that sit just behind the fuel rail.  These are each just a single bolt; 10mm I believe.  Photos below.

RSX Fuel Rail Bracket, Left Side

Left bracket

RSX Fuel Rail Bracket, Right Side

Right bracket

Next, pull the injector plugs off and disconnect the injector ground.  The stock location for this is the valve cover; a lot of people move it to the intake manifold, but with the Hondata intake manifold gasket that’s no longer a great grounding location.  The photo below shows it disconnected; apologies for the motion blur, it it wasn’t blurry on my phone’s lower resolution screen.

RSX Intake Manifold, Injector Plugs Disconnected

Intake manifold showing injector plugs disconnected

Next, bleed the fuel rail.  Grab an adjustable wrench and a shop towel; stuff the shop towel around the fuel pressure regulator, get the wrench on it, and loosen it.  Photo of the regulator below.

RSX Fuel Pressure Regulator

Fuel pressure regulator; this also shows one of the 12mm intake manifold bolts you need to remove

At this point, I started to lose my patience and the photos started to get a bit sparse.  Once the rail is bled, you’ll need to disconnect the fuel line from the rail, remove the two bolts holding the rail down (12mm), and pull the rail off the manifold.  The injectors will come out with it.

Now it’s time to pull the manifold hardware.  This is two bolts and two nuts on top, and three bolts and a nut on the bottom.  I believe they were all 12mm.  The top ones are easy to see and remove, keep those nuts together as they’re the only ones of their kind and you’re going to need them again in a minute.  I have some photos of the hardware on the bottom:

RSX PCV System With Breather Tank

My PCV system; you can see that I just have a cap on the manifold port. This also shows the nut you have to remove from the bottom of the manifold.

RSX Bottom Left Intake Manifold Bolt

Bottom left intake manifold bolt

RSX Fuel Pressure Regulator

Fuel pressure regulator; this also shows one of the 12mm intake manifold bolts you need to remove

RSX Bottom Right Intake Manifold Bolt

Bottom right intake manifold bolt; it’s circled, because it’s difficult to see. This is behind where the throttle body bolts to the manifold.

Once you have all those removed, you need to pull the left stud on the top.  To do this, spin one nut on backwards and the other on normally, and tighten the nuts together (so you’re turning the inside one to the left and the outside one to the right).  You can then use them together to turn the entire stud.  Once that stud is removed, you can rotate the manifold clockwise to get it out from behind the power steering pump bracket.

At this point, my patience was entirely gone and I have no more photos.  My apologies.  Once you have the manifold off, though, the starter’s right there on the front of the engine, below where the manifold was.  Three bolts and it’s off.

Putting it all back together is relatively easy, but make sure you consult your service manual for torque specifications.

  1. Install starter
  2. Put manifold back in place
  3. Reinsert stud, tighten
  4. Bolt the manifold back down
  5. Reinstall fuel rail/injectors
  6. Bolt those brackets behind the fuel rail back on
  7. Plug the fuel injectors back in and bolt down the ground
  8. Put the PCV hose back in place
  9. Reinstall that vacuum hose on the right side of the manifold
  10. Put the throttle body part way back in place so you can plug the two bottom plugs back in
  11. Bolt the throttle body on
  12. Plug the two top plugs back in
  13. Get the throttle linkages back in place and bolt their bracket on; tighten the linkage nuts down to the bracket
  14. Plug the vacuum line back in the top
  15. Get all the coolant lines on the bottom of the throttle body back in place
  16. Reinstall your intake piping
  17. Put the battery back in

And you’re done.  Mission accomplished.

Should I Wear Pants? Version 1.5.0

Posted by Floppie on 2012-12-20
Posted in AndroidJavaLinkProgramming  | Tagged With: , , , | No Comments yet, please leave one

Should I Wear Pants?I pushed an update tonight to Should I Wear Pants?, it was a fairly significant set of changes, and increments the version to 1.5.0.  Changes are as follows:

  • Added the capability for users to specify their location. To do this:
    • Open the preferences screen
    • Under “ABOUT YOU”, uncheck “Use Current Location”
    • The “Location” option will be made available – open this to set your location.
    • Refer to the Help topic called “Specifying Your Location” for more information on supported formats, etc.
  • Added Christmas outfits; these will run from the 23rd to the 27th if you’re in Calendar Fun mode.
  • Fixed a number of small issues with location and weather detection.
    • This was made possible by some new automated testing; we now have approximately 275 cities around the world verified as working and accurate, while ensuring no regressions occurred for the common case when fixing the edge cases.
    • Note that this doesn’t cover the entire world; thus far, we have the Americas, Europe, Australia, and New Zealand covered. We are working to add coverage to the rest of the world.

If you haven’t, do me a favor and check out the app.  If you’re feeling especially nice, I’d certainly appreciate a 5-star rating on the Play Store.