ログイン

やっとドコモのAndroid4.0端末が発売になりました。で、早速入手してきました。
で、早速実機検証をと思ったのですが、なかなかUSBドライバーが見つからなくて苦労しました。
サムスンのサイトにもまだ出ていないみたいですが、下記のサイトで見つけました。
http://androidforums.com/galaxy-nexus-all-things-root/451898-galaxy-nexus-adb-usb-driver.html
落としてきてインストールしてみると、無事デバッグできました。
きっと同じ苦労をする人がいると思うので載せておこうと思います。

例えばWebでの背景画像のように、画像を繰り返しで配置することで表現するような背景を使いたい場合に、bitmapとしてdrawableを作ると実現できる。
こんな感じ

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
	android:tileMode="repeat"
	android:src="@drawable/some_drawable" />

ところが、このリソースをリストビューなどの各アイテムの背景に指定したい場合に、レイアウトのXMLの中で、android:background=”"として指定した場合には期待通りの表示になるのですが、adapter#getView()の中で動的に設定した場合に、繰り返しにならずに、1つの画像(ここではsome_drawable)がViewの大きさいっぱいに引き伸ばされたようになる場合があるようです。
そのViewの高さが確定できていないせいなのか?
また、一度上記のような引き伸ばされた状態の表示がおきると、その後、別のActivityで本来正常に表示されていたケースでも、(同じdrawableが背景に指定されている場合)、リストビューのケースと同様に引き伸ばされた表示になってしまうようです。
drawableのリソースってキャッシュされているのかな、、、

もう少し調査してみたいです、、、

24
11月

Data exceeds UNCOMPRESS_DATA_MAX

Written by araki. No comments Posted in: Android, 開発備忘録
Tagged with

AssetManager#open() でasset内のファイルを開く場合、1.6だと1Mb以上のファイルを開こうとすると

Data exceeds UNCOMPRESS_DATA_MAX 1234567 vs 10488567

見たいなエラーが出て開けなくて、IO Exception が発生するみたいです。
2.2あたりでは起きないようなので、テストの際に注意が必要かもです。

先日苦労した点があったので覚え書を。
ManifestのActivity要素内で、intent-filterを定義する際に、data 要素のandroid:host, android:name, android:path, などを@string/xxxx という感じで参照で書いていたところ、そのアクションが呼ばれた時に一部端末(IS03)で、acitivity not found Exceptionが発生した。
っていうか、Activityがあったから受け取ったのでは?と思うのですが、、、
で、参照をやめて直接書くようにしたら正常に動作した。
正確な原因は不明ですがどうもIS03では参照を正しく解決できない模様です。

08
4月

SoftInputの処理

Written by araki. No comments Posted in: Android, 開発備忘録

前回、Androidのキーボードのモードオプション、でキーボードのモードについて書きましたが、このエンターボタンが押された時の処理で嵌ったことがあったので覚書を。
キーボードのエンターボタンが押された時の処理を、EditText.OnEditorAction 等で拾う場合、実際の必要な処理を行う前に、

InputMethodManager#hideSoftInputFromWindw();

を呼んで明示的にキーボードを非表示にしておかないと、例えば、そこから遷移した次の画面で、Viewが更新されないとか、dialog.show(); したのにダイアログが画面に表示されない、などの問題が起きるよようです。
どうやら、明示的にキーボードを消してやらないと、Activityのレイアウトがフォーカスを持っていないようだ。(次の画面に遷移すればもちろんキーボードはすでに消えているのだけど、内部的にはまだキーボードにフォーカスがあるのか????)

詳細未確認なので、認識違いだったらつっこみ入れてください。

AndroidのsoftInput(画面上に出るキーボード)は、いくつかのモードがある。
例えば右下のEnterボタンに当たるキーが、「次へ」、「実行」、や検索を意味する虫眼鏡になっている、など。
この指定については意外と情報が少なかったので覚書をしておこうと思う。
softInputを呼び出す元のEditTextのLayoutで、android:imeOptions=”actionSearch” などのようにしてモードを指定できる。

<EditText android:id="@+id/edit_test" android:imeOptions="actionSearch"/>

指定できる出来るオプションには、

等がある。

androidで複数のActivityで別々のMapViewを持っている場合に、戻るボタンで戻ってきた時に、前の画面でのMapViewのレイアウトが引き継がれてしまう事があって嵌ったので覚書。

元々adroidでは1つのアプリの中で使用できるMapViewは1つだけらしく、別のActivityでもMapViewのインスタンスは使いまわされているみたい。(詳細未確認)

解決策としては、戻ったActivityのonResume 内で、MapView#requestLayout() を呼んでやると直る。
こんな感じ。

public void onResume(){
  super.onResume();
  MapView mapView = (MapView) findViewById(R.id.MapView);
  mapView.requestLayout();
}

普通によくありそうな事なのに、なかなか情報が見つからなかった、、、。

androidの開発でlocalhostへの接続ではまったので覚書を。
androidから外部APIなどへの接続って言うのは良くあると思いますが、開発時にローカルに開発用サーバーを準備してそこへ接続って言うのは良くやると思います。
ここで、Webアプリの開発をやっている人は(僕もそうですが)うっかり、接続先のホスト名をlocalhost 又は 127.0.0.1 としてしまいそうですが、これだと接続でしません。
connection refused とか言われてしまいます。
AVDから見たlocalhostはAVD自身になるそうです。
その代わりにローカルPCには、10.0.2.2 というIPが割り当てられているようです。
なので、これをURLとして指定してあげると接続できます。
考えてみれば当然のことのようですが、やりががちな間違いです。

こちらの
http://blog.syuhari.jp/archives/1905
記事でimageBehaviorというのを知り、早速使っていました。
findしたときにも、各画像のパス結果レコードに挿入してくれて便利なのですが、どうやらリレーションテーブルのiに関しては処理してくれないようです。
なので、上記ソースに少し手を入れて、belongsToのテーブルに関しても持ってきてくれるように追記してみました。
即席なのでおかしなところがあったらご指摘いただけるとありがたいです。


<?php

class ImageBehavior extends ModelBehavior {

	var $settings = null;

	function setup(&$model, $config = array()) {
		$this->imageSetup($model, $config);
	}

	function imageSetup(&$model, $config = array()) {
		$settings = Set::merge(array(
		'baseDir'=> '',
		), $config);

		if (!isset($settings['fields'])) $settings['fields']=array();
		$fields=array();
		foreach($settings['fields'] as $key=>$value) {
			$field = ife(is_numeric($key), $value, $key);
			$conf = ife(is_numeric($key), array(), ife(is_array($value),$value,array()));
			$conf=Set::merge(
			array (
				'thumbnail' => array('prefix'=>'thumb',
					         'create'=>false,
					         'width'=>'100',
 					         'height'=>'100',
 					         'aspect'=>true,
							 'allow_enlarge'=>true,
		                    ),
				'resize'=>null, // array('width'=>'100','heigth'=>'100'),
				'versions' => array(
				),
			), $conf);
			foreach ($conf['versions'] as $id=>$version) {
				$conf['versions'][$id]=Set::merge(array(
 					         'aspect'=>true,
							 'allow_enlarge'=>false,
		                    ),$version);
			}
			if (is_array($conf['resize'])) {
				if (!isset($conf['resize']['aspect'])) $conf['resize']['aspect']=true;
				if (!isset($conf['resize']['allow_enlarge'])) $conf['resize']['allow_enlarge']=false;
			}
			$fields[$field]=$conf;
		}
		$settings['fields']=$fields;

		$this->settings[$model->name] = $settings;
	}

	/**
	 * Before save method. Called before all saves
	 *
	 * Overriden to transparently manage setting the item position to the end of the list
	 *
	 * @param AppModel $model
	 * @return boolean True to continue, false to abort the save
	 */
	function beforeSave(&$model) {
		extract($this->settings[$model->name]);
		if (empty($model->data[$model->name][$model->primaryKey])) {
		}

		//Ajax でプレアップロードされている場合はそちらを使用する。 modified by M.Araki
		if(isset($model->data[$model->name][TMP_IMG_FILE_NAME])){
			$field = 'image';
			$tempData[$field] = array(
				'tmp_name' => TMP_IMG_DIR_SYSTEM.DS.$model->data[$model->name][TMP_IMG_FILE_NAME],
				'name' => $model->data[$model->name][TMP_IMG_FILE_NAME],
				'type' => $model->data[$model->name][TMP_IMG_FILE_TYPE],
				'error' => '0'
			);
			$model->data[$model->name][$field] = $model->data[$model->name][TMP_IMG_FILE_TYPE];
		}else{
			$tempData = array();
			foreach ($fields as $key=>$value) {
				$field = ife(is_numeric($key), $value, $key);
				if (isset($model->data[$model->name][$field])) {
					if ($this->__isUploadFile($model->data[$model->name][$field])) {
						$tempData[$field] = $model->data[$model->name][$field];
						$model->data[$model->name][$field]=$this->__getContent($model->data[$model->name][$field]);
					} else {
						unset($model->data[$model->name][$field]);
					}
				}
			}
		}

		$this->runtime[$model->name]['beforeSave'] = $tempData;
		return true;
	}

	function afterSave(&$model) {
		extract($this->settings[$model->name]);
		if (empty($model->data[$model->name][$model->primaryKey])) {
		}

		$tempData = $this->runtime[$model->name]['beforeSave'];
		unset($this->runtime[$model->name]['beforeSave']);
		foreach($tempData as $field=>$value) {
			$this->__saveFile($model, $field, $value);
			@unlink($value['tmp_name']);
		}

		return true;
	}

	function afterFind(&$model, &$results, $primary) {
		extract($this->settings[$model->name]);
		$relatedModels = $model->belongsTo;

		if ( is_array( $results ) ) {
			$i=0;
			if (isset($results[0])) {
				while ( isset( $results[$i][$model->name] ) && is_array( $results[$i][$model->name] ) )  {
					foreach ($fields as $field => $fieldParams) {
						if (isset($results[$i][$model->name][$field]) && ($results[$i][$model->name][$field]!='')) {
							$value=$results[$i][$model->name][$field];
							$results[$i][$model->name][$field]=$this->__getParams($model, $field, $value, $fieldParams, $results[$i][$model->name]);
						}
					}
					foreach($relatedModels as $relatedModelName => $param){
						$relatedModel = $model->{$relatedModelName};
						if(!isset($this->settings[$relatedModelName])) continue;
						foreach ($this->settings[$relatedModelName]['fields'] as $field => $fieldParams) {
							if (isset($results[$i][$relatedModelName][$field]) && ($results[$i][$relatedModelName][$field]!='')) {
								$value=$results[$i][$relatedModelName][$field];
								$results[$i][$relatedModelName][$field]=$this->__getParams($relatedModel, $field, $value,$fieldParams, $results[$i][$relatedModelName]);
							}
						}
					}
	                $i++;
				}
			} else {
					foreach ($fields as $field => $fieldParams) {
						if (isset($results[$model->name][$field]) && ($results[$model->name][$field]!='')) {
							$value=$results[$model->name][$field];
							$results[$model->name][$field]=$this->__getParams($model, $field, $value, $fieldParams, $results[$model->name]);
						}
					}
					foreach($relatedModels as $relatedModelName => $param){
						$relatedModel = $model->{$relatedModelName};
						foreach ($this->settings[$relatedModelName]['fields'] as $field => $fieldParams) {
							if (isset($results[$relatedModelName][$field]) && ($results[$relatedModelName][$field]!='')) {
								$value=$results[$relatedModelName][$field];
								$results[$relatedModelName][$field]=$this->__getParams($relatedModel, $field, $value,$fieldParams, $results[$relatedModelName]);
							}
						}
					}

/*				foreach ($fields as $field => $fieldParams) {
					if (isset($results[$model->name][$field]) && ($results[$i][$model->name][$field]!='')) {
						$value=$results[$i][$model->name][$field];
						$results[$model->name][$field]=$this->__getParams($model, $field, $value, $fieldParams, $results[$model->name]);
					}
				}*/
			}
		}
		return $results;
		//return true;
	}

	function __getParams(&$model, $field, $value, $fieldParams, $record) {
		extract($this->settings[$model->name]);
		$result=array();
		if ($value!='') {
			$folderName = $this->__getFolder($model, $record);
			$ext=$this->decodeContent($value);
			$fileName=$field .'.'. $ext;
			$result['path']=$folderName.$fileName;

			$thumb=$fields[$field]['thumbnail'];
			if ($thumb['create']) {
				$result['thumb']=$folderName.$this->__getPrefix($thumb).'_'.$fileName;
			}
			foreach($fields[$field]['versions'] as $version) {
				$result[$this->__getPrefix($version)]=$folderName.$this->__getPrefix($version).'_'.$fileName;
			}
		}
		return $result;
	}

	/**
	 * Before delete method. Called before all deletes
	 *
	 * Will delete the current item from list and update position of all items after one
	 *
	 * @param AppModel $model
	 * @return boolean True to continue, false to abort the delete
	 */
	function beforeDelete(&$model) {
		$this->runtime[$model->name]['ignoreUserAbort'] = ignore_user_abort();
		@ignore_user_abort(true);
		return true;
	}

	function afterDelete(&$model) {
		extract($this->settings[$model->name]);

		foreach ($fields as $field=>$fieldParams) {
			$folderPath=$this->__getFullFolder($model, $field);
			uses ('folder');
			$folder = &new Folder($path = $folderPath, $create = false);
			if ($folder!==false) {
				@$folder->delete($folder->pwd());
			}
		}

		@ignore_user_abort((bool) $this->runtime[$model->name]['ignoreUserAbort']);
		unset($this->runtime[$model->name]['ignoreUserAbort']);
		return true;
	}

	function __isUploadFile($file) {
		if (!isset($file['tmp_name'])) return false;
		return (file_exists($file['tmp_name']) && $file['error']==0);
	}

	function __getContent($file) {
		return $file['type'];
	}
	function decodeContent($content) {
		$contentsMaping=array(
	      "image/gif" => "gif",
	      "image/jpeg" => "jpg",
	      "image/pjpeg" => "jpg",
	      "image/x-png" => "png",
	      "image/jpg" => "jpg",
	      "image/png" => "png",
	      "application/x-shockwave-flash" => "swf",
	      "application/pdf" => "pdf",
	      "application/pgp-signature" => "sig",
	      "application/futuresplash" => "spl",
	      "application/msword" => "doc",
	      "application/postscript" => "ps",
	      "application/x-bittorrent" => "torrent",
	      "application/x-dvi" => "dvi",
	      "application/x-gzip" => "gz",
	      "application/x-ns-proxy-autoconfig" => "pac",
	      "application/x-shockwave-flash" => "swf",
	      "application/x-tgz" => "tar.gz",
	      "application/x-tar" => "tar",
	      "application/zip" => "zip",
	      "audio/mpeg" => "mp3",
	      "audio/x-mpegurl" => "m3u",
	      "audio/x-ms-wma" => "wma",
	      "audio/x-ms-wax" => "wax",
	      "audio/x-wav" => "wav",
	      "image/x-xbitmap" => "xbm",
	      "image/x-xpixmap" => "xpm",
	      "image/x-xwindowdump" => "xwd",
	      "text/css" => "css",
	      "text/html" => "html",
	      "text/javascript" => "js",
	      "text/plain" => "txt",
	      "text/xml" => "xml",
	      "video/mpeg" => "mpeg",
	      "video/quicktime" => "mov",
	      "video/x-msvideo" => "avi",
	      "video/x-ms-asf" => "asf",
	      "video/x-ms-wmv" => "wmv"
		);
		if (isset($contentsMaping[$content]))
			return $contentsMaping[$content];
		else return $content;
	}

	function __saveAs($fileData, $fileName=null, $folder) {

		if (is_writable($folder)) {
			if (is_uploaded_file($_FILES[$fileData]['tmp_name']))
			{
				if (empty($fileName)) $fileName = $_FILES[$fileData]['name'];
				copy($_FILES[$fileData]['tmp_name'], $folder.$fileName);
				return true;
			}
			else
			{
				return false;
			}
		}
		else
		{
			return false;
		}
	}

	function __getFolder(&$model, $record) {
		extract($this->settings[$model->name]);
		return  Inflector::camelize($model->name) .'/'. $record[$model->primaryKey] . '/';
	}
	function __getFullFolder(&$model, $field) {
		extract($this->settings[$model->name]);
		return  WWW_ROOT . IMAGES_URL. Inflector::camelize($model->name) .DS. $model->id .DS;
	}

	function __saveFile(&$model, $field, $fileData) {
		extract($this->settings[$model->name]);
		$folderName = $this->__getFullFolder($model, $field);
		$ext=$this->decodeContent($this->__getContent($fileData));
		$fileName=$field .'.'. $ext;

		uses ('folder');
		uses ('file');
		$folder = &new Folder($path = $folderName, $create = true, $mode = 0777);

		$files=$folder->find($fileName);

		$file= &new File($folder->pwd().DS.$fileName);

		$fileExists=($file!==false);
		if ($fileExists) {
			@$file->delete();
		}

		if (isset($fields[$field]['resize']['width']) && isset($fields[$field]['resize']['height'])) {
			$file=$folder->pwd().DS.'tmp_'.$fileName;
			copy($fileData['tmp_name'], $file);
			$this->__resize($folder->pwd(),'tmp_'.$fileName,$fileName,$field, $fields[$field]['resize']);
			@unlink($file);
		} else {
			$file=$folder->pwd().DS.$fileName;
			copy($fileData['tmp_name'], $file);
		}

		if ($fields[$field]['thumbnail']['create']) {
			$fieldParams=$fields[$field]['thumbnail'];
			$newFile=$this->__getPrefix($fieldParams).'_'.basename($fileName);
			$this->__resize($folder->pwd(),$fileName,$newFile, $field, $fieldParams);
		}
		foreach($fields[$field]['versions'] as $version) {
			$fieldParams=$fields[$field]['thumbnail'];
			$newFile=$this->__getPrefix($version).'_'.basename($fileName);
			$this->__resize($folder->pwd(),$fileName,$newFile,$field, $version);

		}

	}

	function __getPrefix($fieldParams) {
		if (isset($fieldParams['prefix'])) {
			return $fieldParams['prefix'];
		} else {
			return $fieldParams['width'].'x'.$fieldParams['height'];
		}
	}

	/**
	 * Automatically resizes an image and returns formatted IMG tag
	 *
	 * @param string $path Path to the image file, relative to the webroot/img/ directory.
	 * @param integer $width Image of returned image
	 * @param integer $height Height of returned image
	 * @param boolean $aspect Maintain aspect ratio (default: true)
	 * @param array    $htmlAttributes Array of HTML attributes.
	 * @param boolean $return Wheter this method should return a value or output it. This overrides AUTO_OUTPUT.
	 * @return mixed    Either string or echos the value, depends on AUTO_OUTPUT and $return.
	 * @access public
	 */
    function __resize($folder, $originalName, $newName, $field, $fieldParams) {

        $types = array(1 => "gif", "jpeg", "png", "swf", "psd", "wbmp"); // used to determine image type
        $fullpath = $folder;

        $url = $folder.DS.$originalName;

        if (!($size = getimagesize($url)))
            return; // image doesn't exist

		$width=$fieldParams['width'];
		$height=$fieldParams['height'];
        if ($fieldParams['allow_enlarge']===false) { // don't enlarge image
			if (($width>$size[0])||($height>$size[1])) {
				$width=$size[0];
				$height=$size[1];
			}
		} else {
	        if ($fieldParams['aspect']) { // adjust to aspect.
	            if (($size[1]/$height) > ($size[0]/$width))
	                $width = ceil(($size[0]/$size[1]) * $height);
	            else
	                $height = ceil($width / ($size[0]/$size[1]));
	        }
        }

        $cachefile = $fullpath.DS.$newName;  // location on server

        if (file_exists($cachefile)) {
            $csize = getimagesize($cachefile);
            $cached = ($csize[0] == $width && $csize[1] == $height); // image is cached
            if (@filemtime($cachefile) < @filemtime($url)) // check if up to date
                $cached = false;
        } else {
            $cached = false;
        }

        if (!$cached) {
            $resize = ($size[0] > $width || $size[1] > $height) || ($size[0] < $width || $size[1] < $height || ($fieldParams['allow_enlarge']===false));
        } else {
            $resize = false;
        }

        if ($resize) {
            $image = call_user_func('imagecreatefrom'.$types[$size[2]], $url);
            if (function_exists("imagecreatetruecolor") && ($temp = imagecreatetruecolor ($width, $height))) {
                imagecopyresampled ($temp, $image, 0, 0, 0, 0, $width, $height, $size[0], $size[1]);
              } else {
                $temp = imagecreate ($width, $height);
                imagecopyresized ($temp, $image, 0, 0, 0, 0, $width, $height, $size[0], $size[1]);
            }
            call_user_func("image".$types[$size[2]], $temp, $cachefile);
            imagedestroy ($image);
            imagedestroy ($temp);
        }

    }

}
?>

PHPのjson_decode()の挙動がおかしくてはまったので覚書を。
通常、jsonのフォーマットではフィールド名(プロパティ名)部分はクオートする必要はない、というかクオートしないのが正しい形と思っていたが、PHPのjson_decode($json_str)に、クオートされていないフィールド名を持つjsonを渡すと、戻り値はundefinedとなる。
これって仕様としてどうなんでしょうか、、、
ちなみに、 json_last_error()というメソッドがあり、直近のエラーを拾うことができるようなので、おかしいかな?というときは使ってみると何かわかるかも。