コンテンツのダウンロード
このチュートリアルでは、コンテンツのダウンロード方法について学びます。 使用するCGIは command.cgi です。 このチュートリアルは別項の Android Tutorial 2:コンテンツリストの取得 に基づいています。
現在のコンテンツリストを取得し
ListView
に表示します。
リストに表示されたフォルダをクリック(タップ)した場合、そのフォルダの中身を表示します。イメージファイルをクリック(タップ)した場合は、そのファイルをダウンロードし、画面に表示します。
レイアウトは、コンテンツリストの上にコンテンツ数を表示し、その上に現在のフォルダパスを表示します。
また、一つ上のフォルダへ戻るための
Button
も配置します(例. 現在のフォルダが ‘DCIM/106
___05/’ である場合は、 ‘DCIM/’ へ移動します)。
このようにレイアウトを設定します:
コンテンツリストのイメージファイルをクリックすると、 画像はこのように表示されます:
表示されているとおり、Android 端末に画像ファイルがダウンロードされます。
アプリケーションを作成するために、次のファイルを作成します :
- MainActivity.java
- activity_main.xml
- ImageViewActivity.java
- activity_image_view.xml
重要: Android application は、デフォルトの状態ではインターネットにアクセスすることができません。 そのため、設定ファイル AndroidManifest.xml を変更し、権限を与える必要があります。 パス: [Project_Folder]/AndroidManifest.xml AndroidManifest.xml に以下のコードを追加してください:
<uses-permission android:name="android.permission.INTERNET" />
リストのレイアウト作成
ではまず、 activity_main.xml を記述して、レイアウトを決定します。 このファイルは、layout フォルダに配置されています。 パス: [Project_Folder]/res/layout/activity_main.xml
activity_main.xml に以下のように記述してください:
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
tools:context=".MainActivity" >
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:cacheColorHint="#00000000"
android:text="Back"
android:textColor="@android:color/white"
android:textSize="20sp" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:scaleType="centerInside"
android:text="Directory Name"
android:textAlignment="center"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:scaleType="centerInside"
android:text="Number of files"
android:textAlignment="center"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="20sp" />
<ListView
android:id="@+id/listView1"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:cacheColorHint="#00000000"
android:clickable="true"
android:headerDividersEnabled="false" />
</LinearLayout>
イメージビューのレイアウト作成
次に、 activity_image_view.xml を記述して、レイアウトを決定します。 このファイルは、layout フォルダに配置されています。 パス: [Project_Folder]/res/layout/activity_image_view.xml
activity_image_view.xml に以下のように記述してください:
activity_image_view.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ImageViewActivity" >
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:cacheColorHint="#00000000"
android:text="Back"
android:textColor="@android:color/white"
android:textSize="20sp" />
<ImageView
android:id="@+id/imageView1"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:scaleType="centerInside"
android:contentDescription="view image"
android:text="image would be here" />
</LinearLayout>
イメージビューのアクティビティ作成
次に、
AndroidManifest.xml を記述して、アクティビティを追加します。
パス:
[Project_Folder]/AndroidManifest.xml
AndroidManifest.xml の application タグに以下のように記述してください:
AndroidManifest.xml
<activity
android:name="com.example.android_tutorial_03.ImageViewActivity"
android:label="@string/app_name" >
</activity>
コンテンツリストの作成
さて、 MainActivity.java を修正します。 デフォルトでは次のようになります:
MainActivity.java
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
初期化
リストの項目をクリックした場合の処理を記述するため、クラス宣言にOnClickListenerを追加します。
MainActivity.java (1)
public class MainActivity extends Activity implements AdapterView.OnItemClickListener {
使用予定のビューのうちの1つの宣言をし、その他のクラス変数の宣言や画面の初期設定を行います。
また、
onCreate(Bundle savedInstanceState)
をオーバーライドし、アクティビティクラスの初期化も行います。
コンテンツリストの初期設定や一つ上のフォルダへ戻るための
Button
のクリックリスナーも設定します。
MainActivity.java (2)
ListView listView;
ImageView imageView;
TextView currentDirText;
TextView numFilesText;
Button backButton;
String rootDir = "DCIM";
String directoryName = rootDir; // Initialize to rootDirectory
ArrayAdapter<String> listAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set buttons
getWindow().setTitleColor(Color.rgb(65, 183, 216));
backButton = (Button)findViewById(R.id.button1);
backButton.getBackground().setColorFilter(Color.rgb(65, 183, 216), PorterDuff.Mode.SRC_IN);
backButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(directoryName.equals(rootDir)) {
listRootDirectory();
}
else {
int index = directoryName.lastIndexOf("/");
directoryName = directoryName.substring(0, index);
listDirectory(directoryName);
}
}
});
backButton.setEnabled(false); // Disable in root directory
listRootDirectory();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
- 17-32行目
ユーザーがButton
をクリックした際、一つ上のフォルダへ行けるように設定しています。
元々表示しているルートフォルダは、それより上のフォルダへは移動できないため、ボタンを無効にしています。
ルートのフォルダを”DCIM”としていますが(6行目)、こちらは同じように設定をする必要はありません。 販売されている多くのデジタルカメラが撮影した画像を “DCIM” フォルダへ格納しているので、ここでは “DCIM” を設定しました。
ルートフォルダのコンテンツ情報取得
上記で呼ばれている
listRootDirectory()
と
listDirectory(directoryName)
は、FlashAir に処理を要求しコンテンツを取得しています。
listRootDirectory()
は、単にクラス変数
directoryName
にルートフォルダを設定し
listDirectory(String dir)
を呼び出しています:
MainActivity.java (3)
public void listRootDirectory() {
directoryName = rootDir;
listDirectory(directoryName);
}
その他のフォルダのコンテンツ情報取得
現在のフォルダがルートフォルダである場合に、back ボタンの制御を行います。
MainActivity.java (4)
public void listDirectory(String dir)
{ // Prepare command directory path
if(dir.equals(rootDir)) {
backButton.setEnabled(false);
}
else {
backButton.setEnabled(true);
}
- 3-8行目
back ボタンの有効/無効を制御しています。
ルートフォルダを表示中の場合は、それより上の階層へ移動できいないためです。
その結果、画面はこのようになります:
それ以外のフォルダを表示している場合は、ボタンは有効でありクリックすることができます。
ファイル数を取得するためには、以下のコマンドを使用します。
- ファイル数は、
command.cgi
にop=101
とフォルダパスを指定することで取得できます。- コマンド:
http://flashair/command.cgi?op=101&DIR=/DCIM
- コマンドが返す情報:
<NumberofItems>
- コマンド:
ファイルデータを取得するためには、以下のコマンドを使用します。
- ファイルデータは、
command.cgi
にop=100
とフォルダパスを指定することで取得できます。- コマンド:
http://flashair/command.cgi?op=100&DIR=/DCIM
- コマンドが返す情報:
<Directory>,<Filename>,<Size>,<Attribute>,<Date>,<Time>
- コマンド:
コマンドの実行には前項 Android Tutorial 2:コンテンツリストの取得 と同じく、 FlashAirRequest.java を使用します。
MainActivity.java (5)
currentDirText = (TextView)findViewById(R.id.textView1);
currentDirText.setText(dir + "/");
// Fetch number of items in directory and display in a TextView
dir = "/" + dir;
ArrayList <NameValuePair> httpParams = new ArrayList <NameValuePair> ();
httpParams.add(new BasicNameValuePair("DIR", dir));
dir = URLEncodedUtils.format (httpParams, "UTF-8" );
numFilesText = (TextView)findViewById(R.id.textView2);
// Fetch number of items in directory and display in a TextView
new AsyncTask<String, Void, String>(){
@Override
protected String doInBackground(String... params) {
String dir = params[0];
String fileCount = FlashAirRequest.getString("http://flashair/command.cgi?op=101&" + dir);
return fileCount;
}
@Override
protected void onPostExecute(String fileCount) {
numFilesText.setText("Items Found: " + fileCount);
}
}.execute(dir);
- 7行目
返される文字のエンコードは、ここでは UTF-8 を使用します。
command.cgi
の
op=100
は指定したフォルダの以下の情報を返します:
<Directory>,<Filename>,<Size>,<Attribute>,<Date>,<Time>
今回使用するのは、ファイル名のみです。
CGIコマンド実行後、ファイル名のみを取り出すよう調整する必要があります。
また、クラスが現在とルートのフォルダを管理しているので、手動でフォルダ名をCGIに渡す必要はなく、代わりに関数から渡されたフォルダ名を使用します。
MainActivity.java (6)
// Fetch list of items in directory and display in a ListView
new AsyncTask<String, Void, ListAdapter>(){
@Override
protected ListAdapter doInBackground(String... params) {
String dir = params[0];
ArrayList <String> fileNames = new ArrayList <String>();
String files = FlashAirRequest.getString("http://flashair/command.cgi?op=100&" + dir);
String[] allFiles = files.split("([,\n])"); // split by newline or comma
for(int i = 2; i < allFiles.length; i= i + 6) {
if(allFiles[i].contains(".")) {
// File
fileNames.add(allFiles[i]);
}
else { // Directory, append "/"
fileNames.add(allFiles[i] + "/");
}
}
listAdapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, fileNames);
return listAdapter;
}
@Override
protected void onPostExecute(ListAdapter listAdapter) {
// Set the file list to a widget
listView = (ListView)findViewById(R.id.listView1);
ColorDrawable divcolor = new ColorDrawable(Color.rgb(17, 19, 58));
listView.setDivider(divcolor);
listView.setDividerHeight(1);
listView.setAdapter(listAdapter);
listView.setOnItemClickListener(MainActivity.this);
}
}.execute(dir);
- 8-17行目
フォルダであるか、ファイルであるのかをチェックしています。 フォルダの場合は、表示する際最後に “/”を付加しています。これはフォルダの場合の標準的な記述方法です。
さらにこの仕組は、コンテンツリストをクリックした際の、フォルダとファイルで処理を分けるためにも使用します。
コンテンツリストのクリックリスナー設定
MainActivity
クラスは現時点では、コンテンツリストは表示されますがクリックすることができません。
このため、OnItemClickListener
をMainActivity
に定義します。
この特別なクリックリスナーは、単にリストをクリックしたことを把握するだけではなく、どのコンテンツをクリックしたのかを識別します。
リストは次のように設定します:
- フォルダをクリックした場合は、選択したフォルダのコンテンツを表示した画面を表示します。
- ファイルをクリックした場合は、ファイルをダウンロードし、ダウンロードした画像を表示しした画面を表示します。
元のonItemClick
は、< AdapterView<?> l, View v, int position, long id >
といった引数です。
オーバーライドするため、これらの引数も渡します。
MainActivity.java (7)
@Override
public void onItemClick(AdapterView<?> l, View v, int position, long id) {
Object downloadFile = l.getItemAtPosition(position); // get item at clicked position in list of files
if(downloadFile.toString().endsWith("/")) { // Directory, remove "/" and show content list
String dirName = downloadFile.toString().substring(0, downloadFile.toString().length()-1); // all but the "/"
directoryName = directoryName + "/" + dirName;
listDirectory(directoryName);
}
else if( downloadFile.toString().toLowerCase(Locale.getDefault()).endsWith(".jpg") || downloadFile.toString().toLowerCase(Locale.getDefault()).endsWith(".jpeg")
|| downloadFile.toString().toLowerCase(Locale.getDefault()).endsWith(".jpe") || downloadFile.toString().toLowerCase(Locale.getDefault()).endsWith(".png") )
{ // Image file, download using ImageViewActivity
Intent viewImageIntent = new Intent(this, ImageViewActivity.class);
viewImageIntent.putExtra("downloadFile", downloadFile.toString());
viewImageIntent.putExtra("directoryName", directoryName);
MainActivity.this.startActivity(viewImageIntent);
}
}
} // End MainActivity class
- 12行目
実際にファイルをダウンロードし表示するのは2つめのクラス (class ImageViewActivity
)なので、Intent
を作成します。class ImageViewActivity
は、ファイルをダウンロードし表示するので、ファイル名 (downloadFile.toString()
) と フォルダパス (directoryName
) にアクセス可能である必要があります。 - 13-14行目
Intent
に、上記の値を設定します。これにより、ImageViewActivity
生成時に、この値が渡されます。
イメージビューの作成
初期化
このクラスもまたActivity
クラスを拡張したクラスです。
画像を表示するためのImageView
と コンテンツリスト画面へ戻るためのButton
を設定します。
クラス宣言は次のようになります:
ImageViewActivity.java (1)
public class ImageViewActivity extends Activity {
ImageView imageView;
Button backButton;
onCreate(Bundle savedInstanceState)
と
onCreateOptionsMenu(Menu menu)
をオーバーライドする必要もあります。
onCreate(Bundle savedInstanceState)
にて、 back ボタンの動作を設定します。
ImageViewActivity.java (2)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_view);
getIntent();
imageView = (ImageView)findViewById(R.id.imageView1);
backButton = (Button)findViewById(R.id.button2);
getWindow().setTitleColor(Color.rgb(65, 183, 216));
backButton.getBackground().setColorFilter(Color.rgb(65, 183, 216), PorterDuff.Mode.SRC_IN);
Bundle extrasData = getIntent().getExtras();
String fileName = extrasData.getString("downloadFile");
String directory = extrasData.getString("directoryName");
downloadFile(fileName, directory);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.image_view, menu);
return true;
}
- 10行目
Intent
を宣言した際class MainActivity
から渡したデータを読み込んでいます。 - 11-12行目
読み込んだ各値を保存しています。
ファイルのダウンロード
次に, FlashAir からファイルをダウンロードする機能を実装します。
CGIコマンドを実行し結果のBitmap
を返す、getBitmap()
を前項Android Tutorial 2:コンテンツリストの取得 で作成した
FlashAirRequest.java に追加します。
FlashAirRequest.java
static public Bitmap getBitmap(String command) {
Bitmap resultBitmap = null;
try{
URL url = new URL(command);
URLConnection urlCon = url.openConnection();
urlCon.connect();
InputStream inputStream = urlCon.getInputStream();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] byteChunk = new byte[1024];
int bytesRead = 0;
while( (bytesRead = inputStream.read(byteChunk)) != -1) {
byteArrayOutputStream.write(byteChunk, 0, bytesRead);
}
byte[] byteArray = byteArrayOutputStream.toByteArray();
BitmapFactory.Options bfOptions = new BitmapFactory.Options();
bfOptions.inPurgeable = true;
resultBitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length, bfOptions);
byteArrayOutputStream.close();
inputStream.close();
}catch(MalformedURLException e) {
Log.e("ERROR", "ERROR: " + e.toString());
e.printStackTrace();
}
catch(IOException e) {
Log.e("ERROR", "ERROR: " + e.toString());
e.printStackTrace();
}
return resultBitmap;
}
- 15-16行目
メモリーを有効活用するために、解放可能なBitmap
を生成しています。
Intent
により渡したファイル名とフォルダパスからパスを作成し、ファイルをダウンロードします。
ファイルのダウンロードには、FlashAirRequest.java に追加した
getBitmap()
を使用します。
ImageViewActivity.java (3)
void downloadFile(String downloadFile, String directory) {
final ProgressDialog waitDialog;
// Setting ProgressDialog
waitDialog = new ProgressDialog(this);
waitDialog.setMessage("Now downloading...");
waitDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
waitDialog.show();
// Download file
new AsyncTask<String, Void, Bitmap>(){
@Override
protected Bitmap doInBackground(String... params) {
String fileName = params[0];
return FlashAirRequest.getBitmap(fileName);
}
@Override
protected void onPostExecute(Bitmap resultBitmap) {
waitDialog.dismiss();
viewImage(resultBitmap);
}
}.execute("http://flashair/" + directory + "/" + downloadFile.toString());
}
画像を表示する
Bitmap イメージを読み込んだ後、上記の関数でviewImage()
を呼び出すと、画像が表示されます。
この関数では画像の表示にImageView
を使用しています。
ImageViewActivity.java (4)
lined:
void viewImage(Bitmap imageBitmap) {
// Show image in ImageView
backButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ImageViewActivity.this.finish();
}
});
if (imageBitmap == null) {
imageView.setImageResource(R.drawable.ic_launcher);
}
else {
imageView.setImageBitmap(imageBitmap);
}
}
} // End ImageViewActivity class
実行結果
画像はこのように表示されます:
サンプルコード
android_tutorial_03.zip (539KB)
このサイトのサンプルコードは二条項BSDライセンスで提供されています。