Cupcake and Beyond
Followers
Thursday 5 September 2013
Youtube series on Android development
The series is now my primary way of talking about Android development, so I will be posting the series on this blog also.
Friday 27 May 2011
Why is my AdMob ad not showing up?
Strangely, most of my weekend was spent figuring out how to make my ads show where I want them to show! I could make them work in an ampty activity, but not in the header of my listview. I could add them to the very top of my activity hierarchy, but they would not show up in dialogs.
I finally found some obscure paragraph in some obscure document in deep bowels of Googles AdMob documentaion, briefly mentioning that if the adview does not have enough space around it to show it in the size it wants to display, it will not show at all and will not warn you about this.
Well, fuck me with a rake! That solves a lot of problems. See, the ad width is 320dp, which is exactly the width of a portrait screen theme. Which means, that if you add padding or borders, the ad will not fit!
See, it is common to add a bit of padding to tabhost or listviews to make them look nicetr and give a bit of depth effect. However, after this it will not fit into the tab containers or listview headers/footers.
So! Just make sure your ad is in a container with no padding or borders at all unless you want it to autohide :P
I would actually imagine, that you could hide effectively most of the AdMob ads in your device with a Theme that would add 1px/dp padding to LinearLayout component. Anyone care to try this? Please comment on this article and tell the results if you do. I haven't yet researched how to make my own themes.
Another thing I would like to discuss. I made kinda clever "Message from our sponsors" dialog. Here's how it looks like:
What it does, is it creates an adview and request in the background and lets the activity implement the adlistener interface. If/When you get a "tablet" sized ad like above, it will show a dialog to view it.
Here's the code:
/* @Override protected void onPostResume() { super.onPostResume(); loadAd(); } private Dialog adDialog; private void initAd() { adDialog = new Dialog(this); adDialog.setContentView(R.layout.ad_dialog); adDialog.setTitle("A message from our sponsor..."); adDialog.setOwnerActivity(this); } private void loadAd() { // Banner LinearLayout ll = (LinearLayout) adDialog.findViewById(R.id.ad_layout); AdView ad = new AdView(this, AdSize.IAB_MRECT, "a14dd77743f0b3a"); ad.setAdListener(this); AdRequest req = new AdRequest(); ad.loadAd(req); ll.addView(ad); } @Override public void onReceiveAd(Ad arg0) { adDialog.show(); }
I'm sure you can figure out the rest!
Have fun!
However, there is a problem sometimes. I'm not quite sure why, but I get an InvalidTokexException sometimes.
It might be that I dismiss the Activity just before an ad is being loaded. It will crash the app when it tries to show the dialog after its container activity is finished.
This needs more debugging.
I should probably save the state on onPause of the activity and not try to show it if the activity is paused.
Monday 13 December 2010
String.format is a nono...
I have been working this weekend on an application called "WiFi Wraith" (Already on the market, there's a free edition too).
This is the first time I am working on cavases, bitmaps and programmatic drawing. I draw pretty graphs straight on some UI elemts, which I have a ListView full of. This is done each time the app rescans the networks.
I realized quite fast, that I am doing something silly with my drawing, or so I thought, because the ListView got kinda jittery while dragging. My first obvious idea was to offload the graph drawing to another thread, cache the bitmaps and fetch the "latest" bitmap whenever it is needed.
However, out of curiosity, I also wanted to know how to do profiling on Android! So a quick googling told me that the Android team has made it pretty damn easy!
Only thing I need to do is to call Debug.startMethodTracing() when I want to start logging and Debug.stopMethodTracing() when I want to stop it. Then I can visually inspect the produced logfile by pulling it from the root of the sdcard and use 'traceview' from the android-sdk/tools/.
So, I tried this. My first suspectible suspects of suspicion were these:
Suspect #1:
private static void drawData(Canvas canvas, ListsignalHistory) { float prevx, prevy; float w = canvas.getWidth(); float linelen = (float) Math.ceil(w/signalHistory.size()); prevx = prevy = 0f; float xpoint = 0f; boolean first = true; int datanum = 0; for(Integer ia : signalHistory) { datanum++; int q = WifiManager.calculateSignalLevel(ia, 10); float ypoint = canvas.getHeight(); ypoint = ypoint - ypoint / 10 * q; if(ypoint < 1) ypoint = 1; Paint p = selectBrush(q); if(first) { first = false; prevx = xpoint; prevy = ypoint; } xpoint += linelen; canvas.drawLine(prevx, prevy, xpoint, ypoint, p); int makeline = datanum % 10; if(makeline == 0) { canvas.drawLine(xpoint, canvas.getHeight(), xpoint, ypoint, PaintManager.red); } prevx = xpoint; prevy = ypoint; } }
Suspect #2:
if(signalHistory.size() > 100) { signalHistory = new ArrayList(signalHistory.subList(signalHistory.size()-100, signalHistory.size())); }
Sooo.... Let's fire up traceview and see which one of these is the culprit!
Wait! What the hell! It seems that neither of them!
Drawing takes 10% of time. Resizing of the signalHistory takes pretty much nothing at all...
Most of the time the program is doing this:
if(lastSeen < System.currentTimeMillis() - 10000) { Date d = new Date(lastSeen); String tstr = DateFormat.getDateTimeInstance().format(d); out = String.format(ctx.getString(R.string.item_format_oor), data.SSID, data.BSSID, tstr, locstr); } else { out = String.format(ctx.getString(R.string.item_format), data.SSID, data.BSSID, data.level, data.frequency, data.capabilities, locstr); }
The bloody String.format is killing my app! I just have to try if this really can be the problem, so I'll just comment out the string formatting...
Well, yeah. It made it hell of a lot smoother. Another thing that seems to take a lot of time is populating the list with new items. I have no idea how to side step that.
In fact, I'm not sure how to fix the string formatting. It's kinda crucial for an app to actually show some meaningful info. I recall from some old Android statistics that creating objects is very slow on Android, so I try to avoid it. Based on the old stats, I think concatenating assload of strings and manually converting items to strings should be even slower.
Update: Holy hell! Just concatenating the stings with stupid string + int + float + string, etc... Is faster by about factor of 100!
Thursday 18 November 2010
Handler Puzzle
I was making a small update to MagicPlayer and stumbled upon this puzzling way of handling concurrency.
Can you figure out what it does? (Some irrelevant code snipped)
Class: TracData
public class TrackData extends Handler {
String artist, album, track;
CallBack cb;
Context ctx;
public TrackData(Context ctx, CallBack cb) {
this.ctx = ctx;
this.cb = cb;
}
@Override
public void handleMessage(Message msg) {
if(msg.what == 1) {
cb.setData(this.artist, this.album, this.track);
}
}
public void get(final int sid) {
new Thread() {
public void run() {
TalkWithTheServer();
obtainMessage(1).sendToTarget();
}
}.start();
}
interface CallBack {
public void setData(String artist, String album, String track);
}
}
And another Class: TrackInfo
public class TrackInfo extends Activity implements TrackData.CallBack {
TextView txtArtist;
TextView txtAlbum;
TextView txtTrack;
@Override
public void onCreate(Bundle savedInstanceState) {
SetupStuff();
fetchData(id_of_song_that_is_playing);
}
private void fetchData(int sid) {
TrackData d = new TrackData(this, this);
d.get(sid);
}
@Override
public void setData(String artist, String album, String track) {
txtArtist.setText(artist);
txtAlbum.setText(album);
txtTrack.setText(track);
}
}
!!!!!SPOILER!!!!!
TrackData is background fetcher which gets some data from my server and upon receiving it, updates the text fields in the UI. It looks a bit weird, but I figured this was clean way of doing it with as little code as possible.
TrackInfo is just an Activity with some fields for the returned data. It implements the TracData's inner interface to receive the data when TrackData has done talking.
How to handle handlers?
One of the first problems I encountered with Android, was that it crashed when I tried to interact with UI components from a thread!
package net.fizzl.example;
import android.os.Handler;
import android.os.Message;
import android.widget.ProgressBar;
public class FooHandler extends Handler {
private ProgressBar pbar;
public FooHandler(ProgressBar pbar) {
this.pbar = pbar;
}
@Override
public void handleMessage(Message msg) {
if(msg.what == MSG_PROGRESS) {
pbar.setProgress(msg.arg1);
}
}
// Message Names
public static final int MSG_PROGRESS = 1;
}
package net.fizzl.example;
import android.os.Handler;
import android.os.Message;
public class FooThread extends Thread {
private Handler h;
public FooThread(Handler h) {
this.h = h;
}
public void run() {
while(dostuff) {
int progress = dostuff();
Message msg = h.obtainMessage(FooHandler.MSG_PROGRESS, progress, 0);
msg.sendToTarget();
}
}
}
4. Profit!
ProgressBar pb = findViewById(R.id.theprogressbar);
FooHandler h = new FooHandler(pb);
FooThread t = new FooThread(h);
t.start();
Magic Player
Just a brief plug before I'll write the actual article.
I have revamped and republished Magic Player.
It is an app which you can use to listen to Jamendo provided music.
I hold a complete index of Jamendo songs on my own server and the application uses my server to get random songs to play.
You may rate the songs. It will save the rating and the time in the song it was rated. Feel free to rate multiple times per song!
Currently the rating data is not used for anything, but I plan to use it in the future to try and select song which are similar to those you have rated highly before. I'll plan to use tags, genres and user cross-comparisons to do this.
So, anyway, give it a try:
Saturday 4 September 2010
File Shredder
Today I'll tell you about "FileShredder", a data wiping tool coded by be.
This is the file system browser. It is very rudimentary and ugly, but it serves its purpose just fine. You can browse the whoe file system and select a file. That's it.
private void ScanDir(File f) {
if(!f.getAbsolutePath().equals("/"))
a.add(f.getParentFile().getAbsolutePath());
if(f.isDirectory()) {
File[] files = f.listFiles(new AllFileFilter());
if(files == null) return;
for(int i=0;i<files.length;i++) {
a.add(files[i].getAbsolutePath());
}
}
}
"a" is an ArrayAdapter which server as the adapter for the listview.
When you click on a file instead of a directory in the previous view, it will launch this activity. It shows information about the file and determines if it can be shred. The application does not not have root permissions to the file system, so there's no way to accidentally the whole system.
Now, here I stumbled on some problems. I never did quite read the Android Activity/Intent lifetime documentation properly. Well, I tried but I never understood why they bothered to explain all the boring details so well with pretty diagrams and all :)
After the file is shredder, the Activitys buttons are modified acordingly and you may now close the activity. You could also just use the Back button of your device, but I added the button for clarity. I actually might remove it in a later version, because it does not sit well with the Android UI style.
Here's a QR tag which you can read with an Android barcode reader. I'm not sure if it works. It would be great if you could report in the comments whether it works for you!