FlashAir x Androidアプリ開発ワークショップ コーディングパート
Upcoming SlideShare
Loading in...5
×
 

FlashAir x Androidアプリ開発ワークショップ コーディングパート

on

  • 5,400 views

FlashAir x ...

FlashAir x Androidアプリ開発ワークショップは、FlashAirと連携したAndroidアプリを作ることを目的に、FlashAirにAndroidデバイスから通信するための基本から、開発に役立つ機能や設定をご説明し、実際のコーディングを行うワークショップです。
この資料はコーディングパートで使用したスライドです。

【開催日】2013年9月18日
【講師】あんざいゆき
【主催】株式会社フィックスターズ

FlashAirで使用可能なAPIの一覧や開発チュートリアル、Q&AフォーラムはFlashAirデベロッパーズにて公開しています。> https://flashair-developers.com

Statistics

Views

Total Views
5,400
Views on SlideShare
1,009
Embed Views
4,391

Actions

Likes
1
Downloads
12
Comments
0

27 Embeds 4,391

http://y-anz-m.blogspot.jp 1992
https://flashair-developers.com 1188
https://www.flashair-developers.com 652
http://veneridae19.rssing.com 308
http://cloud.feedly.com 115
http://y-anz-m.blogspot.com 34
https://test.flashair-developers.com 26
http://y-anz-m.blogspot.tw 13
http://localhost 11
http://test.flashair-developers.com 9
http://y-anz-m.blogspot.kr 8
https://www.google.co.jp 6
http://y-anz-m.blogspot.ru 5
http://y-anz-m.blogspot.com.br 3
http://digg.com 3
http://reader.aol.com 2
http://www.newsblur.com 2
http://summary 2
http://www.feedspot.com 2
http://y-anz-m.blogspot.fr 2
http://y-anz-m.blogspot.in 2
http://y-anz-m.blogspot.com.es 1
http://cloud.feedly.com&_=1381567122248 HTTP 1
http://y-anz-m.blogspot.com.au 1
http://y-anz-m.blogspot.it 1
http://webcache.googleusercontent.com 1
http://www.google.co.jp 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

FlashAir x Androidアプリ開発ワークショップ コーディングパート FlashAir x Androidアプリ開発ワークショップ コーディングパート Presentation Transcript

  • FlashAir対応 Androidアプリ開発 2013年9月18日 あんざいゆき (株式会社ウフィカ)
  • Steps • FlashAirにアクセスする • FlashAirに保存されているファイルの一覧を表 示する • FlashAirに保存されている画像ファイルのサム ネイルを表示する • FlashAirに保存されている画像ファイルをダウ ンロードする
  • プロジェクトの用意
  • • [File] - [New] - [Android Application Project] • Application Name: FlashAirSample • Project Name: FlashAirSample • Package Name: com.example.flashairsample • Mininum Required SDK:API 9 • 以降はすべてデフォルト設定
  • FlashAirにアクセスする
  • FlashAirにアクセスする(1) • FlashAirにアクセスするには、Androidデバイス がFlashAirのアクセスポイントに接続している必 要があります • 設定アプリのWiFi接続画面を開くメニューを用意 しましょう
  • res/menu/main.xml <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/action_wifi_settings" android:showAsAction="never" android:title="@string/action_wifi_settings"/> </menu> res/values/strings.xml <?xml version="1.0" encoding="utf-8"?> <resources> ... <string name="action_wifi_settings">WiFi Settings</string> </resources>
  • MainActivity.java public class MainActivity extends Activity { ... @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); switch (itemId) { case R.id.action_wifi_settings: Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS); startActivity(intent); return true; } return super.onOptionsItemSelected(item); } }
  • FlashAirにアクセスする(2) • FlashAirにはHTTPでアクセスするため、アプリ にはInternetパーミッションが必要です • デフォルト SSID: flashair_xxxxx • デフォルト Pass: 12345678
  • AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.flashairsample" android:versionCode="1" android:versionName="1.0" > <uses-permission android:name="android.permission.INTERNET"/> ... </manifest>
  • FlashAirにアクセスする(3) • ライブラリプロジェクトを利用する • FlashAirDev • https://github.com/yanzm/FlashAirDev • git clone https://github.com/yanzm/FlashAirDev.git • [File] - [Import] - [Android] - [Existing Android Code Into Workspace] • Select root directory: • FlashAirDevフォルダを指定
  • FlashAirにアクセスする(4) • FlashAirSampleのプロパティを開く • [Android] - [Library] - [Add] • FlashAirDev を選択
  • FlashAirにアクセスする(5) • フォルダ内のファイル数を取得する • http://flashair/command.cgi?op=101&DIR=[path] • https://www.flashair-developers.com/ja/ documents/api/commandcgi/#101 • レスポンスはファイル数(数字)
  • res/layout/activity_main.xml <RelativeLayout 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" ... tools:context=".MainActivity" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> </RelativeLayout>
  • res/menu/main.xml <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/action_reload" android:showAsAction="ifRoom" android:title="@string/action_reload"/> ... </menu> res/values/strings.xml <?xml version="1.0" encoding="utf-8"?> <resources> ... <string name="action_reload">Reload</string> <string name="image_count_format">%1$d images</string> </resources>
  • MainActivity.java public class MainActivity extends Activity { ... @Override public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); switch (itemId) { ... case R.id.action_reload: String dir = "/DCIM"; getFileCount(dir); return true; } return super.onOptionsItemSelected(item); } ... }
  • MainActivity.java public class MainActivity extends Activity { ... private void getFileCount(final String dir) { new AsyncTask<Void, Void, Integer>() { @Override protected Integer doInBackground(Void... params) { return FlashAirUtils.getFileCount(dir); } @Override protected void onPostExecute(Integer result) { TextView tv = (TextView) findViewById(R.id.textView1); tv.setText(getString(R.string.image_count_format, result)); } }.execute(); } }
  • FlashAirUtils.java public class FlashAirUtils { public static final String BASE = "http://flashair/"; public static final String COMMAND = BASE + "command.cgi?"; public static final String FILE_COUNT = COMMAND + "op=101&DIR="; public static int getFileCount(String dir) { try { String result = Utils.accessToFlashAir(FILE_COUNT + dir); return Integer.parseInt(result); } catch (NumberFormatException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return -1; } ... } ファイル数取得
  • Utils.java public class Utils { public static String accessToFlashAir(String uri) throws IOException { URL url = new URL(uri); HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); String result = null; try { InputStream in = new BufferedInputStream(urlConnection.getInputStream()); result = inputStreamToString(in); in.close(); } finally { urlConnection.disconnect(); } return result; } ... } HTTPアクセス用
  • Utils.java public class Utils { ... private static String inputStreamToString(InputStream stream) throws IOException { Reader reader = new InputStreamReader(stream, "UTF-8"); StringBuilder sb = new StringBuilder(); char[] buffer = new char[1024]; int num; while (0 < (num = reader.read(buffer))) { sb.append(buffer, 0, num); } return sb.toString(); } ... } HTTPアクセス用
  • FlashAirに保存されている ファイルの一覧を表示する
  • ファイルの一覧を表示する(1) • DCIMフォルダ内のファイル一覧を取得する • http://flashair/command.cgi?op=100&DIR=[path] • https://www.flashair-developers.com/ja/ documents/api/commandcgi/#100 • レスポンスは<ディレクトリ>,<ファイル名>,<ファイル サイズ>,<属性>,<日付>,<時間> • 例)/DCIM,100__TSB,0,16,9944,129 • ファイル名やディレクトリ名に,が入ってる場合もあり うる!
  • ファイルの一覧を表示する(2) • ファイルサイズはバイト単位 • 属性は16ビット整数の10進数表記 • ビット5 : アーカイブ • ビット4 : ディレクトリ • ビット3 : ボリューム • ビット2 : システムファイル • ビット1 : 隠しファイル • ビット0 : 読み取り専用
  • ファイルの一覧を表示する(3) • 日付は16ビット整数の10進数表記 • ビット15-9 : 1980年を0とした値 • ビット8-5 : 月(1∼12) • ビット4-0 : 日(1∼31) • 時刻は16ビット整数の10進数表記 • ビット15-11 : 時 • ビット10-5 : 分 • ビット4-0 : 秒/2
  • FlashAirFileInfo.java public class FlashAirFileInfo { public FlashAirFileInfo(String info, String dir) { int start; int end; start = info.lastIndexOf(","); int time = Integer.parseInt(info.substring(start + 1).trim()); end = start; start = info.lastIndexOf(",", end - 1); int date = Integer.parseInt(info.substring(start + 1, end).trim()); end = start; start = info.lastIndexOf(",", end - 1); mAttribute = Integer.parseInt(info.substring(start + 1, end).trim()); end = start; start = info.lastIndexOf(",", end - 1); mSize = info.substring(start + 1, end); end = start; start = info.indexOf(",", dir.length()); mFileName = info.substring(start + 1, end); ... ファイル情報用のクラス
  • FlashAirFileInfo.java ... mDir = dir; int year = ((date >> 9) & 0x0000007f) + 1980; int month = (date >> 5) & 0x0000000f - 1; int day = (date) & 0x0000001f; int hourOfDay = (time >> 11) & 0x0000001f; int minute = (time >> 5) & 0x0000003f; int second = ((time) & 0x0000001f) * 2; mCalendar = Calendar.getInstance(); mCalendar.set(year, month, day, hourOfDay, minute, second); } public String mDir; public String mFileName; public String mSize; public int mAttribute; public Calendar mCalendar; ... ファイル情報用のクラス
  • FlashAirFileInfo.java ... public static final int ATTR_MASK_ARCHIVE = 0x00000020; public static final int ATTR_MASK_DIRECTORY = 0x00000010; public static final int ATTR_MASK_VOLUME = 0x00000008; public static final int ATTR_MASK_SYSTEM_FILE = 0x00000004; public static final int ATTR_MASK_HIDDEN_FILE = 0x00000002; public static final int ATTR_MASK_READ_ONLY = 0x00000001; public boolean isDirectory() { return (mAttribute & ATTR_MASK_DIRECTORY) > 0; } @Override public String toString() { return "DIR=" + mDir + " FILENAME=" + mFileName + " SIZE=" + mSize + " ATTRIBUTE=" + mAttribute + " DATE=" + DateFormat.format("yyyy-MM-dd kk:mm:ss", mCalendar); } } ファイル情報用のクラス
  • FlashAirUtils.java public class FlashAirUtils { ... public static List<FlashAirFileInfo> getFileList(String dir) { try { String result = Utils.accessToFlashAir(FILE_LIST + dir); if (TextUtils.isEmpty(result)) { return null; } ArrayList<FlashAirFileInfo> list = new ArrayList<FlashAirFileInfo>(); for (String line : result.split("n")) { if (TextUtils.isEmpty(line)) { continue; } if (line.split(",").length < 6) { continue; } FlashAirFileInfo info = new FlashAirFileInfo(line, dir); list.add(info); } return list; ... ファイル情報取得
  • FlashAirUtils.java ... } catch (IOException e) { e.printStackTrace(); } return null; } ファイル情報取得
  • res/layout/activity_main.xml <RelativeLayout 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" ... tools:context=".MainActivity" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> <ListView android:id="@+id/listView1" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/textView1" /> </RelativeLayout> 一覧用ListView追加
  • MainActivity.java public class MainActivity extends Activity { ... @Override public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); switch (itemId) { ... case R.id.action_reload: String dir = "/DCIM"; getFileCount(dir); getFileList(dir); return true; } return super.onOptionsItemSelected(item); } ... } ファイル一覧取得
  • MainActivity.java public class MainActivity extends Activity { ... private void getFileList(final String dir) { new AsyncTask<Void, Void, List<FlashAirFileInfo>>() { @Override protected List<FlashAirFileInfo> doInBackground(Void... params) { return FlashAirUtils.getFileList(dir); } @Override protected void onPostExecute(List<FlashAirFileInfo> result) { ListView lv = (ListView) findViewById(R.id.listView1); lv.setAdapter(new FileListAdapter(MainActivity.this, result)); } }.execute(); } public class FileListAdapter extends ArrayAdapter<FlashAirFileInfo> { public FileListAdapter(Context context, List<FlashAirFileInfo> data) { super(context, android.R.layout.simple_list_item_1, data); } } } ファイル一覧をListViewにセット
  • FlashAirに保存されている 画像ファイルのサムネイル を表示する
  • サムネイルを表示する(1) • 画像ファイルのサムネイルを取得する • http://flashair/thumbnail.cgi?[path] • 例)http://flashair/thumbnail.cgi?/DCIM/IMG_xxx.jpg • https://www.flashair-developers.com/ja/documents/ api/thumbnailcgi/ • EXIF規格で定められているサムネイル画像 • JPEG(image/jpeg)形式 • JPEGでない場合、EXIF規格で定められたサムネイル画像 がない場合、404 Not Found が返る
  • サムネイルを表示する(2) • ListViewにサムネイルを表示する • Volleyを利用する • Androidのネットワーク処理用のライブラリ • https://android.googlesource.com/platform/ frameworks/volley/ • http://y-anz-m.blogspot.jp/2013/05/google- io-2013-android-volley-easy-fast.html • 通信処理が組み込まれた ImageView である NetworkImageView が用意されている
  • サムネイルを表示する(3) • VolleyのNetworkImagView • <com.android.volley.toolbox.NetworkImageView> • setImageUrl(String url, ImageLoader loader)
  • • Volleyはライブラリプロジェクト • git clone https://android.googlesource.com/ platform/frameworks/volley • [File] - [Import] - [Android] - [Existing Android Code Into Workspace] • Select root directory: • volleyフォルダを指定 サムネイルを表示する(4)
  • • FlashAirSampleのプロパティを開く • [Android] - [Library] - [Add] • volley を選択 サムネイルを表示する(5) Volleyが選択肢に出てこない場合は、 VolleyプロジェクトのIs Libraryに チェックが付いてることを確認
  • res/layout/list_row.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="horizontal" > <com.android.volley.toolbox.NetworkImageView android:id="@+id/imageView1" android:layout_width="100dp" android:layout_height="80dp" android:src="@drawable/ic_launcher" /> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Medium Text" android:textAppearance="?android:attr/textAppearanceMedium" /> </LinearLayout> リスト用レイアウト
  • MainActivity.java public class MainActivity extends Activity { private RequestQueue mQueue; private ImageLoader mImageLoader; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mQueue = Volley.newRequestQueue(getApplicationContext()); mImageLoader = new ImageLoader(mQueue, new BitmapCache()); } ... Volleyの準備
  • MainActivity.java ... public class BitmapCache implements ImageCache { private LruCache<String, Bitmap> mCache; public BitmapCache() { int maxSize = 5 * 1024 * 1024; // 5MB mCache = new LruCache<String, Bitmap>(maxSize) { @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight(); } }; } @Override public Bitmap getBitmap(String url) { return mCache.get(url); } @Override public void putBitmap(String url, Bitmap bitmap) { mCache.put(url, bitmap); } } Volleyの準備
  • MainActivity.java public class MainActivity extends Activity { ... public class FileListAdapter extends ArrayAdapter<FlashAirFileInfo> { LayoutInflater mInflater; public FileListAdapter(Context context, List<FlashAirFileInfo> data) { super(context, 0, data); mInflater = LayoutInflater.from(context); } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(R.layout.list_row, parent, false); } FlashAirFileInfo item = getItem(position); TextView tv = (TextView) convertView.findViewById(R.id.textView1); tv.setText(item.mFileName); ... リスト用Adapterを拡張
  • MainActivity.java ... NetworkImageView niv = (NetworkImageView) convertView .findViewById(R.id.imageView1); if (item.mFileName.endsWith(".jpg") || item.mFileName.endsWith(".jpeg")) { niv.setImageUrl( FlashAirUtils.getThumbnailUrl(item.mDir, item.mFileName), mImageLoader); } else { niv.setImageUrl(null, mImageLoader); } return convertView; } } } NetworkImageViewにURLをセット
  • FlashAirUtils.java public class FlashAirUtils { public static final String BASE = "http://flashair/"; public static final String THUMBNAIL = BASE + "thumbnail.cgi?"; public static String getThumbnailUrl(String dir, String fileName) { return THUMBNAIL + dir + "/" + fileName; } ... }
  • FlashAirに保存されている画像 ファイルをダウンロードする
  • 画像をダウンロードする(1) • 画像ファイルを取得する • http://flashair/[path] • 例)http://flashair/DCIM/IMG_xxx.jpg • DownloadManager を利用する • http://developer.android.com/reference/android/ app/DownloadManager.html
  • 画像をダウンロードする(2) • DownloadManager • getSystemService(Context.DOWNLOAD_SERVICE) でイ ンスタンスを取得 • Request request = new DownloadManager.Request(uri) でダウンロードリクエストを作成 • downloadManager.enqueue(request) でダウンロードリク エストを追加 • SDカードにダウンロードする場合はパーミッションが必要
  • AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.flashairsample" android:versionCode="1" android:versionName="1.0" > <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ... </manifest>
  • MainActivity.java public class MainActivity extends Activity implements OnItemClickListener { ... private void getFileList(final String dir) { new AsyncTask<Void, Void, List<FlashAirFileInfo>>() { @Override protected List<FlashAirFileInfo> doInBackground(Void... params) { return FlashAirUtils.getFileList(dir); } @Override protected void onPostExecute(List<FlashAirFileInfo> result) { ListView lv = (ListView) findViewById(R.id.listView1); lv.setAdapter(new FileListAdapter(MainActivity.this, result)); lv.setOnItemClickListener(MainActivity.this); } }.execute(); } ... リストにリスナーをセット
  • MainActivity.java public class MainActivity extends Activity implements OnItemClickListener { ... @Override public void onItemClick(AdapterView<?> adapter, View v, int position, long l) { FlashAirFileInfo info = (FlashAirFileInfo) adapter .getItemAtPosition(position); File path = Environment .getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM); File file = new File(path, info.mFileName); if (!file.exists()) { startDownload(info); return; } openDownloadedFile(file.toString()); } ... リストにリスナーをセット
  • MainActivity.java ... private void openDownloadedFile(String filePath) { MediaScannerConnection.scanFile(this, new String[] { filePath }, null, new MediaScannerConnection.OnScanCompletedListener() { public void onScanCompleted(String path, Uri uri) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(uri); startActivity(intent); } }); } private void startDownload(FlashAirFileInfo info) { Uri uri = FlashAirUtils.getFileUri(info.mDir, info.mFileName); DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); DownloadManager.Request request = new DownloadManager.Request(uri); request.allowScanningByMediaScanner(); request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DCIM, info.mFileName); manager.enqueue(request); } ...
  • MainActivity.java ... @Override protected void onResume() { super.onResume(); IntentFilter filter = new IntentFilter( DownloadManager.ACTION_DOWNLOAD_COMPLETE); registerReceiver(receiver, filter); } @Override protected void onPause() { super.onPause(); unregisterReceiver(receiver); } ...
  • MainActivity.java ... BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); if (id > 0) { DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); Uri fileUri = manager.getUriForDownloadedFile(id); openDownloadedFile(fileUri.getPath()); } } }; }