タグ別アーカイブ: Android

Android Lollipop での SDカードへ書き込み

タグ: | 投稿日: 投稿者:

こんにちは、kikuです。

Lollipop になりアプリからSDカードへ書き込みできるようになりました。
でも書き込みが可能になっただけで、4.3までの様に自由に書けるわけではないんですね。

これが非常にめんどくさいわけです。。。

今日は Lollipop でのSDカードへの書き込み方についてです。

簡単に説明しますと、、
まずユーザにUI操作で明示的にアクセスを許可するディレクトリを指定してもらう必要があります。(←これが厄介です)
その上で Storage Access Framework を使えば出来るようになります。

具体的には ↓ のコードによって遷移する画面上でユーザに許可してもらいます。

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, REQUEST_CODE);

UIで許可してもらったら、onActivityResult に戻ってくるので、そこでアクセスに必用なURIを取得します。
そのURIから DocumentFile を作成しこれでファイルにアクセスできます。
DocumentFile の作成は ↓ こんな感じです。

public void onActivityResult(int reqCode, int resCode, Intent resData) {
  if (reqCode == REQUEST_CODE && resCode == Activity.RESULT_OK) {
    Uri treeUri = resData.getData();
    DocumentFile pickedDir = DocumentFile.fromTreeUri(getActivity(), treeUri);
    // pickedDir でごにょごにょが可能に
  }
}

この取得した URI から作成した DocumentFile を使えばディレクトリ作成やファイル作成等の書き込みができます。
例えばディレクトリ作成なら ↓ こんな感じ

DocumentFile pickedDir = DocumentFile.fromTreeUri(getActivity(), treeUri);
pickedDir.createDirectory("dir_name");

書き込みを行う毎にURIが必用なのでアプリ側で保持しておきましょう。

一応こんな感じで書き込みできます。なかなか取っ付き難くて理解に苦労しました。。(Googleさん、前の仕様に戻して下さい)

もうGoogleさんはSDカードを使わせる気がないっぽいですよね。
今後のアプリはSDカードを使わないでも動くように作るのが良いのかもしれません。


Android 5.0 Lollipop でのSwitch

タグ: | 投稿日: 投稿者:

こんにちは。kikuです。

Android 5.0 が普及してきましたね。
既存のアプリが何も調整せず動いてくれるととても助かるんですけど、なかなかそううまくはいきません。

今回5.0用に対応したのがSwitch。
4.4までは普通に表示していたスイッチが、何故かテキストしか表示されなくなってしまいました。
現象としてはこんな感じ。
なんで消えてしまったのか不明なんですが、色々調べても他に同じ現象の方があまり居なかったので特別な条件があるのかも。。

とりあえず元々のxmlはこんな感じ

<Switch
    android:id="@+id/switch"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical"
    android:gravity="center_vertical"
    android:textOff="OFF"
    android:textOn="ON"
    android:singleLine="true"/>

で今回対応したのがこれ。

<Switch
    android:id="@+id/switch"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical"
    android:gravity="center_vertical"
    android:textOff="OFF"
    android:textOn="ON"
    android:thumb="@drawable/switch_bg"
    android:track="@drawable/switch_track"
    android:switchMinWidth="35dp"
    android:singleLine="true"/>
drawable/switch_bg.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="false" android:drawable="@drawable/g_thumb" />
    <item android:state_pressed="true"  android:drawable="@drawable/c_thumb" />
    <item android:state_checked="true"  android:drawable="@drawable/c_thumb" />
    <item                               android:drawable="@drawable/g_thumb" />
</selector>
drawable/switch_track.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_focused="true" android:drawable="@drawable/c_thumb" />
    <item                              android:drawable="@drawable/g_track" />
</selector>
drawable/g_thumb.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <size android:height="25dp" android:width="25dp" />
    <gradient android:height="25dp" android:width="25dp"
        android:startColor="#BBBBBBBB" android:endColor="#BBBBBBBB"/>
</shape>
drawable/c_thumb.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <size android:height="25dp" android:width="25dp" />
    <gradient android:height="25dp" android:width="25dp"
        android:startColor="#FF5599DD" android:endColor="#FF5599DD"/>
</shape>
drawable/g_track.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <size android:height="25dp" android:width="25dp" />
    <gradient android:height="25dp" android:width="25dp"
        android:startColor="#DDDDDDDD" android:endColor="#DDDDDDDD"/>
</shape>

これらを設定したら表示できました。
Switchにthumbとかtrackを設定しないと5.0だと消えてしまうみたいです、私の環境だと。(他は知らない)

※2015/12/4追記
AndroidManifest.xmlのテーマ設定を新しくすれば表示が可能とのコメントを頂きました!
ほげさん、ありがとうございます!

世間的には5.0からSwitchCompatというのが追加されたようで、ちょっと調べたらそれを使おうみたいな流れになっているように感じられましたが、実際はどうなんでしょうか。
時代は古い端末を切り捨てていくスタイルなんでしょうかね。

開発者としましては、できれば発売から2年したら新しい端末にするのが常識みたいな世の中になってほしいものです。


Android用 FFmpeg を利用した Androidアプリ

タグ: , | 投稿日: 投稿者:

お久しぶりです。
takeです。

前回の記事はこちら

前回 libffmpeg.so のビルドが完了したので、今回はこのライブラリを使ってJNI部分を作成します。

JNIを作成するため、Eclipse(luna)に以下の設定が必要です。
・C/C++開発環境(Help->Install New Software)
http://download.eclipse.org/releases/luna を入力し、以下を選択する
・Programming Languages -> C/C++ Development Tools
・Programming Languages -> C/C++ Development Tools SDK
・Programming Languages -> C/C++ Library API Documentation Hover Help

・NDKプラグインのインストール(Help->Install New Software)
https://dl-ssl.google.com/android/eclipse/ を入力し、以下を選択する
・Developer Tools -> Android Native Development Tools

・NDKの設定(Window->Preferences->Android->NDK)
前回の記事でNDKを展開したディレクトリを指定します。

以上で設定は終わりです。

次は、プロジェクトを作成します。
File -> New -> Android Application Project を選択します。
アプリケーション名を適当にいれてプロジェクトを作ります。

プロジェクト作成後、プロジェクトメニューから Android Tools -> Add Native Support を選択します。
JNIフォルダが作成されて準備ができました。

ここからが大変です。自分も試行錯誤した結果なので誤ってる個所があるかもしれませんがご容赦ください。

まず、FFMpegライブラリのコピーを実施します。
・jniフォルダにlibフォルダを作成します。
・build/ffmpeg/armv7/libffmpeg.so を jni/lib へ
・build/ffmpeg/armv7/include を jni/include へ

JNIフォルダにある Android.mk ファイルを編集します。

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg
LOCAL_EXPORT_C_INCLUDES := include
LOCAL_SRC_FILES := lib/libffmpeg.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE    := FFMpegTest
LOCAL_SRC_FILES := FFMpegTest.cpp
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_LDLIBS := -llog -ljnigraphics -lz -landroid  -ldl
LOCAL_SHARED_LIBRARIES := ffmpeg
include $(BUILD_SHARED_LIBRARY)

$(call import-module,android/cpufeatures)

※今回のプロジェクトはFFMpegTestとしています。

編集内容を保存後、プロジェクトメニューから「Build Project」を実施すると
JNIライブラリが作成されているはずです。
(I/Fがないのでなにもできませんが。。。)

長くなってしまったので、続きはまた次回!!


Android 4.4以降のSDカード書き込みについて

タグ: | 投稿日: 投稿者:

こんにちは、kikuです。

GoogleはAndroidの4.4からSDカードへの書き込みの仕様が変わって、大勢の開発者達を泣かせていますね。ひどい男です。

現状どのような状況になったのかまとめてみました。

まず今回の対象端末はプライマリストレージ本体のものでセカンダリストレージSDカードの端末、つまり大容量の内部ストレージを所持しつつSDカードにも対応している端末です。

しかし、全くSDカードに書き込めないというわけではありません。
全く書き込めなければSDカード対応という存在意義がなくなってしまいますよね。
下記パスにならアプリ側から書き込みが可能なようです。
[SDカードのroot]/Android/data/[パッケージ名]/[ここから書き込みできます]

しかし!アプリ側から上記階層の[パッケージ名]のフォルダを作成する事はできませんでした。
これは恐らく [SDカードのroot]/Android/data/ のディレクトリに書き込み権限がないからでしょう。
少なくとも私が色々試したところできませんでした。(何かできる方法があれば教えてください)

つまりこういうこと
[SDカードのroot]/Android/data/[ここへは書き込めません]
[SDカードのroot]/Android/data/[パッケージ名]/[ここに書き込めます]

開発者を泣かせすぎです。許しがたいです。よっぽどSDカードは使わせたくないんですね。

ちなみにアプリをアンインストールすると[パッケージ名]フォルダごと消されます。そこらへんも怖い仕様です。

端末にプリインストールされているファイルマネージャアプリは[パッケージ名]フォルダを作成することが出来るようですので、ユーザ側からすると色々と弄くれるみたいですね。

詳しくは、Android.comの「外部ストレージ技術情報」をご覧下さい。

2015/12/8 追記
APIレベル19以降(4.4)であれば、Context.getExternalFilesDirs()という便利なものが使えました!
これをアプリ内で呼べば下記フォルダを作成した上にパスの取得も可能です。
[SDカードのroot]/Android/data/[パッケージ名]


Android用 FFmpeg を ビルド

タグ: | 投稿日: 投稿者:

お久しぶりです。
takeです。

今回は、FFmpeg-Android を centos6.5 でビルドするまでを記述したいと思います。

まず、必要なアイテムをダウンロードしてきます。

※NDKの展開等はここでは割愛します。

環境変数ANDROID_NDKを設定し、FFmpeg-Android ディレクトリ配下にある「FFmpeg-Anroid.sh」を起動(ビルド)します。
sudo ./FFmpeg-Android.sh

あれ?なんかANDROID_NDKの環境変数がうまく読み込めなくてエラーになってやがる。
しょうがないので、FFmpeg-Android.shを編集して、export ANDROID_NDK=xxxxx を追記。
で、またビルド!オラッ!!

途中のログで(たまたま見てた)、ccacheが無い!!とかエラーがでてたので、以下のコマンドでインストール。
sudo yum install --enablerepo=epel ccache
で、またビルド!オラッ!!

ログはあんまり見てなかったけど(すごい時間かかるので見てられない)、完走したので大丈夫かな?
意外とすんなりいけてびっくり!

次回は、今回作成したlibffmpeg.soを使用して、Androidアプリを作ってみたいと思います。

では。


Androidでネットワーク状態を知る

タグ: | 投稿日: 投稿者:

takeです。

弊社の動画配信サービスをAndroidで利用する際、現在のネットワーク状態により
配信する動画のビットレートを決定していたのですが。。。

当初のアプリ配信時には、ネットワーク状態は3GとWifiしかなくてWifiなら高ビットレート、
3Gなら低ビットレートを選択みたいな事をアプリからやってました。
が、時代の流れとは早いもので昨今ではWiMAX(+WiMAX)やらLTEやら新しいネットワークが増えてきました。

ユーザにとっては回線速度があがってとってもありがたい話しなんですが、開発者にとっては
たまったもんじゃありません(汗
上記のように3GとWifiのみで判定していると、プログラムの組み方によっては高速回線であるはずの
WiMAXやLTEが『3G』と判断されて画質の悪い低ビットレートの動画が配信されてしまいます。

この状態を回避する為に、正しいネットワーク状態を取得してみましょう。

まず、ネットワーク情報を取得する為、AndroidManifest.xml に以下のコードを記述します。

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

これでネットワーク状態を取得する準備ができました。

次は、取得メソッドです。

public enum NetworkStatus {
   OFF,
   MOBILE,
   WIFI,
   WIMAX,
   LTE,
}

public NetworkStatus getConnectedState(Context context) {

	ConnectivityManager manager = (ConnectivityManager)context.getSystemService(CONNECTIVITY_SERVICE);
	NetworkStatus status = NetworkStatus.OFF;

	NetworkInfo info = manager.getActiveNetworkInfo();
	if (info == null || info.isConnected() != true) {
		// OFF
		return status;
	}

	switch (info.getType()) {
	case ConnectivityManager.TYPE_WIFI:			// Wifi
		status = NetworkStatus.WIFI;
		break;
	case ConnectivityManager.TYPE_MOBILE_DUN:	// Mobile 3G
	case ConnectivityManager.TYPE_MOBILE_HIPRI:
	case ConnectivityManager.TYPE_MOBILE_MMS:
	case ConnectivityManager.TYPE_MOBILE_SUPL:
	case ConnectivityManager.TYPE_MOBILE:
		switch (info.getSubtype()) {
		case TelephonyManager.NETWORK_TYPE_LTE:
			status = NetworkStatus.LTE;			// LTE
			break;
		default:
			status = NetworkStatus.MOBILE;		// Mobile 3G
			break;
		}
		break;
	case ConnectivityManager.TYPE_WIMAX:		// Wimax
		status = NetworkStatus.WIMAX;
		break;
	}
	return status;
}

一応これで取得できるはずなんですが、LTEを判定できるのは API Level 11(Android3.0) からなんです。
Galaxy S II LTE SC-03D みたいな Android2.3なのにLTE搭載機は正常に判定できるかわかりません。

では。