FlashAirへのアップロード
このチュートリアルでは
upload.cgiを使用して、CGI経由でFlashAirにファイルをアップロード(書き込み)する方法について学びます。
使用するCGIは
upload.cgi です。
このチュートリアルは別項の
Android Tutorial 4: サムネイルの表示 に基づいています。
概要
FlashAirに保存されている画像を、日付別にタイトル&メモをつけて管理できるようにします。
注意: 本機能を誤って使用すると、FlashAirのファイルシステムを破壊してデータを失う恐れがあります。PCなどのSDメモリカードホスト機器はSDメモリカードの内容(FAT)をキャッシュしている場合がありますが、本CGIで行った変更をSDメモリカードホスト機器が認識する方法がありません。そのため、本CGIとSDメモリカードホスト機器から同時に変更を行うとFAT不整合が起こる可能性があります。 CGIからの書き込み後は必ずカードをいったん抜いて再挿入するなどしてSDメモリカードホスト機器に再認識させてください。
コンテンツのリストに Date List ボタンを配置します。
Date List ボタンをタップすると日付別のリストが表示されます。
日付をタップするとその日付の操作画面が表示されます。
アプリケーションを作成するにあたって、次のファイルを作成します。
- MainActivity.java
- activity_main.xml
- ImageViewActivity.java
- activity_image_view.xml
- DateListActivity.java
- activity_date_list.xml
- list_view_item.xml
- MemoEditActivity.java
- activity_memo_edit.xml
- grid_view_item.xml
- FlashAirRequest.java
重要: Android application
は、デフォルトの状態ではインターネットにアクセスすることができません。
そのため、設定ファイル
AndroidManifest.xml を変更し、権限を与える必要があります。
パス:
[Project_Folder]/AndroidManifest.xml
AndroidManifest.xml に以下のコードを追加してください。
<uses-permission android:name="android.permission.INTERNET" />
画面レイアウトの作成
strings.xml修正
strings.xmlは以下のように記述してください。
このファイルは、values フォルダに配置されています。
パス:
[Project_Folder]/res/values/strings.xml
<string name="app_name">android_tutorial_07</string>
<string name="action_settings">Settings</string>
<string name="title_activity_image_view">ImageViewActivity</string>
<string name="date_list">Date List</string>
<string name="back">Back</string>
<string name="directory_name">Directory Name</string>
<string name="view_image">view image</string>
<string name="image_would_be_here">image would be here</string>
<string name="date">Date</string>
<string name="title">Title</string>
<string name="title_">Title:</string>
<string name="memo">Memo</string>
<string name="memo_">Memo:</string>
<string name="save">Save</string>
<string name="grid_view_image">grid_view_image</string>
コンテンツリストのレイアウト作成
activity_main.xml を記述して、レイアウトを決定します。
このファイルは、layout フォルダに配置されています。
パス:
[Project_Folder]/res/layout/activity_main.xml
activity_main.xml は Android Tutorial 3: コンテンツのダウンロードと同じ状態のレイアウトにボタンを1つ追加します。
activity_main.xml に以下の内容を追加してください。
activity_main.xml
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:cacheColorHint="#00000000"
android:text="@string/date_list"
android:textColor="@android:color/white"
android:textSize="20sp" />
イメージビューのレイアウト作成
次に、
activity_image_view.xml を記述して、レイアウトを決定します。
このファイルは、layout フォルダに配置されています。
パス:
[Project_Folder]/res/layout/activity_image_view.xml
activity_image_view.xml は
Android Tutorial 3: コンテンツのダウンロードと同じになります。
実装の説明についてはそちらを参照してください。
日付一覧のレイアウト作成
次に、
activity_date_list.xml と
list_view_item.xml を記述して、レイアウトを決定します。
list_view_item.xml は、リストの1行分のレイアウトです。
これらのファイルは、layout フォルダに配置されています。
パス:
[Project_Folder]/res/layout/activity_date_list.xml
[Project_Folder]/res/layout/list_view_item.xml
activity_date_list.xml に以下のように記述してください。
activity_date_list.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=".DateListActivity" >
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:cacheColorHint="#00000000"
android:text="@string/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="@string/directory_name"
android:textAlignment="center"
android:textAppearance="?android:attr/textAppearanceLarge" />
<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>
list_view_item.xml に以下のように記述してください。
list_view_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/date" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title" />
</LinearLayout>
操作画面のレイアウト作成
次に、
activity_memo_edit.xml と
grid_view_item.xml を記述して、レイアウトを決定します。
grid_view_item.xml は、サムネイルの表示に使用します。
これらのファイルは、layout フォルダに配置されています。
パス:
[Project_Folder]/res/layout/activity_memo_edit.xml
[Project_Folder]/res/layout/grid_view_item.xml
activity_memo_edit.xml に以下のように記述してください。
activity_memo_edit.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=".MemoEditActivity" >
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:cacheColorHint="#00000000"
android:text="@string/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="@string/directory_name"
android:textAlignment="center"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/textView2"
android:layout_width="152dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center"
android:hint="@string/date"
android:paddingLeft="10dp"
android:textSize="18sp" />
<GridView
android:id="@+id/gridView1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.01"
android:horizontalSpacing="10dp"
android:numColumns="5"
android:verticalSpacing="10dp" >
</GridView>
<TextView
android:id="@+id/TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginTop="12dp"
android:paddingLeft="10dp"
android:text="@string/title_"
android:textSize="18sp" />
<EditText
android:id="@+id/editTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/title"
android:inputType="text"
android:textStyle="italic" >
</EditText>
<TextView
android:id="@+id/TextView02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginTop="20dp"
android:paddingLeft="10dp"
android:text="@string/memo_"
android:textSize="18sp" />
<EditText
android:id="@+id/editMemo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/memo"
android:inputType="text"
android:textStyle="italic" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:cacheColorHint="#00000000"
android:text="@string/save"
android:textColor="@android:color/white"
android:textSize="20sp" />
</LinearLayout>
grid_view_item.xml に以下のように記述してください。
grid_view_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/grid_view_image"
/>
</LinearLayout>
コードの作成
コンテンツリストの作成
さて、
MainActivity.java を修正します。
Android Tutorial 4: サムネイルの表示の状態に、Date List
ボタンをタップした際の処理を追加します。
またここでは画像ファイルのみを対象とするので、絞り込み条件の追加等も行います。
初期化
Android Tutorial 4: サムネイルの表示 の MainActivity.java をコピーし、それを新しい MainActivity.java として使用してください。
メンバ変数の追加と
onCreate(Bundle savedInstanceState)
関数を下記のように置き換えます。
MainActivity.java (1)
public class MainActivity extends Activity implements AdapterView.OnItemClickListener {
ListView listView;
ImageView imageView;
TextView currentDirText;
TextView numFilesText;
Button backButton;
Button datelistButton;
String rootDir = "DCIM";
String directoryName = rootDir; // Initialize to rootDirectory
SimpleAdapter listAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
// Set buttons
backButton = (Button)findViewById(R.id.button1);
getWindow().setTitleColor(Color.rgb(65, 183, 216));
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
datelistButton = (Button)findViewById(R.id.button2);
getWindow().setTitleColor(Color.rgb(65, 183, 216));
datelistButton.getBackground().setColorFilter(Color.rgb(65, 183, 216),
PorterDuff.Mode.SRC_IN);
datelistButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent dateList = new Intent(getBaseContext(), DateListActivity.class);
dateList.putExtra("dir", currentDirText.getText());
MainActivity.this.startActivity(dateList);
}
});
listRootDirectory();
}
catch(Exception e) {
Log.e("ERROR", "ERROR: " + e.toString());
e.printStackTrace();
}
}
作成済みの
MainActivity
に Date List ボタンのためのコードを追加します。
- 38-49行目
Date List ボタンをタップした際、日付一覧画面へ遷移するように設定しています。
残りの MainActivity.java はそのまま使用します。
対象ファイルの絞り込み
今回は画像ファイルのみを対象としたいので、
listDirectory()
内のファイル名を取得する部分で以下のように条件を追加します。
MainActivity.java (2)
if( (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpg"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpeg"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpe"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".png")) ) {
// Image file
fileNames.add(allFiles[i]);
}
再表示時の設定
次の画面から戻ってきた場合など、画面が再表示された際にもリストが更新されるように設定しておきます。
MainActivity.java (3)
@Override
public void onRestart(){
super.onRestart();
listDirectory(directoryName);
}
画像表示画面の作成
class ImageViewActivity
は
Android Tutorial 3: コンテンツのダウンロードと同じです。
実装の詳細についてはこのチュートリアルを参照してください。
日付一覧の作成
初期化
リストの項目をクリックした場合の処理を記述するため、クラス宣言にOnClickListenerを追加します。
DateListActivity.java (1)
public class DateListActivity extends Activity implements AdapterView.OnItemClickListener {
ビューの宣言、その他のクラス変数の宣言や画面の初期設定を行います。
また、
onCreate(Bundle savedInstanceState)
をオーバーライドし、アクティビティクラスの初期化も行います。
リストの初期設定や一つ前の画面へ戻るための
Button
のクリックリスナーも設定します。
DateListActivity.java (2)
ListView listView;
ImageView imageView;
TextView currentDirText;
Button backButton;
String rootDir = "DCIM";
String directoryName;
SimpleAdapter listAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_date_list);
try {
Bundle extrasData = getIntent().getExtras();
directoryName = extrasData.getString("dir");
if(!directoryName.equals(rootDir)) {
int index = directoryName.lastIndexOf("/");
directoryName = directoryName.substring(0, index);
}
// Set buttons
backButton = (Button)findViewById(R.id.button1);
getWindow().setTitleColor(Color.rgb(65, 183, 216));
backButton.getBackground().setColorFilter(Color.rgb(65, 183, 216),
PorterDuff.Mode.SRC_IN);
backButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DateListActivity.this.finish(); // Go back to Get screen
}
});
listDirectory(directoryName);
}
catch(Exception e) {
Log.e("ERROR", "ERROR: " + e.toString());
e.printStackTrace();
}
}
- 22-31行目
ユーザーがButton
をクリックした際、一つ前の画面へ戻るように設定しています。
リストの作成
基本的には
command.cgi
を使用して、コンテンツリストを取得するのと同様の処理になります。
ファイル名に変わり、日付を重複しないように取得しています。
DateListActivity.java (3)
public void listRootDirectory() {
directoryName = rootDir;
listDirectory(directoryName);
}
public void listDirectory(String dir) {
// Prepare command directory path
currentDirText = (TextView)findViewById(R.id.textView1);
currentDirText.setText(dir + "/");
// 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];
String cmddir = "/" + dir;
ArrayList <NameValuePair> httpParams = new ArrayList <NameValuePair> ();
httpParams.add(new BasicNameValuePair("DIR", cmddir));
cmddir = URLEncodedUtils.format (httpParams, "UTF-8" );
Set <Integer> dates = new HashSet <Integer>();
String files = FlashAirRequest
.getString("http://flashair/command.cgi?op=100&" + cmddir);
String[] allFiles = files.split("([,\n])"); // split by newline or comma
for(int i = 2; i < allFiles.length; i= i + 6) {
if(allFiles[i].contains(".")) {
if( (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpg"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpeg"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpe"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".png"))){
// Image file
Integer date = Integer.parseInt(allFiles[i+3]);
dates.add(date);
}
}
}
// Get Title
ArrayList<Map<String, Object>> data = new ArrayList<Map<String, Object>>();
for (Integer date : dates) {
Map<String, Object> entry = new HashMap<String, Object>();
String dataStr = FlashAirRequest.getString("http://flashair/"
+ dir + "/" + getDate(date, "") + ".txt");
String[] dataStrAry = dataStr.split("([\n])"); // split by newline or comma
entry.put("date", getDate(date, "/"));
if (dataStrAry.length >= 2) {
entry.put("title", dataStrAry[0]);
}
data.add(entry);
}
// Set the file list to a widget
listAdapter = new SimpleAdapter(DateListActivity.this,
data,
R.layout.list_view_item,
new String[]{"date", "title"},
new int[]{R.id.textView1, R.id.textView2});
return listAdapter;
}
@Override
protected void onPostExecute(ListAdapter listAdapter) {
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(DateListActivity.this);
}
}.execute(dir);
-
22-37行目
各ファイルの日付を取得しています。 -
40-51行目
取得した日付をもとにパスを作成し、ファイルをダウンロードします。
タイトルは、 YYYYMMDD.txt という形式で画像と同じフォルダに保存され、このファイルには1行目にタイトル、2行目にメモの情報が保存されています。
getDate()
は、command.cgi
のop=100
で返却された日付形式(16ビット整数を10進数表記)から、ファイル名用の日付文字列(YYYYMMDD)を生成する関数です。 -
54-58行目
レイアウトに作成済みの list_view_item.xml を設定し、取得した日付、タイトル、メモも設定しています。
これらは、64行目でListView
にバインドされます。 -
63-68行目
ListView
を作成しています。
作成済みのlistAdapter
設定が設定され、日付とタイトルが view にバインドされています。
作成したViewをレイアウトに設定しています。
クリックリスナーの設定
Android Tutorial 4: サムネイルの表示 と同様の設定を行います。
日付をクリックした場合に、選択した日付の操作画面を表示します。
DateListActivity.java (4)
@Override
public void onItemClick(AdapterView<?> l, View v, int position, long id) {
Object item = l.getItemAtPosition(position);
if(item instanceof Map<?, ?>) {
Map<String, Object> mapItem = (Map<String, Object>) item;
Object selectDate = mapItem.get("date");
// Next button to start new intent to allow day detail
Intent memoEdit = new Intent(getBaseContext(), MemoEditActivity.class);
memoEdit.putExtra("date", selectDate.toString());
memoEdit.putExtra("dir", directoryName);
DateListActivity.this.startActivity(memoEdit);
}
}
- 4-6行目
Map
オブジェクトを使用して、ListView
で選択された日付を取得しています。
再表示時の設定
次の画面から戻ってきた場合など、画面が再表示された際にもリストが更新されるように設定しておきます。
DateListActivity.java (5)
@Override
public void onRestart(){
super.onRestart();
listDirectory(directoryName);
}
ファイル名用日付文字列の取得
前述した、16ビット整数を10進数表記の日付形式から、ファイル名用の文字列(YYYYMMDD)を生成する関数です。
DateListActivity.java (6)
public String getDate(Integer date, String sep) {
return String.format("%04d", ((date >> 9) & 0x1FF)+1980)+sep+
String.format("%02d", (date >> 5) & 0xF)+sep+
String.format("%02d", date & 0x1F);
}
} // End DateListActivity class
操作画面の作成
初期化
ビューの宣言、その他のクラス変数の宣言や画面の初期設定を行います。
また、
onCreate(Bundle savedInstanceState)
をオーバーライドし、アクティビティクラスの初期化も行います。
リストの初期設定や
Button
のクリックリスナーも設定します。
MemoEditActivity.java (1)
public class MemoEditActivity extends Activity {
TextView dateText;
TextView currentDirText;
Button backButton;
Button saveButton;
EditText titleField;
EditText memoField;
GridView gridView;
String newTitle = "";
String newMemo = "";
String date;
String rootDir = "DCIM";
String directoryName;
ArrayList<String> fileNamelist;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_memo_edit);
Bundle extrasData = getIntent().getExtras();
date = extrasData.getString("date");
directoryName = extrasData.getString("dir");
getWindow().setTitleColor(Color.rgb(65, 183, 216));
// Set backButton
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) {
MemoEditActivity.this.finish(); // Go back
}
});
// Set saveButton
saveButton = (Button) findViewById(R.id.button2);
saveButton.getBackground().setColorFilter(Color.rgb(65, 183, 216), PorterDuff.Mode.SRC_IN);
saveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getInput();
saveNewTitleMemo();
}
});
// Set titleField and memoField
titleField = (EditText) findViewById(R.id.editTitle);
titleField.setHintTextColor(Color.rgb(65, 183, 216));
memoField = (EditText) findViewById(R.id.editMemo);
memoField.setHintTextColor(Color.rgb(65, 183, 216));
// Set dateText
dateText = (TextView) findViewById(R.id.textView2);
dateText.setText(date);
// Get title and memo
new AsyncTask<String, Void, ArrayList<String>>() {
@Override
protected ArrayList<String> doInBackground(String... params) {
String dir = params[0];
ArrayList<String> rtnAry = new ArrayList<String>();
String files = FlashAirRequest.getString("http://flashair/"
+ dir + "/" + date.replaceAll("/", "") + ".txt");
String[] allFiles = files.split("([\n])"); // split by newline or comma
if (allFiles.length >= 2) {
// File
rtnAry.add(allFiles[0]);
rtnAry.add(allFiles[1]);
}
return rtnAry;
}
@Override
protected void onPostExecute(ArrayList<String> strary) {
// Set the data to a TextView
titleField = (EditText) findViewById(R.id.editTitle);
memoField = (EditText) findViewById(R.id.editMemo);
if (strary.size() > 0) {
titleField.setText(strary.get(0));
memoField.setText(strary.get(1));
} else {
titleField.setText("");
memoField.setText("");
}
}
}.execute(directoryName);
// Set gridView
gridView = (GridView) findViewById(R.id.gridView1);
gridView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
Object item = parent.getItemAtPosition(position);
if (item instanceof Map<?, ?>) {
Map<String, Object> mapItem = (Map<String, Object>) item;
Object downloadFile = mapItem.get("fname");
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(MemoEditActivity.this,
ImageViewActivity.class);
viewImageIntent.putExtra("downloadFile",downloadFile.toString());
viewImageIntent.putExtra("directoryName", directoryName);
MemoEditActivity.this.startActivity(viewImageIntent);
} // Not an image file, do nothing
}
}
});
listDirectory(directoryName);
}
-
37-45行目
save ボタン をタップした際、保存処理が実行されるように設定しています。 -
58-88行目
前画面で選択された日付の、タイトル&メモを取得し画面に設定しています。 -
91-113行目
ファイルをダウンロードし、ダウンロードした画像を表示しした画面を表示します。
サムネイル表示
基本的には今まで行ってきたサムネイルの取得方法と同様になります。
ここでは、
ListView
ではなく、
GridView
を使用してサムネイルを表示しています。
MemoEditActivity.java (2)
public void listDirectory(String dir) {
// Prepare command directory path
currentDirText = (TextView)findViewById(R.id.textView1);
currentDirText.setText(dir + "/");
final ProgressDialog waitDialog;
// Setting ProgressDialog
waitDialog = new ProgressDialog(this);
waitDialog.setMessage("Now downloading...");
waitDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
waitDialog.show();
ArrayList<NameValuePair> httpParams = new ArrayList<NameValuePair>();
httpParams.add(new BasicNameValuePair("DIR", dir));
dir = URLEncodedUtils.format(httpParams, "UTF-8");
// 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>();
fileNamelist = 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(".") && allFiles[i+3].contains(getDate16(date))) {
if( (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpg"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpeg"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpe"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".png"))){
// Image file
fileNames.add(allFiles[i]);
}
}
}
// Get thumbnails
ArrayList<Map<String, Object>> data = new ArrayList<Map<String, Object>>();
for (int i = 0; i < fileNames.size(); i++) {
String url = "";
url = "http://flashair/thumbnail.cgi?" + directoryName + "/" + fileNames.get(i);
Map<String, Object> entry = new HashMap<String, Object>();
Bitmap thumbnail = null;
BitmapDrawable drawnIcon = null;
if ((url.toLowerCase(Locale.getDefault()).endsWith(".jpg"))
|| (url.toLowerCase(Locale.getDefault()).endsWith(".jpeg"))) {
thumbnail = FlashAirRequest.getBitmap(url);
drawnIcon = new BitmapDrawable(getResources(), thumbnail);
}
if(thumbnail == null) {
entry.put("thmb", R.drawable.ic_launcher);
}
else {
entry.put("thmb", drawnIcon);
}
entry.put("fname", fileNames.get(i)); // Put file name onto the map
data.add(entry);
}
// Set the file list to a widget
SimpleAdapter listAdapter = new SimpleAdapter(
MemoEditActivity.this, data, R.layout.grid_view_item,
new String[] { "thmb", "fname" },
new int[] {R.id.imageView1, android.R.id.text1 });
listAdapter.setViewBinder(new CustomViewBinder());
return listAdapter;
}
@Override
protected void onPostExecute(ListAdapter adapter) {
waitDialog.dismiss();
gridView.setAdapter(adapter);
}
}.execute(dir);
}
-
20-36行目
対象となっている日付のファイル名を取得しています。
getDate16()
は、対象となっている日付文字列(YYYY/MM/DD)から、16ビット整数を10進数表記した日付形式の日付文字列を生成する関数です。 -
62-66行目
レイアウトに作成済みの grid_view_item.xml を設定し、取得したサムネイルとファイル名も設定しています。 これらは、68行目でGridView
にバインドされます。 -
72-75行目
日付とタイトルとメモが view にバインドされています。
作成したViewをレイアウトに設定しています。
ViewBinderの作成
カスタムの
ViewBinder
を作成します。
GridView
を使用してサムネイルを表示するのに使用します。
MemoEditActivity.java (3)
class CustomViewBinder implements ViewBinder {
@Override
public boolean setViewValue(View view, Object obj, String text) {
if((view instanceof ImageView) && (obj instanceof Drawable)) {
ImageView imageView = (ImageView) view;
BitmapDrawable thumbnail = (BitmapDrawable) obj;
imageView.setImageDrawable((Drawable)thumbnail);
return true;
}
return false;
}
}
アップロード
タイトル&メモを保存するために、以下のコマンドを使用します。
- アップロードは、
upload.cgi
を使用して以下の手順で行います。- CONFIGファイルに、
UPLOAD=1
を書き込む - FlashAirを再起動する
- FlashAirに無線LAN接続する
WRITEPROTECT
コマンドで、SDメモリカードホスト機器からの書き込みを禁止するUPDIR
コマンドで、アップロード先フォルダを設定するFTIME
コマンドで、ファイルの作成日時を設定する- upload.cgiにファイルをPOSTし、ファイルをアップロードする
- CONFIGファイルに、
upload.cgi
をPOSTする
upload()
を前項
Android Tutorial 3: コンテンツのダウンロード で作成した
FlashAirRequest.java に追加します。
FlashAirRequest.java
static public String upload(String command, String filename, String saveString) {
String result = "";
final String boundary = "========================";
try {
URL url = new URL(command);
HttpURLConnection httpUrlCon = (HttpURLConnection)url.openConnection();
httpUrlCon.setDoInput(true);
httpUrlCon.setDoOutput(true);
httpUrlCon.setUseCaches(false);
httpUrlCon.setRequestMethod("POST");
httpUrlCon.setRequestProperty("Charset", "UTF-8");
httpUrlCon.setRequestProperty("Content-Type",
"multipart/form-data;boundary="+ boundary);
DataOutputStream ds = new DataOutputStream(httpUrlCon.getOutputStream());
ds.writeBytes("--" + boundary + "\r\n");
ds.writeBytes("Content-Disposition: form-data; name=\"upload.cgi\";
filename=\"" + filename +"\""+"\r\n");
ds.writeBytes( "\r\n" );
ds.write(saveString.getBytes("UTF-8"));
ds.writeBytes( "\r\n" );
ds.writeBytes("--" + boundary + "--" + "\r\n");
ds.flush();
ds.close();
if(httpUrlCon.getResponseCode() == HttpURLConnection.HTTP_OK){
StringBuffer sb = new StringBuffer();
InputStream is = httpUrlCon.getInputStream();
byte[] data = new byte[1024];
int leng = -1;
while((leng = is.read(data)) != -1) {
sb.append(new String(data, 0, leng));
}
result = sb.toString();
}
} catch (MalformedURLException e) {
Log.e("ERROR", "ERROR: " + e.toString());
e.printStackTrace();
} catch (IOException e) {
Log.e("ERROR", "ERROR: " + e.toString());
e.printStackTrace();
}
return result;
}
- 12-13行目
multipartform-data形式を指定しています。
MemoEditActivity.java (4)
public void saveNewTitleMemo() {
new AsyncTask<String, Void, String>(){
@Override
protected String doInBackground(String... params) {
String parameter = "?WRITEPROTECT=ON&UPDIR=/" + rootDir;
parameter = parameter + "&FTIME=" + getDateTime16();
String filename = date.replaceAll("/", "") + ".txt";
String rtnStr = "";
rtnStr = FlashAirRequest.getString(params[0] + parameter);
if(rtnStr.toUpperCase(Locale.getDefault()).equals("SUCCESS")) {
rtnStr = FlashAirRequest
.upload( params[0], filename, newTitle + "\n" + newMemo);
}
return rtnStr;
}
@Override
protected void onPostExecute(String result) {
if(result.toUpperCase(Locale.getDefault()).indexOf("SUCCESS") >= 0) {
Toast.makeText(MemoEditActivity.this, "Save Completed.",
Toast.LENGTH_LONG).show();
}
}
}.execute("http://flashair/upload.cgi");
}
-
5-9行目
手順の4-6を実行しています。
getDateTime16()
は、現在の日付からファイルの作成日時を指定するための日付文字列を生成する関数です。
日付、時刻をそれぞれ16ビットの16進数として作成し、合わせて32ビットの1つの値として渡しています。 -
11-12行目
作成したupload()
を使用して、タイトル&メモをアップロードしています。
ファイル作成日時用の日付文字列の取得
前述した、現在の日付からファイルの作成日時用の日付文字列を生成する関数です。
MemoEditActivity.java (5)
public String getDateTime16() {
Calendar calendar = Calendar.getInstance();
int year = (calendar.get(Calendar.YEAR) - 1980) << 9;
int month = (calendar.get(Calendar.MONTH) + 1) << 5;
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hours = calendar.get(Calendar.HOUR_OF_DAY) << 11;
int minites = calendar.get(Calendar.MINUTE) << 5;
int seconds = calendar.get(Calendar.SECOND) / 2;
String rtnStr = "0x" + Integer.toHexString(year + month + day)
+ Integer.toHexString(hours + minites + seconds);
return rtnStr;
}
ファイル名用日付文字列の取得
対象となっている日付文字列(YYYY/MM/DD)から、16ビット整数を10進数表記した日付形式の日付文字列を生成する関数です。
MemoEditActivity.java (6)
public String getDate16(String date) {
String rtnStr = "";
try{
int year = (Integer.parseInt(date.substring(0,4)) - 1980) << 9;
int month = (Integer.parseInt(date.substring(5,7))) << 5;
int day = Integer.parseInt(date.substring(8,10));
rtnStr = String.valueOf(year + month + day);
}
catch(Exception e) {
Log.e("ERROR", "ERROR: " + e.toString());
e.printStackTrace();
}
return rtnStr;
}
実行結果
メモ&タイトルを保存してみます。
メモ&タイトルを入力し、Save ボタンをタップして保存します。
Back ボタン をタップし前画面に戻ると、保存した内容が表示されました。
サンプルコード
android_tutorial_07.zip (533KB)
このサイトのサンプルコードは二条項BSDライセンスで提供されています。