Or, the case of the Android app out-of-order calls
On Android, there is a primary thread which runs all UI stuff. If a GUI operation is ran in a different thread, it just won't work, and may throw an error. If you block this thread with too much processing... well... bad things happen. Due to this design, you have to push all UI operations to this main thread using Looper.run().
Runnables pushed to this thread are always ran in FIFO execution order, which is a useful guarantee for programming.
So I decided to get smart and create the following function to add asynchronous calls that needed to be run on the primary thread. It takes a Runnable and either runs it immediately, if already on the Primary thread, or otherwise adds it to the Primary Thread’s queue.
//Run a function on primary threadpublicstaticvoid RunOnPrimary(RunnableR)
{
LooperL=MainActivity.getMainLooper();
//Start commenting here so that items are always added to the queue, forcing in-order processesingif(Looper.myLooper()==Looper.getMainLooper())
R.run();
else//End commenting herenewHandler(Looper.getMainLooper()).post(R);
}
I was getting weird behaviors though in a part of the project where some actions pulled in from JavaScript were happening before subsequent actions.
After the normal debugging one-by-one steps to figure it out, I realized that MAYBE some of the JavaScript calls were, for some bizarre reason, already running on the primary thread. In this case they would run immediately, before the queued items started coming in. This turned out to be the case, so I ended up having to comment out the first 3 lines after the function’s first comment (if/R.run/else), and it worked great.
I found it kind of irritating that I had to add actions to the queue when it could have been run immediately on the current thread, but oh well, I didn’t really have a choice if I want to make sure everything is always run in order across the system.
Due to the facts of life and economic demand, a majority of the work I’ve been doing over the past 10 years has been web work
(PHP, JavaScript, SQL, HTML, CSS).
When I first started in this field, browser incompatibilities were a MAJOR problem, but nowadays, it’s incredibly rare for me to write a piece of code in one browser that doesn’t automatically work in all the others without bugs. Maybe it’s just the fact I’ve been doing this for so long that I know what to avoid, but I’d like to think it’s mostly because the browser makers have gotten their acts together and subscribe to making everything compatible by following [W3C] guidelines.
Browser incompatibilities still creep up from time to time though, and I’ll be writing up about a few of them in some of my upcoming posts. The majority and biggest problems I seem to run into nowadays revolve around mobiles.
So in Google Chrome for Android, when trying to include an HTML5 video, the “autoplay” HTML5 attribute seems to be ignored. The obvious less-than-optimal hack would be to just include a “.play()” call in JavaScript, but this seems to only work if the action is triggered by the user (like when trying to create a popup window). So as far as I can see right now, there is no way to autoplay a video in Google Chrome for Android.
Due to another new project I’ve been working on, I was able to test out different methods on rendering sprites on the Android platform.
Method #1: Use OpenGL ES to render to a buffer in C via JNI. This method turned out to be the fastest if polygons are batched via lists in a single (or a few) draw calls, and if using fixed instead of floating point numbers. It also opens up a lot of other effects natively not easily implemented through the other two methods (native 3D and matrices). However, it does not support antialiasing natively as is advertised and as I expected. The OpenGL ES 1.0 specs report “antialiased polygon rendering (with alpha border fragments, not multisample)”, but I could not find any information on this at all, and no antialiasing calls that I tried actually worked. There are methods that would allow antialiasing, but none efficient enough for a phone.
Method #2: Drawing directly to a pixel buffer in C via JNI. This method came in a very close second place if using memory copying functions to blit images, but that also removes a lot of important effects like translucency. Results were less than optimal when not using direct memory copies (I didn’t even worry about including them below) :-\.
Method #3: Rendering through the Android API in Java. While this method is the most robust in terms of feature set, it is also by far the slowest.
For my personal goals, I’ve come to the conclusion it would be best to use Method #1 for my heavy rendering tasks, and used baked translucent pixels around the borders in the images to emulate 2D antialiasing. The translucent borders could also be added at load time. I think I may also use Method #3 for some other special effects and overlays, though that will also make it harder to port, but I think worth it for now.
The following are the approximate average FPS results to the speed tests I performed (preceded by column descriptions):
The Test: 45*45 quads (2*45*45=4050 polygons) drawn to cover the whole screen each frame, none overlapping
Emulator/Droid: The tests were run on 2 separate systems. An Android emulator running v2.2 on my Intel core i5 2.53ghz laptop, and my Motorola Droid also running Android v2.2.
FLOAT/FIXED: This is only used on Method #1. OpenGL ES supports (in different functions) both floating point numbers and fixed point numbers. Since phones usually have no FPU, and the ones that do might have very slow ones, it’s much more safe efficiency wise to not use floating point numbers. The OpenGL ES solution was to have non-floating (fixed) point decimal arithmetic numbers that act virtually the exact same as normal integers when used with arithmetic functions, but do not have near the range as either floating point numbers or normal integers. The gain by going from floating to fixed was marginal in my tests. 16 bit integers (shorts), which I prefer, can also be used and showed near identical results to fixed point numbers.
INDIVIDUAL/ALL: This is only used on Method #1. It means whether all the quads were drawn individually in a different call for each (which draws 2 triangles to make a quad), or in 1 call from a list of all the triangles. The gain by going from INDIVIDUAL to ALL was ~1fps on the emulator and ~12fps on the Droid in my tests.
Seeing as there are a number of applications on the market that have both a “Free” and “Full” version, you’d think this would be an easy thing to accomplish. Unfortunately, the marketplace uses an application’s package name as its unique identifier, so both versions have to have a different package name, which is again, a bit of a nuisance.
One method of remedying this is just having a recursive string replace through all the files [...]
I spent a bit of time coming up with a solution for this a few months ago for my latest project, [TEMPORARILY DOWN**]. This solution uses a shared package with a shared code base and resources that the different project versions pull from.
**The project that is a great example of how this process works should be uploaded very soon. At that time this message will disappear and appropriate links will be added. You’ll know this has happened when I upload my next project.
The steps for this setup are as follows: (The source for [TEMPORARILY DOWN**] can be used as an example)
First some definitions that will be used below*:
ProjectName: The base name of the project (e.g. “MyAndroidProject”)
VersionName: The name of separate versions (e.g. “Free” and “Full”)
SharedName: The name of the shared section of the code (e.g. “Shared”)
BasePackageName: The base name of the package group (e.g. “com.example.MyAndroidProject”)
BasePackageDirectory: The base path of the package group (e.g. “com/example/MyAndroidProject”)
*Please note these definitions are used in code sections below.
Create the directory structure:
A base directory of ProjectName (e.g. “~/MyAndroidProject”)
A subdirectory under the base directory named SharedName for the shared files (e.g. “~/MyAndroidProject/Shared”). It will hold any shared files in appropriate Android standard directories (e.g. “res”, “src”).
Subdirectories under the base directory named VersionName for each version’s project (e.g. “~/MyAndroidProject/Free”). Each of these will hold its own complete project including the AndroidManifest.
Creating the shared resources: There’s nothing special about shared resources (probably in “SharedName/res”), except I suggest noting at the top of the files that they are shared, for reference sake.
Creating the shared code:
Shared code goes in “SharedName/src/BasePackageDirectory/SharedName” (e.g. “~/MyAndroidProject/Shared/src/com/example/MyAndroidProject/Shared”).
As noted for shared resources, you might want to note at the top of the files that they are shared.
Set the package name for shared code to “BasePackageName.SharedName” (e.g. “package com.example.MyAndroidProject.Shared;”).
Shared code should never directly refer back to a version’s package (non shared) code except through reflection.
Resource IDs are still accessible in this shared package through the “R” class, but when accessed the function or class that does the accessing needs to be proceeded with “@SuppressWarnings({ "static-access" })”. The “R” variable also has a “Version” member that can be used to alter shared code flow depending on the version being used. This will be explained more in detail later.
*BONUS*
If shared code needs to access information from a static member in a non-shared class, reflection can be used, for example:
A static function can be called in a similar manner through reflection:
Class.forName("BasePackageName."+R.Version.name()+".NonSharedClassName").getMethod("StaticMethodName", new Class[0]).invoke(null);
*BONUS* Global Shared Class: I also suggest having a shared class that holds global variables that allows easy interaction from the shared to non shared classes, and holds static information that both use, with members including:
A reference to a Context (or Activity) from the main program
The BasePackageName (needed for reflection, and other stuff like preference storage)
Other useful interfaces like logging
Creating the non-shared code:
Create a separate project for each version in their corresponding subdirectories listed in the third step of the Create the directory structure" section above.
Make the package name for a version as “BasePackageName.VersionName”.
When referencing shared classes form an android manifest, make sure to use their full package name, for example, a shared activity would look like “<activity android:name="BasePackageName.SharedName.ActivityName">”
Import the shared package into all non-shared class files “import BasePackageName.SharedName.*;”
Linking the shared code into each project:
The shared code now needs to get integrated into each project. To do this, all the shared files need to be symbolically (or hard) linked back into their corresponding directories for each version.
First, make sure each project directory also contains the same subdirectories as those found in the shared directory.
The script I have written for this, which needs to be put in the base directory, is as follows: [EDIT ON 2011-01-03 @ 6:28am] See here for a better copy of the script. [/EDIT]
#!/bin/bash
#Run this file to install links to shared files into all branches
LN="ln" #Use hard links, which work well in Windows and require less logic to calculate linking
#if [ `uname || grep -vi 'cygwin'` ]; then #If not windows (NOT YET SUPPORTED)
# LN="ln -s" #Use symbolic links, which take some additional logic that is not yet programmed
#fi
LocalBranches=`find -maxdepth 1 -type d | grep -iPv "^\.(/SharedName|)$"` #Find version names, ignoring "." ".." and the shared directory
#Propagate shared files into different versions
cd Shared
for i in $LocalBranches; do
find -type f -print0 | xargs -0 -i rm -f ../$i/{} #Clear out old links from version just in case the link has been undone
if [ "$1" != "clean" ]; then
find -type f -print0 | xargs -0 -i $LN {} ../$i/{} #Link shared files into directories
fi
done
Tying the resources together:
The resources IDs in the “R” class might need to be accessible by the shared classes. To do this, an “R” class needs to be added into the shared namespace that duplicates the information. Unfortunately, the Android generated resources ID class “R” marks everything as “final” so class duplication requires a bit more work than would be preferable.
Each different version needs its own “R” class, put into the directory “~/MyAndroidProject/VersionName/src/BasePackageDirectory/SharedName” that the shared code reads from.
This class will also contain version information so the shared classes can know which version it is currently interacting with.
The code is as follows:
package BasePackageName.SharedName;
import BasePackageName.VersionName.R.*;
public final class R //Mimic the resources from the (non-shared) parent project
{
//There may be more resource ID groups than these that need to be shared
final static attr attr=null;
final static drawable drawable=null;
final static id id=null;
final static layout layout=null;
final static string string=null;
final static xml xml=null;
final static array array=null;
public enum Versions { Version1, Version2 }; //List versions here
public final static Versions Version=Versions.CurrentVersion;
}
Whenever this “shared” “R” class is accessed, the function or class that does the accessing needs to be proceeded with “@SuppressWarnings({ "static-access" })”. This is due to the hack required to reproduce the “final” class information from the original “R” class into a shared class.
Working with shared projects and code in Eclipse:
When modifying shared code in Eclipse for any of the different versions, other version’s projects need to be refreshed to pick up the new code. I tried many different methods and processes to fix this different package name problem but this described method is still much quicker and easier than any of the others.
A fix for this issue has been recommended and I consider it resolved. There was always a fix for this but it had not been given yet, as there was confusion to the problem in the bug report. Other people are still reporting the problem but I cannot reproduce it after the suggested fix.
[END OF EDIT]
It has come to my attention that my Mini Acuity application suffers from a “known defect” in Android, namely, that it “requires” 2 security permissions I did not ask for or use.
The “Storage” and “Phone Calls” permissions are listed in the “Application Info” in the Android settings dialog even though the market does not specify that they are used during install.
This is a result of using an old SDK version as the base of my code. Google actually encourages authors to use the minimum possible SDK version, as Android is backwards compatible, so that as many users as possible are supported. This means my only solution to not having these security permissions listed would be to upgrade the SDK version, thereby disabling compatibility from some older phones. I wish there was a way I could see the distribution of Android Versions for my application’s downloads to help determine if this would be worth it.
I have updated the Mini Acuity project page accordingly with a “Permissions” section.
On a side note, it occurs to me how much of a security hole this [possibly] is. If an application is running on these old SDK versions, and the user sees an application has only network access permission, they might not worry about the application stealing their data while it could! Though, I have not yet done the research to confirm this, or plan on doing so. I feel more and more that Android’s security system leaves a lot to be desired.
One of the main selling points for me for the Android platform was that, as I understood it, the system was supposed to be very “open” in nature. It would allow a programmer to create virtually any application imaginable that they wanted to for users, as long as the user’s security is maintained. This is, of course, the antithesis of Apple’s philosophy with the iPhone/iPod Touch. However, I find this much spouted openness to not be the case at all. Security permissions are way too tight across the board, especially regarding interfacing with the hardware, making many things next to impossible to accomplish. This is especially true when interfacing with the phone functionality. While a programmer is free to do what they want within the scope of their own application(s) and their GUIs, working with the rest of the system can be a major PITA, or even impossible.
Some of this functionality can be gained back with rooted (jail broken) phones, but it is not always easy (or completely safe) to get one’s phone to such a state. It was simple with the Android 2.0 platform, which I originally had on my Motorola Droid, but not so much with the v2.1 software. Version 2.1 is (currently) a major PITA to root, as it entails having to restore the phone to its original state first, losing everything on it (which can, of course, be restored manually). I also, at this point, do not consider it worth it putting in the time to build things for rooted-only phones as the market is much smaller, and I myself haven’t even bothered rooting my phone on the current Android version.
Anyone can also compile their own version of the Android platform as it is open source. This would be worth it, for example, if an organization wanted to distribute their own compilation with modifications internally. However, it doesn’t much help application programmers like myself that want to reach a wide audience. I am also under the impression that putting your own flavor of the Android platform on your phone would lose certain functionalities/things included by the image provided by the provider of the phone (usually cell phone network carriers).
I really like how they did one section of the security system, which is, allowing an application to request special permissions from the operating system. A user is informed of the requested permissions before they install an application from the market place. The main problem is, though, that so many permissions are unavailable that should be possible. Another major downside is that way too many applications request permissions that they shouldn’t be requesting. This leaves users high and dry with applications they consider critical only available if they accept things they don’t want to. For example, many programs request full internet access with no need for it. It would be great to be able to selectively turn off these permissions, but I doubt the option for this is going to happen. I’m going to do more research myself on if an application can be written to do this, but I am not going to get even the slightest hope up on this possibility.
There are at least 3 applications ATM I wanted to create but could not due to permissions:
Call recording: I have written on this previously, but this functionality is unavailable, and Google is not commenting as to why. There are also countless other applications that could use the ability to access a call’s audio. This functionality was available on some older versions of the Android platform (and there are applications out there that take advantage of this), but it seems unavailable on newer versions for 1 of 3 reasons:
Legal reasons: It’s illegal to record calls in some areas (which would be dumb to revoke accessing call audio because of this because it’s legal in so many other places, including where I live in Texas).
Technological reasons: Some phone manufacturers might have it so the audio never even makes it to the operating system (it’s kept on the phone’s radio stack).
Google reasons: They decided it was a feature they no longer wanted to support. The fact of the matter is the interface is provided by the platform to do this, but bugs have been introduced into it and it no longer seems to work.
Automated call menu: I would love to make an application that created an automated call menu on the phone, which could include leaving messages. I would personally use this so I could keep my phone on when sleeping, allowing the phone to direct the caller to either my [local or remote] voice mail or to wake me up if it’s an emergency. This is not possible due to the inability to access a call’s audio, as is explained in the above unimplementable application, but I am betting that there would be many more permissions that would make this not possible.
Global Key interception: I have somewhat solved this problem, as I will be explaining in a post most likely coming tomorrow.
Yet another platform/library to learn. It never ends.
Having recently finished my first Android project (and hopefully not last), I decided to supply some notes I took about the process.
While I am going to try and keep impressions to a minimum on the rest of this post, and keep it to tangible notes, I must first comment that trying to find out things for the Android platform was often like pulling teeth. While its typical Java reference online documentation is all there with all the classes and cross-linking, that is about all it is, very dry and virtually useless beyond a base reference. The comments on variable parameters (and many other sections) in the reference are often coarse and not descriptive at all, for example, one parameter named mask has the basic description as “this is a mask”. Some functions don’t even have descriptions at all.
Perhaps I am getting too complacent as a programmer and getting used to excellent documentation like for Python or GTK (I’ve even grown to love Microsoft documentation after having used it for long enough!). After all, most required information is just a Google away, and being a programmer is often just about finding the proper magical incantations to hook into a certain library. Unfortunately, however, even web searches were often yielding less than fruitful results when dealing with Android, as the platform is relatively new.
Some useful tasks and some problems:
Using the virtual (soft) keyboard without a TextView:
Note: “getWindow().getDecorView()” can also be replaced by a View on your screen
Getting the keyboard input: Add the following function to the Activity that opened the keyboard:
@Override public boolean onKeyDown(int keyCode, KeyEvent msg)
Note: This will not work if you’re not using default keyboard input (like if it’s set to enter Japanese or Chinese characters).
Determining the physical dimensions of the screen:
This should be a trivial task using the DisplayMetrics (getWindowManager().getDefaultDisplay()) interface to get dpis and multiply by the screen dimensions getWindowManager().getDefaultDisplay().getWidth() (and .getHeight). However, it doesn’t always work as it should.
The best method to get the DPI would be to use “DisplayMetrics.xdpi” and “DisplayMetrics.ydpi”, but unfortunately, these are misreported by at least the Motorola Droid. I’ve found “DisplayMetrics.density”*160 to be pretty accurate, but if true accuracy is needed, a calibration screen might be required.
Inform user of touch events: Many Android widgets (Views) change their visual state (highlight) when the user presses down on them to let the user know something is going to happen if the user lifts their finger while still on the widget. Unfortunately, there seems to be no text widget or layout view that does this automatic highlighting by itself (ListViews do in groups). The following is some example code to produce this effect.
import android.view.View.OnTouchListener;
public class CLASSNAME extends Activity
{
@Override public void onCreate(Bundle savedInstanceState)
{
View HighlightView=findViewById(R.id.THE_VIEWS_ID);
HighlightView.setOnTouchListener(HighlightState);
}
private OnTouchListener HighlightState = new OnTouchListener() { public boolean onTouch(View v, MotionEvent event)
{
if(event.getAction()==MotionEvent.ACTION_DOWN)
v.setBackgroundColor(0xFF0000FF); //Set background color to blue
else if(event.getAction()==MotionEvent.ACTION_CANCEL || event.getAction()==MotionEvent.ACTION_UP)
v.setBackgroundResource(0); //No background color
return false;
} };
}
Retrieving the names and IDs of all resources in a resource group:
Setting a color matrix on an image: If you have 2 ImageViews that display the same resource image, and either has a color matrix set on it, the will both share one of the color matrices. If this occurs, copy the image the resource, or use a separate image resource. For kicks, here is an example of setting an inverse color matrix on an image.
requestWindowFeature(Window.FEATURE_NO_TITLE); //This must be called before "setContentView", and hides the title bar
getWindow().setFlags(FULLSCREEN ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0, WindowManager.LayoutParams.FLAG_FULLSCREEN); //Turns on/off the status bar
Starting another local activity: Instead of using Intent(String action) for Context.StartActivity, as the Reference explains, it is much easier to use Intent(Context packageContext, Class<?> cls) like the following: (called from inside an Activity)
Creating a timed event that updates the UI: A function running through java.util.Timer cannot interact with the GUI. One solution to make a timer is with the android.os.Handler interface.
import android.os.Handler;
public class ExampleActivity extends Activity
{
final int InitialDelay, RepeatDelay;
Handler TimedHandler=new Handler();
public void ExampleFunction()
{
TimedHandler.postDelayed(new Runnable() { public void run() {
//Do GUI stuff...
TimedHandler.postDelayed(this, RepeatDelay);
} }, InitialDelay);
}
}
Another solution is to post to a Handler from the Timer function.
When dealing with putting on the market place:
Getting an account to put applications on the Android Market cost $25.
Screenshots shown on the Android Market description page are somewhat buggy, and seemingly randomly either stretch properly or crop. Viewing the full sized screenshots does seem to work properly.
Seeing as there are a number of applications on the market that have both a “Free” and “Full” version, you’d think this would be an easy thing to accomplish. Unfortunately, the marketplace uses an application’s package name as its unique identifier, so both versions have to have a different package name, which is again, a bit of a nuisance.
One method of remedying this is just having a recursive string replace through all the files to change the package names. However, if using eclipse, so you don’t have to reopen it, it’s quicker to update the string first in the manifest, and then renaming the package under the “src” folder by pressing F2 (rename) on it when it is selected.
Also, unfortunately, if you do this, when a person upgrades from the lite to the full version, preferences are not automatically transferred :-\.
The publisher’s market place page is very sparse and leaves a lot to be desired. It also seems to update only once every 24 hours or so (not sure of exact times).
If an application is put up, it WILL get downloads immediately. For example, I put up an application with a description of “This is a test, do not download this” for doing security tests that I took down within like 10 minutes. It already had 2 comments/ratings on it within that time ~.~; .
Google Checkout: Fees. When a copy of your application is purchased, the user has 24 hours to return it. The money is not deposited into your bank account until after this time (when it’s not a weekend). If you want to give your application to someone for free, they need to purchase it through the market, and then you can cancel the purchase transaction before the 24 hours are up. Unfortunately, this has to be done every time they want to update the application. It also seems you cannot buy your own applications, as the purchase server throws an error.
Application Protection:
You can download any Android application by default from your phone to your computer, modify them, and reinstall them back to any phone. An example use for this would be to crack a shareware application where just a single byte probably needs to be changed to make it a full version.
The applications themselves are in an .apk file (which is just a .zip file), and the source code (classes) are encoded as a “Dalvik Executable” file within it (classes.dex), which as I understand it, is optimized Javabytecode. So, AFAIK right now, there is no way to decompile the .dex file back to the original source, like you can with normal Java. However, the Android emulator, part of the Android SDK, includes a tool called dexdump, which allows you to decompile it to bytecode.
The marketplace also has an option to turn on Copy Protection. When this is turned on for an application, the user cannot backup or access the applications package file. I would assume however with a rooted phone you could still grab it from “/data/app-private”, and the rest of the process should be the same. I have not tested this as rooting Android 2.1 is much more of a pain in the butt, ATM, than I want to deal with.
I finally gave in and got myself a Motorola Droid phone, as I’ve been meaning to do for quite some time, but was reluctant to do so due to some known problems with it. I have been incredibly happy with it, and find it leaps and bounds better than the iPhone platform (and many other smart phones).
Pros (when compared to other phones/platforms on the market):
It’s on the Verizon network, which, from my experience, has by far the best coverage and quality
The Android platform keeps things running in the background so you can go in and out of applications freely, as opposed to only being able to run 1 application at a time on the iPhone. This multitasking approach is much better because...
This means that applications can continue to run/process in the background for necessary tasks, like periodically talking and receiving data from servers (without push notifications, which aren’t as powerful)
Applications, since they remain open, take no extra time to start back up every time you want to go to them
Keeping applications open has (as I understand it) next to zero, if not zero, drain on system resources if they are not doing anything in the background
If the phone is running out of memory, it will kill an application that is not in use
I find the touchscreen much more responsive and accurate than the iPhone
The voice recognition is spectacular (especially useful when combined with GPS/mapping programs)
It’s not Apple :-) (and it’s Google!)
It has a physical qwerty keyboard (see Cons)
The lock screen protection is pretty neat and quick (gesture-like unlocking)
Music/books/etc are directly loaded from an SD Card with no need to go through something like iTunes. This can, however, be slow when indexing from the SD card after changes
Music sorts first by folder and then by album. While this would have been great for me a year or so ago when I got my iTouch, it no longer is as useful as I have already gone through my whole music folder and properly re-tagged everything (and it has actually caused a few bugs in my music organization)
Free tethering to a computer through PDANet (1 month trial that blocks HTTPS sites afterwards, but I SSL tunnel my connections anyways :-) ). [Edit on 5/25/2010]: Google has just announced Android 2.2 has a new added feature “a portable Wi-Fi hotspot that can be shared with up to 8 devices.”
Great [5.0 megapixel] camera/video recorder
Applications can use self signed certificates for distribution. Signed applications prevent tampering (which has both advantages and disadvantages :-) ), and self signing means you can develop for the platform for free
Cons:
It cannot be on the Internet and a phone call at the same time (Verizon [CDMA] problem). This is especially problematic when tethering
Even though the system is Linux based, working with the terminal emulator leaves a lot to be desired
Most of my standard bash commands [coreutils] are not there
Permissions are so tight you can barely do anything without rooting
The shell itself is missing a lot of functionality (problems with finding files even though they are in the “path”, bash-type arrow shortcuts [autofill, history], etc)
Motorola has seemingly [possibly?] even removed some of the utilities that should have come with the Android platform (they are on the Android emulator at least) like the command line SQLite program
The physical keyboard is a lot harder to use than my last phone (VX9800, I’ve been using one since 2004), and also leaves a lot to be desired. It’s better than having no keyboard though!
Since there is no spacing between the keys, it is very easy to make a mistake
The number of keys is very small and not always in typical qwerty layout (for numbers and symbols), so it’s hard to find some characters
Some characters (like the pipe | ) are not even listed, and you just have to know how to get to them (alt+shift+comma for the pipe)
The phone does not let you record call conversations. This seems to be a hardware issue specific to the Droid (and possibly the Nexus One) and not necessarily because of the Android platform. I cannot find any official or confirmable information on the reason. [Edited this bullet on 3/1/2010]
It’s made to interface with Java when programming, and you don’t have much of a choice with this :-( . However, C/C++ modules can be made that directly interface easily with the Java base.
Anywho, the original intent of this post was not to talk about the Droid itself, but I figured I might as well get that out of the way while I was on the topic :-). The real point was to talk about a problem I ran into and the solution.
I had a lot of trouble finding a way to quickly set a ringtone to many people. As I have many hundreds of contacts in my phone, and I need groups of people set to different ringtones (and sometimes just one individual), it would have taken many hours to accomplish the task of assigning ringtones to these contacts. I found the perfect solution though in an application, not on the market, called JUSTones (also called “Just Tones”). Not only does it allow you to set ringtones for many people at once, but it also lets you set SMS tones for contacts (Yay!), which is not native to the platform.
Unfortunately, it was made for a depreciated version of the Android platform, and did not work on my phone. I have been in communication with the author, and after sending him the solution to my main problem, he has sent me a new working version (with many more fixes for bugs I mentioned too) ^_^. He even refunded my original purchase and told me to not worry about paying for it, which was pretty cool of him. It’s nice to find other software authors as nice and willing to work with you as this :-).
The problem was that the ringtones being set through the program were not actually being set to the contacts. The reason was that ringtone values are now set in 2 separate places in the OS, and only 1 of them was actually being used. I presume the old location of the value was still being set so legacy applications could still read the ringtone for the contact, but not necessarily set it.
The following is the code I sent to him that copies over the values from the old value location to the new one.
//Prepare to read from the old contacts database
Cursor cur=managedQuery(People.CONTENT_URI, new String[] { People.NAME, People.CUSTOM_RINGTONE }, null, null, People.NAME + " ASC"); //Query the old contacts database
cur.moveToFirst(); //Move to the first record of the old contacts database
//Prepare queries to run on new contacts database
ArrayList<ContentProviderOperation> ops = new ArrayList(); //This holds the queries to run
do //Loop over each [old] record of the database
{
String TheName=String.valueOf(cur.getString(cur.getColumnIndex(People.NAME))); //Get the name from the old record
String TheRingtone=cur.getString(cur.getColumnIndex(People.CUSTOM_RINGTONE)); //Get the ringtone from the old record
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Contacts.CONTENT_URI) //Prepare a query on the new contacts database
.withSelection(ContactsContract.Contacts.DISPLAY_NAME + "=?", new String[]{TheName}) //On any record where the Name = OLD_NAME
.withValue(ContactsContract.Contacts.CUSTOM_RINGTONE, TheRingtone) //Set Ringtone = OLD_RINGTONE
.build());
} while (cur.moveToNext());
//Execute the queries on the new contacts database
try
{
getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
} catch(Exception e) { }