カテゴリー: Android

Android:デバイスIDの取得

取り敢えずnexus9で起動したアプリを見てあることに気づいた。というか思い出した。
確かLolipopだとdeviceIdをLogCatから見つけられないから、testDviceに追加する
方法がわからなかったんだった。まあどうせ1000回表示したとしても1円だし、年に一回、
最低振込金額に到達すれば良い方なので余り気にしなくてもいいような気がしないでも
ないが、余計な事で怒られるのも嫌なので改めてぐぐる。

android - How can I get device ID for Admobにあった、md5()メソッドを利用する方法で
nexus9のdeviceIdを取得出来たみたいだったので、早速addTestDevice()にidを追加する。
テスト用のバナーになった。ひゃっほいヽ( ・∀・)ノ ←歓喜する俺氏。

返す刀で、

String deviceId = md5(android_id).toUpperCase();
        mAdRequest.addTestDevice(deviceId);

みたいに前もって取得したidをaddTestDevice()するのではなく動的にdeviceIdを
取得してaddTestDevice()してみる。テスト用のバナーになった。
ひゅーひゅーだよ ひゅーひゅーかきーん(・∀・)
↑歓喜の余りよくわからないことを言い出す俺氏。年齢がかなりいってることが分かる。

sol25で試す。
 
 
 
 
 
あれ(´・ω・`)?
テスト用バナーが表示されず、実際の広告が…
キャッシュとかされてるのだろうかと何度か試したが、事態は変わらず。
nexus9で再び試すとテスト用バナーが(・∀・)
sol25で再び試すと実際の広告が(´・ω・`)

ここで4.0.xを載せているnovo7でも試す。
 
 
 
 
 
やっぱり実際の広告が…_| ̄|○

もしかしてサンプルコードのmd5算出部分が間違えてるんじゃないの(・∀・)?と
失礼な疑念を抱き、色々とぐぐる。いくつかのサイトを眺めてみたが
 
 
 
 
 
よくわからない(・∀・)


仕方がないので取り敢えず、算出されたdeviceIdをコピペしてみる。

84D38FF70AB5889E8FB12E44734AFE08

特におかしなところも見当たらない

LogCatで取得済みのidは

84D38FF7AB5889E8FB12E44734AFE8

なので同一か…
 
 
 
 
 
いや、ちょっと待て(;・´ω・`)

84D38FF70AB5889E8FB12E44734AFE08
84D38FF7AB5889E8FB12E44734AFE8

並べてみて気づいたが、ちょっと長いぞ。なんか0が2つ多い。
なんかの法則で0が2つついておかしくなるに違いない(`・ω・´)
↑実はこの時、大きい勘違いをしてる俺氏。0付加されているのが正しい値で、
LogCatから取得した値。無い方がmd5()メソッドで取得した値。
色々調べてるうちに2つの値を取り違えたみたい(ノ∀`)

名探偵俺氏、その確証を得る為にnovo7のdeviceIdを取得して比較。

2F5820181984FD634C5344329B170C1
2f58201819840fd634c5344329b170c1

あれ、0が一つ多いだけだね(´・ω・`)


まあ何にしてもおかしいのでぐぐり再開。
取り敢えずmd5変換に種類があるのだろうかとぐぐったがそんなことはない模様。
php等でmd5変換をしてくれるページなどで試してみたが、md5()で算出される値と同じ。

Android4系以前とlolipop以降の差なんだろうかとも考えたがlolipop搭載端末は一台しか
持っていないのでその説を検証するすべがない。うーんと思いながら値を眺めたり、
md5変換のコードが載っているページを読んでいて、ふとある部分に目が行く。

//MD5 ハッシュ関数  
    private static String hashByte2MD5(byte []hash) {  
        StringBuffer hexString = new StringBuffer();  
        for (int i = 0; i < hash.length; i++) {  
            if ((0xff & hash[i]) < 0x10) {  
                hexString.append("0" + Integer.toHexString((0xFF & hash[i])));  
            } else {  
                hexString.append(Integer.toHexString(0xFF & hash[i]));  
            }  
        }  
          
        return hexString.toString();  
    }  

 if ((0xff & hash[i]) < 0x10) {  
                hexString.append("0" + Integer.toHexString((0xFF & hash[i])));  

の部分だ。

そう言えば余分な文字は”0”だヽ(`Д´)ノ!
つまり10以下の値の時は結果が二桁になるように”0”を付加してるんじゃないか( ・´ω・`)?
↑”0x”がついてるので正確には15以下です。しかもそう思うにしても10じゃなくて9以下です。

よっしゃと思いmessageDigestの値を見る。
-124, -45, -113, -9, 10, -75, -120, -98, -113, -79, 46, 68, 115, 74, -2, 8

ビンゴや(・∀・)


勢いに乗ってnovo7のmessageDigestの値を見る。
47, 88, 32, 24, 25, -124, 15, -42, 52, -59, 52, 67, 41, -79, 112, -63

あるぇー(・3・)? 10以下がないお(´・ω・`)

予想が外れたことに意気消沈するも、

0が付加されるのは”15”…
元の値は”f”……
判定式は”0x10”………
 
 
 
16進数やないかΣ(゚∀゚;)

とようやく自分の初歩的な勘違いに気づいた(ノ∀`)

何はともあれAndroidのdeviceIdはF以下の値には0付加しないという規則があるのか
という結論に到達したが、これが勘違いによる正反対の結論であることに気がつくのは数時間後のお話…(ヽ'ω`)


取り敢えず、カッコ悪いがmd5()メソッドを

String tempString = "";
for (int i=0; i<messageDigest.length; i++){
     tempString = Integer.toHexString(0xFF & messageDigest[i]);

     if(tempString.length()<2){
        hexString.append("0" + tempString);
     }else{
        hexString.append(tempString);
     }
}

みたいに改変。StringBufferを使ってるコードでStringを使う俺氏、鬼畜(・∀・)
とか思って参考にしたコードの上の回答を見てみたら

String h = Integer.toHexString(0xFF & messageDigest[i]);
  while (h.length() < 2)
    h = "0" + h;
hexString.append(h);

みたいな感じで0付加するコードが載ってた…_| ̄|○
まあでも最初の段階でこのコードを読んでもわけが分からなかっただろうし、
同じようなコーディングだからいいか(・∀・)

AdRequestの方もちょっと修正。直にaddTestDevice()の中でgetDeviceId()を
呼んでもいいけど今の所はLogに書き込みたいのでこれでいい。

String tempString = getDeviceId();
Log.d(TAG, tempString);
AdRequest adRequest = new AdRequest.Builder()
.addTestDevice(AdRequest.DEVICE_ID_EMULATOR)       //エミュレータ
.addTestDevice(tempString)
.build();
adView.loadAd(adRequest);
private String getDeviceId(){
  String android_id = Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID);
  String deviceId = md5(android_id).toUpperCase(Locale.ENGLISH);

  return deviceId;
}

取り敢えずはこれでsol25とnexus9でテスト用バナーを確認。

だがしかしnovo7では表示されず(´・ω・`)
よくよく考えみたら、novo7上でこのコードではテスト用バナーも実際の広告も
表示されているところを見たことがなかったので他の問題か。でもゲームベースでは
表示されたしなと思って文字列で静的指定をするとテスト用バナーが表示された…
わけがわからないのでもう一度デバッグで動的指定を試すとテスト用バナーが表示された…
本当にわけがわからない…(ヽ'ω`)

まあ一応動的指定は出来るようになったようだ…


で、まあいつものパターンで日本語でぐぐったら、日本語の答えがあったりする…(ヽ'ω`)
AdMob テストで使う、デバイスIDを取得

ここのページのリンクを辿ってJavaDocを読んだけど、ANDROID_IDって

A 64-bit number (as a hex string) that is randomly generated when the user first sets up the device and should remain constant for the lifetime of the user's device. The value may change if a factory reset is performed on the device.

Note: When a device has multiple users (available on certain devices running Android 4.2 or higher), each user appears as a completely separate device, so the ANDROID_ID value is unique to each user.

Constant Value: "android_id"
Settings.Secure | Android Developers

デバイスをファクトリーリセットすると変わる可能性があるんだねぇ。
加えて複数ユーザー機能を使ってる場合、ユーザー毎にANDROID_IDは一意的なんだねぇ。
ANDROID_IDから取得出来るStringの内容を見てないから断言出来ないけど、複数
ユーザー機能を使用しているデバイスからは複数のデバイスIDが取得出来るのかな?
それともANDROID_IDから取得出来るStringは同一なんだろうか。謎だ…(ヽ'ω`)

IllegalArgumentException: Service Intent must be explicit

nexus9(Lolipop)の方での動作もたまには見てみるかと思って実行したら、

java.lang.IllegalArgumentException: Service Intent must be explicit

みたいなエラーで速攻で落ちたΣ(゚∀゚;)

コードの方をチェックしたら、エラーになってるのはインテント絡みらしい。
こんな感じのインテントを飛ばしてる箇所。

OtherService.java
public static final String ACTION_STATE_CHANGED = "com.example.activityloopertest.ACTION_STATE_CHANGED";

OtherClass.java
startService(new Intent(OtherService.ACTION_STATE_CHANGED));

Android4系では動くのにLolipopでは落ちる(´・ω・`)
よくわからないのでぐぐる。
Google In-App billing, IllegalArgumentException: Service Intent must be explicit, after upgrading to Android L Dev Preview

ちゃんと読んでないのでよくわからない(ノ∀`)
Lolipopからexplicit intentじゃないと駄目みたいだが、そもそもexplicitだし…
取り敢えず途中にあったコードを参考に全ての上記のようなインテントの箇所を
修正してみた。

tempIntent = new Intent(MoleMusicPlayerService.ACTION_STATE_CHANGED);
tempIntent.setPackage(MainActivity.PACKAGENAME);
startService(tempIntent);

一応これで落ちなくはなった。setPackage()すれば良いと言うことなのかな?
よく分からない…(ヽ'ω`)

xmlファイルを使わずにFragmentを生成する方法(´・ω・`)?

xmlを使わずにFragmentを生成する方法をぐぐっていて、ヒットした
android - How to programmatically create the layout for a Fragment?
android - How do I add a Fragment to an Activity with a programmatically created content view
を参考にコードをでっち上げたが、果たしてこれで正しいのかどうかよくわからないw

そもそもなんでそんなことをしようしたのかというと、前のエントリを確認している時に
LongClickを拾えなくなった時に「前に試したコードはListFragmentだったから、それと
一緒にしないと再現できないのか…( ;・´ω・`)ゴクリッ?」という勘違いをして、かつ、
「サンプルコードはワンストップならぬワンコピペで完結すべき(`・ω・´)」という
謎の思想から、xmlファイルによるレイアウト指定を無しにしたいと思ったから(ノ∀`)

一応これで出来てるみたいだけれども、実際にプログラムの一部として使うには
もっと何かをしないといけないのかもしれないが、その辺はよくわからないw

取り敢えずワンコピペで試せるFragmentを使用するサンプルを作るのには
利用出来るので、これはこれでいいかな。

なぜかLongClickでToastが表示出来ないのは謎(´・ω・`)
Log.dには出力されているのに。まあタッチイベント関連はこのサンプルの
本質とは全く関係ないのだけれどw

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends Activity {

	private GestureDetector mGestureDetector;
	private View.OnTouchListener mGestureListener;
	private static final int CONTENT_VIEW_ID = 10101010;
	private static final String[] CONFECTIONERY = {
		"Apple Pie", "Banana Bread", "Cupcake", "Donut", "Eclair", "Froyo",
		"Gingerbread", "Honeycomb", "Ice Cream Sandwich", "Jelly Bean",
		"KitKat", "Lollipop"
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		LinearLayout layout=new LinearLayout(this);
		layout.setBackgroundColor(Color.WHITE);
		layout.setOrientation(LinearLayout.VERTICAL);
		layout.setId(CONTENT_VIEW_ID);

		setContentView(layout, new LayoutParams(
				LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

		Bundle bundle = new Bundle();
		bundle.putStringArray("confectionery", CONFECTIONERY);

		if (savedInstanceState == null) {
			Fragment newFragment = new MyFragment();
			FragmentTransaction ft = getFragmentManager().beginTransaction();
			ft.add(CONTENT_VIEW_ID, newFragment).commit();
		}
	}

	private void myOnItemClick(int position) {
		Log.d("myOnItemClick", "itemClick: " + position);
		Toast.makeText(MainActivity.this, "myOnItemClick itemClick" + position, Toast.LENGTH_LONG).show();
	}

	public class MyFragment extends Fragment {

		public MyFragment() {}

		View rootView;
		ListView listView;
		@Override
		public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
			LinearLayout layout=new LinearLayout(getActivity());
			Log.d("MyFragment", "onCreateView");
			layout.setOrientation(LinearLayout.VERTICAL);

			listView = new ListView(getActivity());

			listView.setLayoutParams(new LinearLayout.LayoutParams(
					LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
			listView.setAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1,
					CONFECTIONERY));

			layout.addView(listView);

			rootView = layout;
			rootView.setBackgroundColor(Color.WHITE);
			return rootView;
		}

		@Override
		public void onActivityCreated(Bundle savedInstanceState) {
			super.onActivityCreated(savedInstanceState);

			mGestureDetector = new GestureDetector(getActivity(),new MyGestureDetector());
			mGestureListener = new View.OnTouchListener() {
				@Override
				public boolean onTouch(View v, MotionEvent event) {
					return mGestureDetector.onTouchEvent(event);
				}
			};

			listView.setOnTouchListener(mGestureListener);
			listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
				@Override
				public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
					Log.d("onItemLongClick", "LongClick");
					Toast.makeText(MainActivity.this, "onItemLongClick LongClick", Toast.LENGTH_LONG).show();
					return true;
				}
			});
		}

		class MyGestureDetector extends SimpleOnGestureListener {
			private static final int SWIPE_MIN_DISTANCE = 120;
			private static final int SWIPE_MAX_OFF_PATH = 250;
			private static final int SWIPE_THRESHOLD_VELOCITY = 200;

			@Override
			public boolean onSingleTapUp(MotionEvent e) {
				int pos = listView.pointToPosition((int)e.getX(), (int)e.getY());
				myOnItemClick(pos);
				return false;
			}

			@Override
			public boolean onFling(MotionEvent e1, MotionEvent e2,
					float velocityX, float velocityY) {
				if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) {
					return false;
				}

				boolean isDoCancel = false;

				if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
					Log.d("onFling", "FlingLeft");
					Toast.makeText(MainActivity.this, "onFling FlingLeft", Toast.LENGTH_LONG).show();

					isDoCancel = true;

				} else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
					Log.d("onFling", "FlingRight");
					Toast.makeText(MainActivity.this, "onFling FlingRight", Toast.LENGTH_LONG).show();

					isDoCancel = true;
				}

				if(isDoCancel){
					MotionEvent cancelEvent = MotionEvent.obtain(e2);
					cancelEvent.setAction(MotionEvent.ACTION_UP);
					listView.onTouchEvent(cancelEvent);
					return true;
				}else{
					return false;
				}

			}
		}
	}
}

Java.lang.NoSuchMethodError: android.graphics.Typeface_Delegate.getSystemFontConfigLocation()Ljava/io/File;

レイアウトXMLがグラフィカルレイアウトで表示されないヽ(`Д´)ノ
レンダリング中にエラーがどうとか
renderingError01

ぐぐる。なんかヒットした。
Issue 77726 - android - Eclipse + Android 5.0 SDK = parseSdkContent failed - Android Open Source Project - Issue Tracker

中身をほとんどよく読んでないのでよくわからないがfixされている問題らしいので
これとは関係ないか…(´・ω・`) APIレベルを19にするとか21にするとか書いてあるような。

取り敢えずマニフェストファイルのandroid:targetSdkVersionが21だったので
適当に20にしてみる。
 
 
 
 
 
何も変わらない(´・ω・`)


再び画面を眺めていて左上に22という数字が表示されているのに気づく。
何となく20にしてみる。
renderingError02
 
 
 
 
 
グラフィカルレイアウトが表示された(・∀・)
renderingError03


まあ本質的な解決方法ではないのだろうけれどもw
取り敢えずのところはこれでいいやw


2015/05/11追記

解決したと思ったら再発…(ヽ'ω`)

再びぐぐる。
ヒットしたandroid - XML Error in Eclipse while viewing Graphical Layout of Xml
内容ではプロジェクトのAndroid SDKバージョンと左上のバージョンを合わせると直るとある。

合わせて再起動する。
 
 
 
 
直らない_| ̄|○

この件とは関係無かったけれども、そう言えばなぜAPIレベルが20の次が22なのかと
疑問に思い、SDKマネージャーを起ち上げたら21はインストールされていなかった。
一瞬これが原因かと思ったが、これは21(5.0.x)でセキュリティかなんかのバグがあって
早々に22(5.1.x)にアップデートされたかなんかしたからだったかな?

エラーメッセージだけだと特にヒットせず、検索ワードをどんどん削っていったが
どれも関係のない話ばかり…(ヽ'ω`) モウアカン…

諦めかけた時にEclipseのグラフィカル・レイアウトエラーがヒット
そうか、eclipseを-cleanオプションを付けて起動すれば良かったんかヽ(`Д´)ノ

なんで俺氏はプロジェクトのクリーンを試してたんだろうか…eclipseのUIだからeclipse自体をcleanオプションで起ち上げないと意味ないよな…(ヽ'ω`)

まあ何はともあれ、これで完全解決(`・ω・´) だといいなぁ(´・ω・`)