こものAndroidメモ

Androidアプリの開発メモブログ!!

Android4.4でメディアマウント時に落ちた時の回避方法 —

端末のアップデートでAndroid 4.4にすると、
昔作っていたカメラアプリが画像保存時に強制終了するようになりました。
 

会社勤め時代に作ったアプリなので、今はもう手元にソースコードはありませんが、
アプリ自体は普段から使っているので大変困りました。
 

なので、後任者の方に直してもらいたいと思い、原因の調査に乗り出してみました。
 

強制終了時にLogCatで確認したら、下記のエラーが出ていました。
 

java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.intent.action.MEDIA_MOUNTED from pid=XXXXX, uid=XXXXX
 
 

むむむっ。どうやらAndroid 4.4からメディアをマウントするためのBroadcastの送信が使えなくなったみたいですね。
Android 4.3端末では正常に動いていました。
 

画像ファイルを外部ストレージに保存する場合、物理的に保存するだけではギャラリーアプリなどが認識してくれないので、
下記のようにマウントのBroadcastを送信してギャラリアプリに認識させていました。
 

sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, 
    Uri.parse ("file://" + Environment.getExternalStorageDirectory())));

 

ずいぶん昔の話ですが、当時はギャラリーアプリが認識してくれる魔法のコードとして、
どこかのサイトで紹介されていたのをそのまま使っていたのをふと思い出しました。

それがどうやらAndroid 4.4から使えなくなったみたいです。
 
 

対策として、メディアをマウントし直すのではなく、MediaScannerConnectionを使って
保存したファイルをMediaContentProviderに登録(スキャン)する方法なら回避できるみたいです。

テストコードを書いたところ、ギャラリーアプリなどにすぐに反映されるのも確認しました。
 

String[] filePath = {targetFIleName};
String[] mimeType = {"image/*"};
MediaScannerConnection.scanFile(context, filePath, mimeType, null);

 

アプリを作った当時からこのコードを書けていたら、今頃こんな調査は必要なかったのでしょうが……(^^;;
 



Startup Weekend Kyotoへ参加してみた —

8月8日(金)〜8月10日(日)にかけて開催された「Startup Weekend Kyoto」というイベントに参加してきました。
 

スタートアップウィークエンド

3日間の時間を使って、起業に必要なプロセスを経験し、メンターと呼ばれる先輩たちに指導してもらい、プロダクトをブラッシュアップしていくイベントになります。
 
 

開発中
 

僕らのチームは、日々の情報を色で残していく「カラーログ」というプロダクトを提案し、僕はAndroidアプリの開発の担当をさせていただきました。
 
 
 

開発中のデモ用画像です。

カラーログ

カラーログ

いろんな失敗もあって、追い詰められたりもしましたが、参加できてとても楽しかったです。
 
 

願わくば、製品を世に出すところまで頑張りたいと思うのでした。
 



DevFest Japan 2014 Summer in 京都 —

Google I/O報告会の「DevFest Japan 2014 Summer」の京都会場に参加してきました。

 

イノベーションルーム

各会場の人たちとGoogle ハングアウトでつなぎます。

京都会場にいるAPI Expertのお二人のセッションを始め、いろんな方々のセッションを聞くことができました。

DevFest Japan 2014 Summer in 京都

 

新しい技術を学ぶのはワクワクしますね(^^)

 

 

尚、会場の提供は、京都リサーチパーク様でした。

京都リサーチパーク

いつもお世話になっております(^^)



Geek Bearへ行ってきた —

久しぶりにGeek Bearへ行ってきました。

普段は平日の木曜日にやっているのですが、
今回は三周年記念イベントということで土曜日に開催されていました。

大阪に用があって行ったついでに、ふらっと行ってみました。

ホワイトベアー

スパイダーマン

段ボールドロイド

あまり長居できませんでしたが、久しぶりに楽しく飲めました。

また機会をみて行ってみたいです。

Geek Bear 公式サイト



カスタムビューを使ってみた —

既存のビュークラスを継承してカスタムビューを作成することができます。

最小限のレイアウトとソースコードで、カスタムビューを作成してみました。

 
完成図

カスタムビューの完成図
 
ソースの構成

カスタムビューの例のソース構造
 
 

下図にて、赤枠で囲んだ部分がカスタムビューになります。
そのカスタムビューを縦に3つ並べました。

カスタムビュー例
 
 

今回のカスタムビューは、LinearLayoutで囲ったButtonとTextViewにて構成しています。
 
 

まず、カスタムビューのxmlファイルについて記述します。
カスタムビューとして、作りたいパーツで構成します。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    
	<Button
	    android:id="@+id/btnTestButton"
	    android:layout_width="wrap_content"
	    android:layout_height="wrap_content"
	    />
	
	<!-- 区切り線 -->
	<TextView 
	    android:layout_width="wrap_content"
	    android:layout_height="wrap_content"
	    android:text="---------------------------------"
	    android:layout_marginBottom="30dp"
	    />
	
</LinearLayout>

 
 

次に、LinearLayoutクラスを継承して、独自のカスタムビュークラスを作成します。
クラス名は任意の名前でいいですが、とりあえず「CustomLayout」としておきます。
 

そして、コンストラクタ内でレイアウト用のxmlを読み込みます。

package com.example.custom;

import com.example.customviewtest.R;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;

/** カスタムレイアウト .*/
public class CustomLayout extends LinearLayout {

	/** 通常のコンストラクタ .*/
	public CustomLayout(Context context) {
		super(context);
	}
	
	/** xml配置用のコンストラクタ .*/
	public CustomLayout(Context context, AttributeSet attrs) {
		super(context, attrs);

		/** カスタムビューのxmlからのlayoutの情報を読み込む. */
		View layout = LayoutInflater.from(context).inflate(
                    R.layout.custom_layout, this);
		
		/** カスタムビューのボタンに任意の文字を表示する例. */
		Button btn = (Button)layout.findViewById(R.id.btnTestButton);
		btn.setText("カスタムビューのボタン");
	}
}

これにて、カスタムビュー側の処理は完了です。

※通常のコンストラクタの方は今回使わないので、xmlの読み込みは省略しています。

 
 

次にメインActivityのxmlファイルにカスタムビューを配置します。

今回はカスタムビューを3つ縦に並べてみます。

パッケージ名を含むクラス名で記述。
今回の例の場合は、「com.example.custom.CustomLayout」となります。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >
    
	<!-- カスタムビューを配置 -->
	<com.example.custom.CustomLayout
	    android:layout_width="wrap_content"
	    android:layout_height="wrap_content"
	    />

	<com.example.custom.CustomLayout
	    android:layout_width="wrap_content"
	    android:layout_height="wrap_content"
	    />
	
	<com.example.custom.CustomLayout
	    android:layout_width="wrap_content"
	    android:layout_height="wrap_content"
	    />
	
</LinearLayout>

 

最後に、何も中身がないですが、メインActivityのソースはこちらです。

package com.example.customviewtest;


import android.app.Activity;
import android.os.Bundle;

/** カスタムビュー用のメインActivity.*/
public class CustomViewTestMainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    	setContentView(R.layout.main_activity);
    }

}

 

こんな感じで、カスタムビューを作ることができます。

あとはカスタムビューを自分好みに作り込むだけです。