Shallow Thoughts : : May
Akkana's Musings on Open Source Computing and Technology, Science, and Nature.
Thu, 28 May 2015
I recently needed to update an old Android app that I hadn't touched
in years. My Eclipse setup is way out of date, and I've been hearing
about more and more projects switching to using command-line builds.
I wanted to ditch my fiddly, difficult to install Eclipse setup
and switch to something easier to use.
Some of the big open-source packages, like OsmAnd, have switched to
gradle for their Java builds. So I tried to install gradle -- and
on Debian, apt-get install gradle
wanted to pull
in a total of 153 packages! Maybe gradle wasn't the best option to pursue.
But there's another option for command-line android builds: ant.
When I tried apt-get install ant, since I
already have Java installed (I think the relevant package
is openjdk-7-jdk
), it installed without needing a single
additional package.
For a small program, that's clearly a better way to go!
Then I needed to create a build directory and move my project into it.
That turned out to be fairly easy, too -- certainly compared to the
hours it spent setting up an Eclipse environment.
Here's how to set up your ant Android build:
First install the Android "Stand-alone SDK Tools" from
Installing
the Android SDK. This requires a fair amount of clicking around,
accepting licenses, and waiting for a long download.
Now install an SDK or two. Use
android sdk
to install new SDK versions, and
android list targets
to see what versions you have installed.
Create a new directory for your project, cd into it, and then:
android create project --name YourProject --path . --target android-19 --package tld.yourdomain.YourProject --activity YourProject
Adjust the Android target for the version you want to use.
When this is done, type ant
with no arguments to
make sure the directory structure was created properly.
If it doesn't print errors, that's a good sign.
Check that local.properties has sdk.dir set correctly.
It should have picked that up from your environment.
There will be a stub source file in src/tld/yourdomain/YourProject.java.
Edit it as needed, or, if you're transferring a project from another
build system such as eclipse, copy the existing .java files
to that directory.
If you have custom icons for your project, or other resources like
layout or menu files, put them in the appropriate directories under res.
The directory structure is the same as in eclipse, but unlike an eclipse
build, you can edit the files at any time without the build mysteriously
breaking.
Signing your app
Now you'll need a key to sign your app. Eclipse generates a debugging
key automatically, but ant doesn't. It's better to use a real key
anyway, since debugging keys expire and need to be regenerated periodically.
If you don't already have a key, generate one with:
keytool -genkey -v -keystore my-key.keystore -alias mykey -keyalg RSA -sigalg SHA1withRSA -keysize 2048 -validity 10000
It will ask you for a password; be sure to use one you won't forget
(or record it somewhere).
You can use any filename you want instead of my-key.keystore, and any
alias you want instead of mykey.
Now create a file called ant.properties containing these two lines:
key.store=/path/to/my-key.keystore
key.alias=mykey
Some tutorials tell you to put this in
build.properties, but
that's outdated and no longer works.
If you forget your key alias, you can find out with this command and
the password:
keytool -list -keystore /path/to/my-key.keystore
Optionally, you can also include your key's password:
key.store.password=xxxx
key.alias.password=xxxx
If you don't, you'll be prompted twice for the password (which echoes
on the terminal, so be aware of that if anyone is bored enough to watch
over your shoulder as you build packages. I guess build-signing keys
aren't considered particularly high security). Of course, you should
make sure not to include both the private keystore file and the password
in any public code repository.
Building
Finally, you're ready to build!
ant release
If you get an error like:
AndroidManifest.xml:6: error: Error: No resource found that matches the given name (at 'icon' with value '@drawable/ic_launcher').
it's because older eclipse builds wanted icons named
icon.png,
while ant wants them named
ic_launcher.png. You can fix this
either by renaming your icons to
res/drawable-hdpi/ic_launcher.png
(and the same for
res/drawable-lpdi and
-mdpi), or
by removing everything under
bin (
rm -rf bin/*
)
and then editing
AndroidManifest.xml. If you don't clear
bin
before rebuilding,
bin/AndroidManifest.xml will take
precendence over the
AndroidManifest.xml in the root, so you
might have to edit both files.
After ant release
, your binary will be in
bin/YourProject-release.apk.
If you have an adb connection, you can (re)install it with:
adb install -r bin/YourProject-release.apk
Done! So much easier than eclipse, and you can use any editor you want,
and check your files into any version control system.
That just leaves the coding part.
If only Java development were as easy as Python or C ...
Tags: android, programming, ant
[
20:52 May 28, 2015
More programming |
permalink to this entry |
]
Fri, 15 May 2015
I have a bunch of devices that use VFAT filesystems. MP3 players,
camera SD cards, SD cards in my Android tablet. I mount them through
/etc/fstab, and the files always look executable, so when
I ls -f
them, they all have asterisks after their names.
I don't generally execute files on these devices; I'd prefer the
files to have a mode that doesn't make them look executable.
I'd like the files to be mode 644 (or 0644 in most programming
languages, since it's an octal, or base 8, number). 644 in binary
is 110 100 100, or as the Unix ls
command puts it,
rw-r--r--.
There's a directive, fmask, that you can put in fstab
entries to control the mode of files when the device is mounted.
(Here's Wikipedia's long
umask article.)
But how do you get from the mode you want the files to be, 644,
to the mask?
The mask (which corresponds to the umask
command)
represent the bits you don't want to have set. So, for instance,
if you don't want the world-execute bit (1) set, you'd put 1 in the mask.
If you don't want the world-write bit (2) set, as you likely don't, put
2 in the mask. So that's already a clue that I'm going to want the
rightmost byte to be 3: I don't want files mounted from my MP3 player
to be either world writable or executable.
But I also don't want to have to puzzle out the details of all nine bits
every time I set an fmask. Isn't there some way I can take the mode I
want the files to be -- 644 -- and turn them into the mask I'd need to
put in /etc/fstab or set as a umask?
Fortunately, there is. It seemed like it ought to be straightforward,
but it took a little fiddling to get it into a one-line command I can type.
I made it a shell function in my .zshrc:
# What's the complement of a number, e.g. the fmask in fstab to get
# a given file mode for vfat files? Sample usage: invertmask 755
invertmask() {
python -c "print '0%o' % (~(0777 & 0$1) & 0777)"
}
This takes whatever argument I give to it -- $1 -- and takes
only the three rightmost bytes from it, (0777 & 0$1). It takes
the bitwise NOT of that, ~. But the result of that is a negative
number, and we only want the three rightmost bytes of the result,
(result) & 0777, expressed as an octal number -- which
we can do in python by printing it as %o. Whew!
Here's a shorter, cleaner looking alias that does the same thing,
though it's not as clear about what it's doing:
invertmask1() {
python -c "print '0%o' % (0777 - 0$1)"
}
So now, for my MP3 player I can put this in /etc/fstab:
UUID=0000-009E /mp3 vfat user,noauto,exec,fmask=133,shortname=lower 0 0
Tags: linux, cmdline
[
10:27 May 15, 2015
More linux/cmdline |
permalink to this entry |
]
Wed, 06 May 2015
I saw on Slashdot that Google is going to start down-rating sites
that don't meet its criteria of "mobile-friendly":
Are you ready for Google's 'Mobilegeddon' on Tuesday?.
And from the the Slashdot
discussion, it was pretty clear that Google's definition included
some arbitrary hoops to jump through.
So I headed over to
Google's
Mobile-friendly test to check out some of my pages.
Now, most of my website seemed to me like it ought to be pretty mobile
friendly. It's size agnostic: I don't specify any arbitrary page widths
in pixels, so most of my pages can resize down as far as necessary
(I was under the impression that was what "responsive design" meant
for websites, though I've been doing it for many years and it seems now
that "responsive design" includes a whole lot of phone-specific tweaks
and elaborate CSS for moving things around based on size.)
I also don't set font sizes that might make the page less accessible to
someone with vision problems -- or to someone on a small screen with high
pixel density. So I was pretty confident.
I shouldn't have been. Basically all of my pages failed. And in chasing
down some of the problems I've learned a bit about Google's mobile rules,
as well as about some weird quirks in how current mobile browsers
render websites.
Basically, all of my pages failed with the same three errors:
- Text too small to read
- Links too close together
- Mobile viewport not set
What? I wasn't specifying text size at all -- if the text is too small
to read with the default font, surely that's a bug in the mobile browser,
not a bug in my website. Same with links too close together, when I'm
using the browser's default line spacing.
But it turned out that the first two points were meaningless. They were
just a side effect of that third error: the mobile viewport.
The mandatory meta viewport tag
It turns out that any page that doesn't add a new meta tag, called
"viewport", will automatically fail Google's mobile friendly test
and be downranked accordingly. What's that all about?
Apparently it's originally Apple's fault.
iPhones, by default, pretend their screen is 980 pixels wide instead
of the actual 320 or 640, and render content accordingly, and so they
shrink everything down by a factor of 3 (980/320).
They do this assuming that most website designers will set a hard
limit of 980 pixels (which I've always considered to be bad design)
... and further assuming that their users care more about seeing the
beautiful layout of a website than about reading the website's text.
And Google apparently felt, at some point during the Android development
process, that they should copy Apple in this silly behavior.
I'm not sure when Android started doing this; my Android 2.3 Samsung
doesn't do it, so it must have happened later than that.
Anyway, after implementing this, Apple then introduced a meta tag you
can add to an HTML file to tell iPhone browsers not to do this scaling,
and to display the text at normal text size. There are various
forms for this tag, but the most common is:
<meta name="viewport" content="width=device-width, initial-scale=1">
(A lot of examples I found on the web at first suggested this:
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
but
don't do that -- it prevents people from zooming in to see
more detail, and hurts the accessibility of the page, since people who
need to zoom in won't be able to. Here's more on that:
Stop
using the viewport meta tag (until you know how to use it.)
Just to be clear, Google is telling us that in order not to have our pages
downgraded, we have to add a new tag to every page on the web
to tell mobile browsers not to do something silly that they shouldn't
have been doing in the first place, and which Google implemented
to copy a crazy thing Apple was doing.
How width and initial-scale relate
Documentation on how width and initial-scale relate to
each other, and which takes precedence, are scant. Apple's
documentation
on the meta viewport tag says that setting
initial-scale=1 automatically sets width=device-width.
That implies that the two are basically equivalent: that they're only
different if you want to do something else, like set a page width in
pixels (use width=) or set the width to some ratio of the
device width other than 1 (use initial-scale=.
That means that using initial-scale=1 should
imply width=device-width -- yet nearly everyone on the web
seems to use both. So I'm doing that, too.
Apparently there was once a point to it: some older iPhones had a bug
involving switching orientation to landscape mode, and specifying
both initial-scale=1 and width=device-width helped,
but supposedly that's long since been fixed.
initial-scale=2, by the way, sets the viewport to half what it would
have been otherwise; so if the width would have been 320, it sets it
to 160, so you'll see half as much. Why you'd want to set
initial-scale to anything besides 1 in a web page, I don't know.
If the width specified by initial-scale conflicts with that
specified by width, supposedly iOS browsers will
take the larger of the two, while Android won't accept a width
directive less than 320, according to
Quirks mode:
testing Meta viewport.
It would be lovely to be able to test this stuff; but my only Android
device is running Android 2.3, which doesn't do all this silly zooming out.
It does what a sensible small-screen device should do: it shows text at
normal, readable size by default, and lets you zoom in or out if you need to.
(Only marginally related, but interesting if you're doing elaborate
stylesheets that take device resolution into account, is
A List Apart's discussion,
A
Pixel Identity Crisis.)
Control width of images
Once I added meta viewport tags, most of my pages passed the test.
But I was seeing something else on some of my photo pages, as well as
blog pages where I have inline images:
- Content wider than screen
- Links too close together
Image pages are all about showing an image. Many of my images are wider
than 320 pixels ... and thus get flagged as too wide for the screen.
Note the scrollbars, and how you can only see a fraction of the image.
There's a simple way to fix this, and unlike the meta viewport thing,
it actually makes sense.
The solution is to force images to be no wider than the screen
with this little piece of CSS:
<style type="text/css">
img { max-width: 100%; height: auto; }
</style>
I've been using similar CSS in my RSS reader for several months, and I
know how much better it made the web, on news sites that insist on
using 1600 pixel wide images inline in stories.
So I'm happy to add it to my photo pages. If someone on a mobile
browser wants to view every hair in a squirrel's tail, they can
still zoom in to the page, or long-press on the image to view it at
full resolution. Or rotate to landscape mode.
The CSS rule works for those wide page banners too. Or you can use
overflow: hidden
if the right side of your banner isn't
all that important.
Anyway, that takes care of the "page too wide" problem. As for the
"Links too close together" even after I added the meta viewport tag,
that was just plain bad HTML and CSS, showing that I don't do enough
testing on different window sizes. I fixed it so the buttons lay out
better and don't draw on top of each other on super narrow screens,
which I should have done long ago. Likewise for some layout problems
I found on my blog.
So despite my annoyance with the whole viewport thing, Google's mandate
did make me re-examine some pages that really needed fixing,
and should have improved my website quite a bit for anyone looking at
it on a small screen. I'm glad of that.
It'll be a while before I have all my pages converted, especially
that business of adding the meta tag to all of them. But readers, if
you see usability problems with my site, whether on mobile devices or
otherwise, please tell me about them!
Tags: web, android
[
15:48 May 06, 2015
More tech/web |
permalink to this entry |
]