Flex、AIR、Java、Androidなど

およそ3ヶ月ぶりの新アプリリリース。

今回はAndroid Application Award (A3,エーキューブ) 2010 Springに応募するために作っていたアプリを一般向けにもリリースする形です。

残念ながら現在はレイアウトを一部決めうちしていますのでXperiaでしか動かないと思うのですが(N1、Desire等でも画面が切れるとは思いますが大丈夫かもしれません)、需要があるようでしたらHT-03A等にも対応させます。

A3開催に当たってとりあえず何かは出そうと思っていたのですがもうAndroidアプリも相当数世に出ており、全く誰もやっていないことを探すのは大変でした。アイディアがかぶってしまうと私のように技術力で勝負できない人間は敵いませんので色々考えていたのですが、ターゲットを変えてみようということで膨らませ、ターゲットを子供に絞りました。

そうこうしているうちに親戚の子供がトミカが好きなのをちらっと聞いて、そこから膨らませて車の画像から車種を当てるクイズを思いつきました。

その子はまだ3歳なのですがトミカを並べてこれはハマー、これはランボルギーニ、とか言っていますので3歳〜小学校低学年の子くらいが一番やっていて楽しいかもしれませんね。

その位の子供がこのアプリを通じて文字・アルファベットを学習し、車への理解を深めてくれるようなきっかけになれればうれしいです。また、当然シンプルに中古車を検索する機能も付いておりますので大人の方でも普通にご利用頂けます。若い頃に乗っておられた車の話とか、楽しいですよね。

私も若い時は車が好きだったので車種の3択を作るときに役立ちました(笑)

以下、機能を箇条書きしておきます。大きく分けて4つのセクションがありますのでそれぞれ記載しておきます。


■ 中古車検索
・県別、車種別、ブランド別での中古車の検索
・検索結果から公式の見積もり・詳細ページへのリンク


■ 車種当てクイズ
・国産車をランダムに検索し、外観が似ている車種名3択の中から正解を選ぶクイズ
・問題となる車種の画像が最大5枚表示されるので、その写真をもとに車種名を解答します。
・問題となる車種はランダムに並べ替えされるので同じ問題が再度でることは少ないです。
・その為、問題のバリエーションが豊富で飽きが来ませんし、同じ車種でも年式の違い等で問題のバリエーションは豊富です。
・問題に解答するごとにポイントがたまります。連続正解するとさらにたくさんのポイントがたまります。

■ クイズの成績記録
・クイズの成績を記録できます。総解答数、誤解答数、最高連続正解数。
・最新の正解率を表示し、推移をグラフに表示させることができます。
・たまったポイントを確認できます。100ポイント毎にトミカを一台買ってあげる等すればモチベーションを持続させてくれるのではないでしょうか?
・間違えた問題を記録し、それをもとにWikipediaで検索したり、中古車を検索することができます。


■ トミカ検索
・楽天市場から車種名でのトミカの検索
・購入ページへのリンク

例によって今後もご意見等頂ければ色々な機能を加えていきたいと思っていますのでお気軽にご連絡下さい。ブログよりTwitterの方がリプライは早いと思います(^^) Twitterは右サイドバーに表示されていますので。

インストールはAndroid Marketからお願いします。

Androlib:中古車検索+車種当てクイズ v1.0 Application for Android | Entertainment
Cyrket:中古車検索+車種当てクイズ – Cyrket

何時間か悩んでできなかったのでTwitterでつぶやいてみたところ @esmasui さんに教えてもらいながらできるようになりました。

せっかくのなのでこちらにプロジェクトごと公開しておきます。

onCreateの中でbindServiceした後すぐにAidlに記述した関数を呼んでいたのですがこれがいけなかったようです。ServiceConnectionのonServiceConnected(ComponentName name, IBinder service)の中でIServiceConnection.Stub.asInterface(service);した後に呼んでやると無事呼べました。

このタイミングが未だによく分かってないのですがとりあえずできるようになってよかったです。しかしServiceは奥が深い。

@esmasui さんありがとうございました!

Tags: ,

先日解答したクイズの採点が行われ、無事に参加資格を得られたようなので京都まで日帰りで行ってきます。

Google Discussion Groupの得点が0だった方がいらっしゃるようですが私は普通に登録し、そのアドレスを解答欄に入力したような記憶があります。Androidアプリケーションは6.5万DLで☆4つのAutomatic Task Killerを入力したら6点もらえました。実績系の問題は解答によって差をつけているのですね。

何かそこまでする理由でもあるのでしょうかね?まあ当日を待ちます。

普段Twitterで交流のある方々も無事パスされたようなので会場で会えるのが楽しみです(^^)

暗号化はwgetで、パッチワークと漢字変換サーバーはJavaで書きました。

http://twitter.com/stachibana/status/9465013343

私は本やリファレンスを読まずに書いてしまう人なので真似しない方がいいと思いますよ。

他にも公開されている方がいらっしゃるようなので拝見させてもらって勉強します。

まずパッチワーク。各マスから同じ文字が隣にあるマスを再帰的になめてIDを振っていき、最後に各行を数えてます。何の工夫もない、1.8秒くらいかかるコードです。

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

public class Main {
	static ArrayList<String> source = new ArrayList<String>();
	static HashMap<String, String> doneHash = new HashMap<String, String>();
	static int x = 0;
	static int y = 0;
	static int cur = 0;
	public static void main(String[] args) {
		long start = System.currentTimeMillis();
		try{
			BufferedReader br = new BufferedReader(new FileReader(args[0]));
			String line;
			while ((line = br.readLine()) != null){
				source.add(line);
			}
		} catch (Exception e) {
			System.out.println("Invalid Parameter");
		}
		for(int y = 0; y < source.size(); y++){
			String str = source.get(y);
			for(int x = 0; x < str.length(); x++)
			{
				String _cur = "" + cur;
				checkRecursively(str.substring(x, x+1), y, x, _cur);
				cur++;
			}
		}
		HashMap<String, Integer> countMap = new HashMap<String, Integer>();
		ArrayList<HashMap<String, Integer>> countArray = new ArrayList<HashMap<String,Integer>>();
		int column = source.get(0).length();
		for(int i = 0; i < source.size(); i++){
			String str = "";
			for(int j = 0; j < column; j++){
				if(doneHash.get("" + i + ":"+ j) != null){
					str = doneHash.get("" + i + ":"+ j);
					if(countMap.get(str) == null){
						countMap.put(str, 1);
					}else{
						countMap.put(str, countMap.get(str)+1);
					}
				}
			}
		}
		Set set = countMap.keySet();
		Iterator it = set.iterator();
		String keyStr = "";
		while(it.hasNext()){
			HashMap<String, Integer> h = new HashMap<String, Integer>();
			keyStr = (String)it.next();
			h.put("id", Integer.parseInt(keyStr));
			h.put("count", countMap.get(keyStr));
			countArray.add(h);
		}
		Comparator<HashMap<String, Integer>> compToUse = new Comparator<HashMap<String, Integer>>(){
			public int compare(HashMap<String, Integer> object1, HashMap<String, Integer> object2) {
						Integer t1int = object1.get("count");
						Integer t2int = object2.get("count");
						return t2int.compareTo(t1int);
			}
		};
		Collections.sort(countArray, compToUse);
		String maxId = "" + countArray.get(0).get("id");
		String answer = "";
		for(int i = 0; i < source.size(); i++){
			int count = 0;
			for(int j = 0; j < column; j++){
				if(doneHash.get("" + i + ":"+ j) != null){
					if(doneHash.get("" + i + ":"+ j).equals(maxId)){
						count++;
					}
				}
			}
			answer += ("" + count + "\n");
		}
		long end = System.currentTimeMillis();
		System.out.println(answer + "\n by " + (end - start) + " miliseconds");
	}

	public synchronized static void checkRecursively(String str, int _y, int _x, String _cur){
		if(doneHash.get("" + _y + ":" + _x) == null){
			doneHash.put("" + _y + ":" + _x, _cur);
			try{
				if(str.equals(source.get(_y).substring(_x-1, _x))){
					checkRecursively(source.get(_y).substring(_x-1, _x), _y, _x-1, _cur);
				}
			} catch(Exception e){}
			try{
				if(str.equals(source.get(_y).substring(_x+1, _x+2))){
					checkRecursively(source.get(_y).substring(_x+1, _x+2), _y, _x+1, _cur);
				}
			} catch(Exception e){}
			try{
				if(str.equals(source.get(_y-1).substring(_x, _x+1))){
					checkRecursively(source.get(_y-1).substring(_x, _x+1), _y-1, _x, _cur);
				}
			} catch(Exception e){}
			try{
				if(str.equals(source.get(_y+1).substring(_x, _x+1))){
					checkRecursively(source.get(_y+1).substring(_x, _x+1), _y+1, _x, _cur);
				}
			} catch(Exception e){}
		}
	}

}

続いて漢字変換サーバー。DecimalFormat(“####,####”)でフォーマットしたあと各要素を万未満の数値に変換、その後億や兆をつけて返してます。

package biz.stachibana.quiz;
import java.text.DecimalFormat;

public class NumToKanji {

	static char[] c = "TEHMPKRNDQ".toCharArray();
	static char[] lc = "ABUFXZ".toCharArray();

	public static String getResult(long i){
		String result = "";
		DecimalFormat numFormat = new DecimalFormat("####,####");
		String str = numFormat.format(i);

		String[] al = str.split(",");

		if(al.length == 1){
			result = makeFirst(al[0]);
		}
		else if(al.length == 2){
			result += (makeFirst(al[0]) + Character.toString(lc[3]) + makeFirst(al[1]));
		}
		else if(al.length == 3){
			if(!makeFirst(al[2]).equals("")){
				result = makeFirst(al[2]);
			}
			if(!makeFirst(al[1]).equals("")){
				result = (makeFirst(al[1]) + Character.toString(lc[3]) + result);
			}
			result = (makeFirst(al[0]) + Character.toString(lc[4]) + result);
		}
		else if(al.length == 4){
			if(!makeFirst(al[3]).equals("")){
				result = makeFirst(al[3]);
			}
			if(!makeFirst(al[2]).equals("")){
				result = (makeFirst(al[2]) + Character.toString(lc[3]) + result);
			}
			if(!makeFirst(al[1]).equals("")){
				result = (makeFirst(al[1]) + Character.toString(lc[4]) + result);
			}
			result = (makeFirst(al[0]) + Character.toString(lc[5]) + result);
		}
		else{
			return "parameter must be less than 9,999,999,999,999,999";
		}
		return result;
	}

	public static String makeFirst(String str){
		String result = "";

		if(str.length() == 4){
			if(str.substring(0, 1).equals("0")){}
			else if(str.substring(0, 1).equals("1")){
				result += Character.toString(lc[2]);
			}
			else{
				result += (Character.toString(c[Integer.parseInt(str.substring(0, 1))]) + Character.toString(lc[2]));
			}
			if(str.substring(1, 2).equals("0")){}
			else if(str.substring(1, 2).equals("1")){
				result += Character.toString(lc[1]);
			}
			else{
				result += (Character.toString(c[Integer.parseInt(str.substring(1, 2))]) + Character.toString(lc[1]));
			}
			if(str.substring(2, 3).equals("0")){}
			else if(str.substring(2, 3).equals("1")){
				result += Character.toString(lc[0]);
			}
			else{
				result += (Character.toString(c[Integer.parseInt(str.substring(2, 3))]) + Character.toString(lc[0]));
			}
			if(!str.substring(3, 4).equals("0")) result += Character.toString(c[Integer.parseInt(str.substring(3, 4))]);
		}
		else if(str.length() == 3){
			if(str.substring(0, 1).equals("0")){}
			else if(str.substring(0, 1).equals("1")){
				result += Character.toString(lc[1]);
			}
			else{
				result += (Character.toString(c[Integer.parseInt(str.substring(0, 1))]) + Character.toString(lc[1]));
			}
			if(str.substring(1, 2).equals("0")){}
			else if(str.substring(1, 2).equals("1")){
				result += Character.toString(lc[0]);
			}
			else{
				result += (Character.toString(c[Integer.parseInt(str.substring(1, 2))]) + Character.toString(lc[0]));
			}
			if(!str.substring(2, 3).equals("0")) result += Character.toString(c[Integer.parseInt(str.substring(2, 3))]);
		}
		else if(str.length() == 2){
			if(str.substring(0, 1).equals("0")){}
			else if(str.substring(0, 1).equals("1")){
				result += Character.toString(lc[0]);
			}
			else{
				result += (Character.toString(c[Integer.parseInt(str.substring(0, 1))]) + Character.toString(lc[0]));
			}
			if(!str.substring(1, 2).equals("0")) result += Character.toString(c[Integer.parseInt(str.substring(1, 2))]);
		}
		else{
			result += Character.toString(c[Integer.parseInt(str.substring(0, 1))]);
		}

		return result;
	}
}

コード書く系の3問は正解しましたが、ハッカソン、Issue Tracker、Chrome Extensionはだめ。AndroidはAutomatic Task Killer出しておきました。

e58699e79c9f-2

2/13(土)、Shikoku GTUG #4 in Kagawaで講師をしてきました。

講師というとおおげさですかね。まあ資料を作って発表するような会です。

プレゼンは大学時代はよくやってましたが仕事でやることはめったになく、どちらかというと敬遠しがちだったのですが、今年のテーマは「刺激」に決めているので思い切って参加してきました。

運営、スタッフの皆様、いい機会を与えて頂きありがとうございました。お疲れ様でした。

特に、依頼を下さった@T_1000_Freeさん(→ブログ)には感謝しています。機会を与えて下さった上にサンポートで迷っているところを救っていただきました。

また、前回の広島でもお世話になった@LuckOfWiseさん(→ブログ)、@Toro_kunさん(→ブログ)とは懇親会で熱いお話が出来て楽しかったです。

全員の方の連絡先が分かりませんし、勝手に紹介すると問題のある方もいらっしゃるようなのでこのような形で失礼致します。

やはり聞き手の方が熱心に聴いてくださっているのが伝わると話している方も楽しく、テンションが上がってしまうものですね。時間を少しオーバーしてしまいました。ご迷惑をおかけし申し訳なく思っています。

しかし、発表の後何人かの方に質問を頂いたり、その中でも私の発表を聞いて開発を始めた方もいらっしゃるようで(!!)、発表した方としてはこんなに嬉しいことはありません。

全てというわけにはいかないと思いますが、今後もできるだけ参加していきたいと思っていますのでもしまたお会いできることがありましたらよろしくお願いいたします。

懇親会に参加された方も多かったので全ての方とお話できたわけではありませんが、有意義な時間をすごせました!

スライドも公開する予定なので見てもらえたらうれしいです!

A3に出すアプリの構想はもうできているのでぼちぼちと、更に何か思いついたら作ろうと思っています。

About

Author: tachibana

  • ちょっとしたことはTwitterに書いています。こっちはアプリの公開等の時に更新されます。
  • 最近はもっぱらJavaとObjective Cです。AS3は飽きました。
  • スクリプト言語ではPerlが好きでしたが最近はGAE/Jで何でもやってます。
  • Linuxは自宅サーバー建てるのがやっとのレベルです。前の会社で何日も徹夜してやったのはいい思い出です。
  • アプリへのご要望などご意見等ありましたらお気軽にご連絡下さい。

Twitter

  • @Toro_kun まあ仕事で来られるんやったらたいがいそうですわなー。わたしゃ街中から三分くらいのとこに住んどんで松山来られた時はいつでも誘うてくださいね〜。 in reply to Toro_kun 1 week ago
  • @Toro_kun ほんまですかぁー。ほなまた次の機会にでもー。楽しんでって下さいね! in reply to Toro_kun 1 week ago
  • iPadは相方のおもちゃとして絶賛稼働中。あんまゲームしない人なんだけどHarbor MasterとFlight Controlに嵌ってずーーーっとやってらw 1 week ago
  • @Toro_kun おっ、時間あるんでしたらどっか行きますか??w in reply to Toro_kun 1 week ago
  • 昨日のiPhoneアプリのDL数は約200。どこで宣伝してるわけでもないんで、やっぱ新着効果じゃなくて検索で引っかかるんやろなぁ。いつまで続くやら。 1 week ago
  • Follow me on Twitter

Alternative content here