<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
+ <component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="PLATFORM" />
+ <option name="disableWrapperSourceDistributionNotification" value="true" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="1.8 (2)" />
+Theta 'Thought' Shutter : An EEG shutter PlugIn for the THETA.
+
defaultConfig {
applicationId "jp.osdn.gokigen.thetathoughtshutter"
- minSdkVersion 24
+ minSdkVersion 25
targetSdkVersion 30
- versionCode 10000
- versionName "1.0.00"
+ versionCode 10000000
+ versionName "10.00.0001"
}
buildTypes {
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="jp.osdn.gokigen.thetathoughtshutter">
+ <uses-feature android:name="com.theta360.receptor.v" android:required="true"/>
+ <uses-feature android:name="com.theta360.receptor.z1" android:required="true"/>
+
+ <uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.BLUETOOTH" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:allowBackup="false"
override fun onKeyUp(keyCode: Int, event: KeyEvent?)
{
notificationLedBlink(LedTarget.LED3, LedColor.BLUE, 1000)
+ //notificationLedBlink(LedTarget.LED4, LedColor.CYAN, 1000)
}
override fun onKeyLongPress(keyCode: Int, event: KeyEvent?)
{
- if (keyCode == KeyReceiver.KEYCODE_MEDIA_RECORD)
+ if (keyCode == KeyReceiver.KEYCODE_MEDIA_RECORD) // Modeボタン
+ {
+
+ }
+ if (keyCode == KeyReceiver.KEYCODE_CAMERA) // Shutterボタン
+ {
+
+ }
+ if (keyCode == KeyReceiver.KEYCODE_FUNCTION) // Fnボタン (Z1のみ)
+ {
+
+ }
+ if (keyCode == KeyReceiver.KEYCODE_WLAN_ON_OFF) // Wirelessボタン
{
}
super.onPause()
}
-}
\ No newline at end of file
+}
+
+//
+// -----------------------------------------------------
+// LED1 : 電源ランプ
+// LED2 : カメラステータス ランプ(レンズとマイクの間)
+// LED3 : ワイヤレスマーク ランプ
+// LED4 : キャプチャーモード (カメラ)
+// LED5 : キャプチャーモード (ムービー)
+// LED6 : キャプチャーモード (LIVEストリーミング)
+// LED7 : ビデオ録画 ランプ
+// LED8 : メモリ警告ランプ
+//
+// BTN1 : 電源ボタン
+// BTN2 : ワイヤレスボタン
+// BTN3 : モードボタン
+// SHUT : シャッターボタン
+// -----------------------------------------------------
+//
+// [制御可能なLED]
+// - LED3~LED8 : カラー : "blue", "green", "red", "cyan", "magenta", "yellow", "white"
+// - ブリンク間隔 : 1~2000 msec
+//
+// [KeyCode]
+// - 27 : Shutter Button
+// - 130 : Mode Button
+// - 284 : Wireless Button
+// - 119 : Fn Button (Z1 Only)
+//
+//
+//
--- /dev/null
+package jp.osdn.gokigen.thetathoughtshutter.bluetooth
+
+interface ITextDataUpdater
+{
+ fun setText(data: String?)
+ fun addText(data: String?)
+ fun showSnackBar(message: String?)
+ fun showSnackBar(rscId: Int)
+ fun enableOperation(isEnable: Boolean)
+}
--- /dev/null
+package jp.osdn.gokigen.thetathoughtshutter.bluetooth
+
+import android.bluetooth.BluetoothAdapter
+
+
+class MyBluetoothAdapter
+{
+ fun getBondedDevices(): List<String>
+ {
+ val s: MutableList<String> = ArrayList()
+ try
+ {
+ val btAdapter = BluetoothAdapter.getDefaultAdapter()
+ val bondedDevices = btAdapter.bondedDevices
+ for (bt in bondedDevices)
+ {
+ s.add(bt.name)
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ return (s)
+ }
+}
--- /dev/null
+package jp.osdn.gokigen.thetathoughtshutter.bluetooth.connection
+
+import android.app.Activity
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothManager
+import android.content.Context
+import android.util.Log
+import jp.osdn.gokigen.thetathoughtshutter.R
+import jp.osdn.gokigen.thetathoughtshutter.utils.SnackBarMessage
+
+class BluetoothDeviceFinder(private val context: Activity, private val scanResult: IBluetoothScanResult) : BluetoothAdapter.LeScanCallback
+{
+
+ companion object
+ {
+ private val TAG = toString()
+ private const val BLE_SCAN_TIMEOUT_MILLIS = 15 * 1000 // 15秒間
+ private const val BLE_WAIT_DURATION = 100 // 100ms間隔
+ }
+ private lateinit var targetDeviceName: String
+ private val messageToShow = SnackBarMessage(context, false)
+ private var foundBleDevice = false
+
+ fun reset()
+ {
+ foundBleDevice = false
+ }
+
+ fun startScan(targetDeviceName: String)
+ {
+ try
+ {
+ this.targetDeviceName = targetDeviceName
+ val btAdapter = BluetoothAdapter.getDefaultAdapter()
+ if (!btAdapter.isEnabled)
+ {
+ // Bluetoothの設定がOFFだった
+ messageToShow.showMessage(R.string.bluetooth_setting_is_off)
+ }
+ // Bluetooth のサービスを取得
+ val btMgr: BluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
+ scanBluetoothDevice(btMgr)
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ private fun scanBluetoothDevice(btMgr: BluetoothManager)
+ {
+ try
+ {
+ // スキャン開始
+ foundBleDevice = false
+ val adapter = btMgr.adapter
+ if (!adapter.startLeScan(this))
+ {
+ // Bluetooth LEのスキャンが開始できなかった場合...
+ Log.v(TAG, "Bluetooth LE SCAN START fail...")
+ messageToShow.showMessage(R.string.bluetooth_scan_start_failure)
+ return
+ }
+ Log.v(TAG, " ----- BT SCAN STARTED ----- ")
+ var passed = 0
+ while (passed < BLE_SCAN_TIMEOUT_MILLIS)
+ {
+ if (foundBleDevice)
+ {
+ // デバイス発見
+ Log.v(TAG, "FOUND DEVICE")
+ break
+ }
+
+ // BLEのスキャンが終わるまで待つ
+ Thread.sleep(BLE_WAIT_DURATION.toLong())
+ passed += BLE_WAIT_DURATION
+ }
+ // スキャンを止める(500ms後に)
+ Thread.sleep(500)
+ adapter.stopLeScan(this)
+ Log.v(TAG, " ----- BT SCAN STOPPED ----- ")
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ Log.v(TAG, "Bluetooth LE SCAN EXCEPTION...")
+ messageToShow.showMessage(R.string.scan_fail_via_bluetooth)
+ }
+ Log.v(TAG, "Bluetooth SCAN STOPPED.")
+ }
+
+ override fun onLeScan(device: BluetoothDevice, rssi: Int, scanRecord: ByteArray?)
+ {
+ try
+ {
+ val btDeviceName = device.name
+ if (btDeviceName != null && btDeviceName.matches(Regex(targetDeviceName)))
+ {
+ // device発見!
+ foundBleDevice = true
+ scanResult.foundBluetoothDevice(device)
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+}
--- /dev/null
+package jp.osdn.gokigen.thetathoughtshutter.bluetooth.connection
+
+import android.bluetooth.BluetoothDevice
+
+interface IBluetoothScanResult
+{
+ fun foundBluetoothDevice(device: BluetoothDevice)
+}
--- /dev/null
+package jp.osdn.gokigen.thetathoughtshutter.bluetooth.connection.eeg
+
+import android.app.Activity
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothSocket
+import android.util.Log
+import jp.osdn.gokigen.thetathoughtshutter.bluetooth.connection.IBluetoothScanResult
+import jp.osdn.gokigen.thetathoughtshutter.bluetooth.connection.BluetoothDeviceFinder
+import jp.osdn.gokigen.thetathoughtshutter.brainwave.BrainwaveFileLogger
+import jp.osdn.gokigen.thetathoughtshutter.brainwave.IBrainwaveDataReceiver
+import java.io.ByteArrayOutputStream
+import java.io.InputStream
+import java.util.*
+import kotlin.experimental.and
+
+
+class MindWaveConnection(private val context : Activity, private val dataReceiver: IBrainwaveDataReceiver) : IBluetoothScanResult
+{
+ companion object
+ {
+ private val TAG = toString()
+ }
+
+ private val deviceFinder: BluetoothDeviceFinder = BluetoothDeviceFinder(context, this)
+ private var fileLogger: BrainwaveFileLogger? = null
+ private var foundDevice = false
+ private var loggingFlag = false
+
+ fun connect(deviceName: String, loggingFlag: Boolean = false)
+ {
+ Log.v(TAG, " BrainWaveMobileCommunicator::connect() : $deviceName Logging : $loggingFlag")
+ try
+ {
+ this.loggingFlag = loggingFlag
+
+ // Bluetooth のサービスを取得、BLEデバイスをスキャンする
+ foundDevice = false
+ deviceFinder.reset()
+ deviceFinder.startScan(deviceName)
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ private fun parseReceivedData(data: ByteArray)
+ {
+ // 受信データブロック1つ分
+ try
+ {
+ if (data.size <= 3)
+ {
+ // ヘッダ部しか入っていない...無視する
+ return
+ }
+ val length = data[2]
+ if (data.size < length + 2)
+ {
+ // データが最小サイズに満たない...無視する
+ return
+ }
+ if (data.size == 8 || data.size == 9)
+ {
+ var value: Int = (data[5] and 0xff.toByte()) * 256 + (data[6] and 0xff.toByte())
+ if (value > 32768)
+ {
+ value -= 65536
+ }
+ dataReceiver.receivedRawData(value)
+ return
+ }
+ dataReceiver.receivedSummaryData(data)
+
+ // ファイルにサマリーデータを出力する
+ fileLogger?.outputSummaryData(data)
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ private fun serialCommunicationMain(btSocket: BluetoothSocket)
+ {
+ var inputStream: InputStream? = null
+ try
+ {
+ btSocket.connect()
+ inputStream = btSocket.inputStream
+ }
+ catch (e: Exception)
+ {
+ Log.e(TAG, "Fail to accept.", e)
+ }
+ if (inputStream == null)
+ {
+ return
+ }
+ if (loggingFlag)
+ {
+ try
+ {
+ // ログ出力を指示されていた場合...ファイル出力クラスを作成しておく
+ fileLogger = BrainwaveFileLogger()
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ // シリアルデータの受信メイン部分
+ var previousData = 0xff.toByte()
+ val outputStream = ByteArrayOutputStream()
+ while (foundDevice)
+ {
+ try
+ {
+ val data: Int = inputStream.read()
+ val byteData = (data and 0xff).toByte()
+ if (previousData == byteData && byteData == 0xaa.toByte())
+ {
+ // 先頭データを見つけた。 (0xaa 0xaa がヘッダ)
+ parseReceivedData(outputStream.toByteArray())
+ outputStream.reset()
+ outputStream.write(0xaa)
+ outputStream.write(0xaa)
+ }
+ else
+ {
+ outputStream.write(byteData.toInt())
+ }
+ previousData = byteData
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+ try
+ {
+ btSocket.close()
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ override fun foundBluetoothDevice(device: BluetoothDevice)
+ {
+ try
+ {
+ if (foundDevice)
+ {
+ // デバイスがすでに見つかっている
+ Log.v(TAG, " ALREADY FIND BLUETOOTH DEVICE. : $device.name")
+ return
+ }
+ foundDevice = true
+ val btSocket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"))
+ val thread = Thread {
+ try
+ {
+ serialCommunicationMain(btSocket)
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+ if (btSocket != null)
+ {
+ thread.start()
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+package jp.osdn.gokigen.thetathoughtshutter.brainwave
+
+import android.util.Log
+import java.util.*
+
+
+class BrainwaveDataHolder(maxBufferSize: Int) : IBrainwaveDataReceiver
+{
+ private val TAG = toString()
+
+ private var valueBuffer: IntArray
+ private var currentSummaryData = BrainwaveSummaryData()
+ private var maxBufferSize = 0
+ private var currentPosition = 0
+ private var bufferIsFull = false
+
+ init
+ {
+ this.maxBufferSize = maxBufferSize
+ valueBuffer = IntArray(maxBufferSize)
+ }
+
+ override fun receivedRawData(value: Int)
+ {
+ //Log.v(TAG, " receivedRawData() : " + value);
+ try {
+ valueBuffer[currentPosition] = value
+ currentPosition++
+ if (currentPosition == maxBufferSize) {
+ currentPosition = 0
+ bufferIsFull = true
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+
+ override fun receivedSummaryData(data: ByteArray?)
+ {
+ if (data != null)
+ {
+ if (!currentSummaryData.update(data))
+ {
+ // parse failure...
+ Log.v(TAG, " FAIL : PARSE EEG SUMMARY DATA (" + data.size + ")")
+ }
+ }
+ }
+
+ fun getSummaryData(): BrainwaveSummaryData
+ {
+ return currentSummaryData
+ }
+
+ fun getValues(size: Int): IntArray?
+ {
+ var replyData: IntArray? = null
+ try {
+ var endPosition = currentPosition - 1
+ if (currentPosition > size) {
+ return Arrays.copyOfRange(valueBuffer, endPosition - size, endPosition)
+ }
+ if (!bufferIsFull) {
+ return Arrays.copyOfRange(valueBuffer, 0, endPosition)
+ }
+ if (currentPosition == 0) {
+ endPosition = maxBufferSize - 1
+ return Arrays.copyOfRange(valueBuffer, endPosition - size, endPosition)
+ }
+ val remainSize = size - (currentPosition - 1)
+ val size0: IntArray = Arrays.copyOfRange(valueBuffer, 0, currentPosition - 1)
+ val size1: IntArray = Arrays.copyOfRange(
+ valueBuffer,
+ maxBufferSize - 1 - remainSize, maxBufferSize - 1
+ )
+ replyData = IntArray(size)
+ System.arraycopy(size1, 0, replyData, 0, size1.size)
+ System.arraycopy(size0, 0, replyData, size1.size, size0.size)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ return replyData
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package jp.osdn.gokigen.thetathoughtshutter.brainwave
+
+import android.os.Environment
+import jp.osdn.gokigen.thetathoughtshutter.utils.SimpleLogDumper
+import java.io.File
+import java.io.FileOutputStream
+import java.text.SimpleDateFormat
+import java.util.*
+
+
+class BrainwaveFileLogger(prefix : String = "TTShut", private val loggingSize : Int = 36)
+{
+ private var outputStream: FileOutputStream? = null
+ init
+ {
+ try
+ {
+ val fileNamePrefix = "${prefix}_EEG"
+ val calendar: Calendar = Calendar.getInstance()
+ val extendName: String = SimpleDateFormat("yyyyMMdd-HHmmss", Locale.getDefault()).format(calendar.time)
+ @Suppress("DEPRECATION") val directoryPath: String = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path.toString() + "/"
+ val outputFileName = fileNamePrefix + "_" + extendName + ".bin"
+ val filepath: String = File(directoryPath.toLowerCase(Locale.getDefault()), outputFileName.toLowerCase(Locale.getDefault())).path
+ outputStream = FileOutputStream(filepath)
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ fun outputSummaryData(data: ByteArray)
+ {
+ try
+ {
+ SimpleLogDumper.dumpBytes("RX [" + data.size + "] ", data)
+ if (outputStream != null)
+ {
+ if (data.size >= loggingSize)
+ {
+ outputStream?.write(data, 0, loggingSize)
+ outputStream?.flush()
+ }
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+package jp.osdn.gokigen.thetathoughtshutter.brainwave
+
+import kotlin.experimental.and
+
+class BrainwaveSummaryData
+{
+ // 3-byte value : delta (0.5 - 2.75Hz), theta (3.5 - 6.75Hz), low-alpha (7.5 - 9.25Hz), high-alpha (10 - 11.75Hz), low-beta (13 - 16.75Hz), high-beta (18 - 29.75Hz), low-gamma (31 - 39.75Hz), and mid-gamma (41 - 49.75Hz).
+ private var delta = 0
+ private var theta = 0
+ private var lowAlpha = 0
+ private var highAlpha = 0
+ private var lowBeta = 0
+ private var highBeta = 0
+ private var lowGamma = 0
+ private var midGamma = 0
+ private var poorSignal = 0
+ private var attention = 0
+ private var mediation = 0
+
+ fun update(packet: ByteArray): Boolean
+ {
+ var ret = false
+ try
+ {
+ val length = packet.size
+ if (length < 36)
+ {
+ return ret
+ }
+ poorSignal = packet[4].toInt()
+ delta = (packet[7] and 0xff.toByte()) * 65536 + (packet[8] and 0xff.toByte()) * 256 + (packet[9] and 0xff.toByte())
+ theta = (packet[10] and 0xff.toByte()) * 65536 + (packet[11] and 0xff.toByte()) * 256 + (packet[12] and 0xff.toByte())
+ lowAlpha = (packet[13] and 0xff.toByte()) * 65536 + (packet[14] and 0xff.toByte()) * 256 + (packet[15] and 0xff.toByte())
+ highAlpha = (packet[16] and 0xff.toByte()) * 65536 + (packet[17] and 0xff.toByte()) * 256 + (packet[18] and 0xff.toByte())
+ lowBeta = (packet[19] and 0xff.toByte()) * 65536 + (packet[20] and 0xff.toByte()) * 256 + (packet[21] and 0xff.toByte())
+ highBeta = (packet[22] and 0xff.toByte()) * 65536 + (packet[23] and 0xff.toByte()) * 256 + (packet[24] and 0xff.toByte())
+ lowGamma = (packet[25] and 0xff.toByte()) * 65536 + (packet[26] and 0xff.toByte()) * 256 + (packet[27] and 0xff.toByte())
+ midGamma = (packet[28] and 0xff.toByte()) * 65536 + (packet[29] and 0xff.toByte()) * 256 + (packet[30] and 0xff.toByte())
+ attention = ((packet[32] and 0xff.toByte()).toInt())
+ mediation = ((packet[34] and 0xff.toByte()).toInt())
+ ret = true
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ return ret
+ }
+
+ fun isSkinConnected(): Boolean
+ {
+ return poorSignal != 200
+ }
+
+ fun getPoorSignal(): Int
+ {
+ return poorSignal
+ }
+
+ fun getDelta(): Int
+ {
+ return delta
+ }
+
+ fun getTheta(): Int
+ {
+ return theta
+ }
+
+ fun getLowAlpha(): Int
+ {
+ return lowAlpha
+ }
+
+ fun getHighAlpha(): Int
+ {
+ return highAlpha
+ }
+
+ fun getLowBeta(): Int
+ {
+ return lowBeta
+ }
+
+ fun getHighBeta(): Int
+ {
+ return highBeta
+ }
+
+ fun getLowGamma(): Int
+ {
+ return lowGamma
+ }
+
+ fun getMidGamma(): Int
+ {
+ return midGamma
+ }
+
+ fun getAttention(): Int
+ {
+ return attention
+ }
+
+ fun getMediation(): Int
+ {
+ return mediation
+ }
+}
\ No newline at end of file
--- /dev/null
+package jp.osdn.gokigen.thetathoughtshutter.brainwave
+
+interface IBrainwaveDataReceiver
+{
+ fun receivedRawData(value: Int)
+ fun receivedSummaryData(data: ByteArray?)
+}
--- /dev/null
+package jp.osdn.gokigen.thetathoughtshutter.utils
+
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.util.Log
+import java.io.*
+import java.net.HttpURLConnection
+import java.net.URL
+
+class SimpleHttpClient
+{
+ /**
+ *
+ *
+ *
+ */
+ fun httpGet(url: String, timeoutMs: Int): String
+ {
+ var inputStream : InputStream? = null
+ var replyString = ""
+ var timeout = timeoutMs
+ if (timeoutMs < 0)
+ {
+ timeout = DEFAULT_TIMEOUT
+ }
+
+ // HTTP GETメソッドで要求を投げる
+ try
+ {
+ val httpConn = URL(url).openConnection() as HttpURLConnection
+ try
+ {
+ httpConn.requestMethod = "GET"
+ httpConn.connectTimeout = timeout
+ httpConn.readTimeout = timeout
+ httpConn.connect()
+ val responseCode = httpConn.responseCode
+ if (responseCode == HttpURLConnection.HTTP_OK)
+ {
+ inputStream = httpConn.inputStream
+ }
+ if (inputStream == null)
+ {
+ Log.w(TAG, "httpGet: Response Code Error: $responseCode: $url")
+ return ("")
+ }
+ }
+ catch (ee : Exception)
+ {
+ Log.w(TAG, "httpGet: " + url + " " + ee.message)
+ ee.printStackTrace()
+ httpConn.disconnect()
+ return ("")
+ }
+ }
+ catch (e: Exception)
+ {
+ Log.w(TAG, "httpGet(2): " + url + " " + e.message)
+ e.printStackTrace()
+ return ("")
+ }
+
+ // 応答を確認する
+ try
+ {
+ val responseBuf = StringBuilder()
+ val reader = BufferedReader(InputStreamReader(inputStream))
+ var c: Int
+ while (reader.read().also { c = it } != -1)
+ {
+ responseBuf.append(c.toChar())
+ }
+ replyString = responseBuf.toString()
+ reader.close()
+ }
+ catch (e: Exception)
+ {
+ Log.w(TAG, "httpGet: exception: " + e.message)
+ e.printStackTrace()
+ }
+ finally
+ {
+ try
+ {
+ inputStream.close()
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+ return (replyString)
+ }
+
+ /**
+ *
+ *
+ *
+ */
+ fun httpGetBytes(url: String, setProperty: Map<String, String>?, timeoutMs: Int, callback: IReceivedMessageCallback)
+ {
+ httpCommandBytes(url, "GET", null, setProperty, null, timeoutMs, callback)
+ }
+
+ /**
+ *
+ *
+ *
+ */
+ fun httpPostBytes(url: String, postData: String?, setProperty: Map<String, String>?, timeoutMs: Int, callback: IReceivedMessageCallback)
+ {
+ httpCommandBytes(url, "POST", postData, setProperty, null, timeoutMs, callback)
+ }
+
+ private fun httpCommandBytes(url: String, requestMethod: String, postData: String?, setProperty: Map<String, String>?, contentType: String?, timeoutMs: Int, callback: IReceivedMessageCallback)
+ {
+ var inputStream: InputStream? = null
+ var timeout = timeoutMs
+ if (timeoutMs < 0)
+ {
+ timeout = DEFAULT_TIMEOUT
+ }
+
+ // HTTP メソッドで要求を送出
+ try
+ {
+ val httpConn = URL(url).openConnection() as HttpURLConnection
+ httpConn.requestMethod = requestMethod
+ if (setProperty != null)
+ {
+ for (key in setProperty.keys)
+ {
+ val value = setProperty[key]
+ httpConn.setRequestProperty(key, value)
+ }
+ }
+ if (contentType != null)
+ {
+ httpConn.setRequestProperty("Content-Type", contentType)
+ }
+ httpConn.connectTimeout = timeout
+ httpConn.readTimeout = timeout
+ if (postData == null)
+ {
+ httpConn.connect()
+ }
+ else
+ {
+ httpConn.doInput = true
+ httpConn.doOutput = true
+ val outputStream = httpConn.outputStream
+ val writer = OutputStreamWriter(outputStream, "UTF-8")
+ writer.write(postData)
+ writer.flush()
+ writer.close()
+ outputStream.close()
+ }
+ val responseCode = httpConn.responseCode
+ if (responseCode == HttpURLConnection.HTTP_OK)
+ {
+ inputStream = httpConn.inputStream
+ }
+ if (inputStream == null)
+ {
+ Log.w(TAG, " http $requestMethod Response Code Error: $responseCode: $url")
+ callback.onErrorOccurred(NullPointerException())
+ callback.onCompleted()
+ return
+ }
+
+ // 応答を確認する
+ try
+ {
+ var contentLength = httpConn.contentLength
+ if (contentLength < 0)
+ {
+ // コンテンツ長が取れない場合の処理...
+ try
+ {
+ val headers = httpConn.headerFields
+ // コンテンツ長さが取れない場合は、HTTP応答ヘッダから取得する
+ val valueList = headers["X-FILE_SIZE"]
+ try
+ {
+ if (valueList != null)
+ {
+ contentLength = getValue(valueList).toInt()
+ }
+ }
+ catch (ee: Exception)
+ {
+ ee.printStackTrace()
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+ val buffer = ByteArray(BUFFER_SIZE)
+ var readBytes = 0
+ var readSize = inputStream.read(buffer, 0, BUFFER_SIZE)
+ while (readSize != -1)
+ {
+ callback.onReceive(readBytes, contentLength, readSize, buffer)
+ readBytes += readSize
+ readSize = inputStream.read(buffer, 0, BUFFER_SIZE)
+ }
+ Log.v(TAG, "RECEIVED $readBytes BYTES. (contentLength : $contentLength)")
+ inputStream.close()
+ }
+ catch (e: Exception)
+ {
+ Log.w(TAG, "httpGet: exception: " + e.message)
+ e.printStackTrace()
+ callback.onErrorOccurred(e)
+ }
+ finally
+ {
+ try
+ {
+ inputStream.close()
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+ }
+ catch (e: Exception)
+ {
+ Log.w(TAG, "http " + requestMethod + " " + url + " " + e.message)
+ e.printStackTrace()
+ callback.onErrorOccurred(e)
+ callback.onCompleted()
+ return
+ }
+ callback.onCompleted()
+ }
+
+
+ private fun getValue(valueList: List<String>): String
+ {
+ // 応答ヘッダの値切り出し用...
+ var isFirst = true
+ val values = StringBuilder()
+ for (value in valueList)
+ {
+ values.append(value)
+ if (isFirst)
+ {
+ isFirst = false
+ }
+ else
+ {
+ values.append(" ")
+ }
+ }
+ return values.toString()
+ }
+
+ fun httpGetBitmap(url: String, setProperty: Map<String, String>?, timeoutMs: Int): Bitmap?
+ {
+ return (httpCommandBitmap(url, "GET", null, setProperty, null, timeoutMs))
+ }
+
+ /**
+ *
+ *
+ *
+ */
+ fun httpPostBitmap(url: String, postData: String?, timeoutMs: Int): Bitmap?
+ {
+ return (httpCommandBitmap(url, "POST", postData, null, null, timeoutMs))
+ }
+
+ /**
+ *
+ *
+ *
+ */
+ private fun httpCommandBitmap(url: String, requestMethod: String, postData: String?, setProperty: Map<String, String>?, contentType: String?, timeoutMs: Int): Bitmap?
+ {
+ //var httpConn: HttpURLConnection? = null
+ var inputStream: InputStream? = null
+ //var outputStream: OutputStream? = null
+ //var writer: OutputStreamWriter? = null
+ var bmp: Bitmap? = null
+ var timeout = timeoutMs
+ if (timeoutMs < 0)
+ {
+ timeout = DEFAULT_TIMEOUT
+ }
+
+ // HTTP メソッドで要求を送出
+ try
+ {
+ val httpConn = URL(url).openConnection() as HttpURLConnection
+ httpConn.requestMethod = requestMethod
+ if (setProperty != null)
+ {
+ for (key in setProperty.keys)
+ {
+ val value = setProperty[key]
+ httpConn.setRequestProperty(key, value)
+ }
+ }
+ if (contentType != null)
+ {
+ httpConn.setRequestProperty("Content-Type", contentType)
+ }
+ httpConn.connectTimeout = timeout
+ httpConn.readTimeout = timeout
+ if (postData == null)
+ {
+ httpConn.connect()
+ }
+ else
+ {
+ httpConn.doInput = true
+ httpConn.doOutput = true
+ val outputStream = httpConn.outputStream
+ val writer = OutputStreamWriter(outputStream, "UTF-8")
+ writer.write(postData)
+ writer.flush()
+ writer.close()
+ outputStream.close()
+ }
+ val responseCode = httpConn.responseCode
+ if (responseCode == HttpURLConnection.HTTP_OK)
+ {
+ inputStream = httpConn.inputStream
+ if (inputStream != null)
+ {
+ bmp = BitmapFactory.decodeStream(inputStream)
+ }
+ }
+ if (inputStream == null)
+ {
+ Log.w(TAG, "http: ($requestMethod) Response Code Error: $responseCode: $url")
+ return (null)
+ }
+ inputStream.close()
+ }
+ catch (e: Exception)
+ {
+ Log.w(TAG, "http: (" + requestMethod + ") " + url + " " + e.message)
+ e.printStackTrace()
+ return (null)
+ }
+ return (bmp)
+ }
+
+ /**
+ *
+ *
+ *
+ */
+ fun httpPost(url: String, postData: String?, timeoutMs: Int): String?
+ {
+ return (httpCommand(url, "POST", postData, null, null, timeoutMs))
+ }
+
+ /**
+ *
+ *
+ *
+ */
+ fun httpGetWithHeader(url: String, headerMap: Map<String, String>?, contentType: String?, timeoutMs: Int): String?
+ {
+ return (httpCommand(url, "GET", null, headerMap, contentType, timeoutMs))
+ }
+
+ /**
+ *
+ *
+ *
+ */
+ fun httpPostWithHeader(url: String, postData: String?, headerMap: Map<String, String>?, contentType: String?, timeoutMs: Int): String?
+ {
+ return (httpCommand(url, "POST", postData, headerMap, contentType, timeoutMs))
+ }
+
+ /**
+ *
+ *
+ *
+ */
+ fun httpPutWithHeader(url: String, putData: String?, headerMap: Map<String, String>?, contentType: String?, timeoutMs: Int): String?
+ {
+ return (httpCommand(url, "PUT", putData, headerMap, contentType, timeoutMs))
+ }
+
+ /**
+ *
+ *
+ *
+ */
+ fun httpPut(url: String, postData: String?, timeoutMs: Int): String?
+ {
+ return (httpCommand(url, "PUT", postData, null, null, timeoutMs))
+ }
+
+ /**
+ *
+ *
+ *
+ */
+ fun httpOptions(url: String, optionsData: String?, timeoutMs: Int): String?
+ {
+ return (httpCommand(url, "OPTIONS", optionsData, null, null, timeoutMs))
+ }
+
+ /**
+ *
+ *
+ *
+ */
+ private fun httpCommand(url: String, requestMethod: String, postData: String?, setProperty: Map<String, String>?, contentType: String?, timeoutMs: Int): String?
+ {
+ var inputStream: InputStream? = null
+ var timeout = timeoutMs
+ if (timeoutMs < 0)
+ {
+ timeout = DEFAULT_TIMEOUT
+ }
+
+ // HTTP メソッドで要求を送出
+ try
+ {
+ val httpConn = URL(url).openConnection() as HttpURLConnection
+ httpConn.requestMethod = requestMethod
+ if (setProperty != null)
+ {
+ for (key in setProperty.keys)
+ {
+ val value = setProperty[key]
+ httpConn.setRequestProperty(key, value)
+ }
+ }
+ if (contentType != null)
+ {
+ httpConn.setRequestProperty("Content-Type", contentType)
+ }
+ httpConn.connectTimeout = timeout
+ httpConn.readTimeout = timeout
+ if (postData == null)
+ {
+ httpConn.connect()
+ }
+ else
+ {
+ httpConn.doInput = true
+ httpConn.doOutput = true
+ val outputStream = httpConn.outputStream
+ val writer = OutputStreamWriter(outputStream, "UTF-8")
+ writer.write(postData)
+ writer.flush()
+ writer.close()
+ outputStream.close()
+ }
+ val responseCode = httpConn.responseCode
+ if (responseCode == HttpURLConnection.HTTP_OK)
+ {
+ inputStream = httpConn.inputStream
+ }
+ if (inputStream == null)
+ {
+ Log.w(TAG, "http $requestMethod : Response Code Error: $responseCode: $url")
+ return ""
+ }
+ }
+ catch (e: Exception)
+ {
+ Log.w(TAG, "http " + requestMethod + " : IOException: " + e.message)
+ e.printStackTrace()
+ return ("")
+ }
+
+ // 応答の読み出し
+ return readFromInputStream(inputStream)
+ }
+
+ private fun readFromInputStream(inputStream: InputStream?): String
+ {
+ //var reader: BufferedReader? = null
+ var replyString = ""
+ if (inputStream == null)
+ {
+ return ""
+ }
+ try
+ {
+ val responseBuf = StringBuilder()
+ val reader = BufferedReader(InputStreamReader(inputStream))
+ var c: Int
+ while (reader.read().also { c = it } != -1)
+ {
+ responseBuf.append(c.toChar())
+ }
+ replyString = responseBuf.toString()
+ reader.close()
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ return replyString
+ }
+
+ interface IReceivedMessageCallback
+ {
+ fun onCompleted()
+ fun onErrorOccurred(e: Exception?)
+ fun onReceive(readBytes: Int, length: Int, size: Int, data: ByteArray?)
+ }
+
+ companion object
+ {
+ private val TAG = SimpleHttpClient::class.java.simpleName
+ private const val DEFAULT_TIMEOUT = 10 * 1000 // [ms]
+ private const val BUFFER_SIZE = 131072 * 2 // 256kB
+ }
+}
--- /dev/null
+package jp.osdn.gokigen.thetathoughtshutter.utils
+
+import android.util.Log
+import java.io.*
+import java.net.HttpURLConnection
+import java.net.URL
+
+class SimpleLiveViewSlicer
+{
+ class Payload(private val jpegData: ByteArray?, val paddingData: ByteArray?)
+ {
+ fun getJpegData(): ByteArray?
+ {
+ return jpegData
+ }
+ }
+
+ private var mJpegStartMarker = intArrayOf(0x0d, 0x0a, 0x0d, 0x0a, 0xff, 0xd8)
+ private var mHttpConn: HttpURLConnection? = null
+ private var mInputStream : InputStream? = null
+
+ fun setMJpegStartMarker(startMarker: IntArray)
+ {
+ mJpegStartMarker = startMarker
+ }
+
+ fun open(liveViewUrl: String?, postData: String?, contentType: String?)
+ {
+ try
+ {
+ if ((mInputStream != null)||(mHttpConn != null))
+ {
+ Log.v(TAG, "Slicer is already open.")
+ return
+ }
+ val urlObj = URL(liveViewUrl)
+ mHttpConn = urlObj.openConnection() as HttpURLConnection
+ mHttpConn?.requestMethod = "POST"
+ mHttpConn?.connectTimeout = CONNECTION_TIMEOUT
+ if (contentType != null)
+ {
+ mHttpConn?.setRequestProperty("Content-Type", contentType)
+ }
+ run {
+ try
+ {
+ mHttpConn?.doInput = true
+ mHttpConn?.doOutput = true
+ val outputStream = mHttpConn?.outputStream
+ val writer = OutputStreamWriter(outputStream, "UTF-8")
+ writer.write(postData)
+ writer.flush()
+ writer.close()
+ outputStream?.close()
+ }
+ catch (e : Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+ if (mHttpConn?.responseCode == HttpURLConnection.HTTP_OK)
+ {
+ mInputStream = mHttpConn?.inputStream
+ }
+ else
+ {
+ Log.v(TAG, " RESPONSE NG : " + mHttpConn?.responseCode + " " + mHttpConn?.responseMessage)
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ fun open(liveViewUrl: String?)
+ {
+ try
+ {
+ if ((mInputStream != null)||(mHttpConn != null))
+ {
+ Log.v(TAG, "Slicer is already open.")
+ return
+ }
+ val urlObj = URL(liveViewUrl)
+ mHttpConn = urlObj.openConnection() as HttpURLConnection
+ mHttpConn?.requestMethod = "GET"
+ mHttpConn?.connectTimeout = CONNECTION_TIMEOUT
+ mHttpConn?.connect()
+ if (mHttpConn?.responseCode == HttpURLConnection.HTTP_OK)
+ {
+ mInputStream = mHttpConn?.inputStream
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ fun close()
+ {
+ try
+ {
+ if (mInputStream != null)
+ {
+ mInputStream?.close()
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ try
+ {
+ if (mHttpConn != null)
+ {
+ mHttpConn?.disconnect()
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ fun nextPayload(): Payload?
+ {
+ var payload: Payload? = null
+ try
+ {
+ while ((mInputStream != null)&&(payload == null))
+ {
+ // Common Header
+ var readLength = 1 + 1 + 2 + 4
+ val commonHeader = readBytes(mInputStream!!, readLength)
+ if ((commonHeader == null)||(commonHeader.size != readLength))
+ {
+ Log.v(TAG, "Cannot read stream for common header.")
+ payload = null
+ break
+ }
+ if (commonHeader[0].toUByte().toByte() != 0xFF.toByte())
+ {
+ Log.v(TAG, "Unexpected data format. (Start byte)")
+ payload = null
+ break
+ }
+ when (commonHeader[1].toUByte().toByte())
+ {
+ 0x12.toByte() -> {
+ // This is information header for streaming. skip this packet.
+ readLength = 4 + 3 + 1 + 2 + 118 + 4 + 4 + 24
+ readBytes(mInputStream!!, readLength)
+ }
+ 0x01.toByte(), 0x11.toByte() -> payload = readPayload()
+ else -> {
+ }
+ }
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ System.gc()
+ }
+ return payload
+ }
+
+ private fun readPayload(): Payload?
+ {
+ try
+ {
+ if (mInputStream != null)
+ {
+ // Payload Header
+ val readLength = 4 + 3 + 1 + 4 + 1 + 115
+ val payloadHeader = readBytes(mInputStream!!, readLength)
+ if (payloadHeader == null || payloadHeader.size != readLength)
+ {
+ throw EOFException("Cannot read stream for payload header.")
+ }
+ if (payloadHeader[0].toUByte().toByte() != 0x24.toByte() || payloadHeader[1].toUByte().toByte() != 0x35.toByte() || payloadHeader[2].toUByte().toByte() != 0x68.toByte() || payloadHeader[3].toUByte().toByte() != 0x79.toByte())
+ {
+ throw EOFException("Unexpected data format. (Start code)")
+ }
+ val jpegSize = bytesToInt(payloadHeader, 4, 3)
+ val paddingSize = bytesToInt(payloadHeader, 7, 1)
+
+ // Payload Data
+ val jpegData = readBytes(mInputStream!!, jpegSize)
+ val paddingData = readBytes(mInputStream!!, paddingSize)
+ return Payload(jpegData, paddingData)
+ }
+ }
+ catch (eo: EOFException)
+ {
+ eo.printStackTrace()
+ close()
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ return (null)
+ }
+
+ /**
+ * 先頭のjpegマーカーが出てくるまで読み飛ばす
+ *
+ */
+ private fun skipJpegMarkStart(stream: InputStream?)
+ {
+ if (stream == null)
+ {
+ return
+ }
+ var searchIndex = 0
+ while (true)
+ {
+ try
+ {
+ val data = stream.read()
+ if (data == mJpegStartMarker[searchIndex])
+ {
+ searchIndex++
+ if (searchIndex >= mJpegStartMarker.size)
+ {
+ break
+ }
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ return
+ }
+ }
+ }
+
+ /**
+ *
+ *
+ */
+ fun nextPayloadForMotionJpeg(): Payload?
+ {
+ var searchIndex = 0
+ val endMarker = intArrayOf(0xff, 0xd9)
+ var payload: Payload? = null
+ try
+ {
+ while ((mInputStream != null)&&(payload == null))
+ {
+ skipJpegMarkStart(mInputStream)
+ val tmpByteArray = ByteArrayOutputStream()
+ // 先頭にJPEGのマークを詰める
+ tmpByteArray.write(0xff)
+ tmpByteArray.write(0xd8)
+ while (true)
+ {
+ try
+ {
+ // 1byteづつの読み込み... 本当は複数バイト読み出しで処理したい
+ val data = mInputStream?.read()
+ if (data != null)
+ {
+ tmpByteArray.write(data)
+ if (data == endMarker[searchIndex])
+ {
+ searchIndex++
+ if (searchIndex >= endMarker.size)
+ {
+ break
+ }
+ }
+ else
+ {
+ searchIndex = 0
+ }
+ }
+ }
+ catch (e: Throwable)
+ {
+ Log.v(TAG, "INPUT STREAM EXCEPTION : " + e.localizedMessage)
+ return (null)
+ }
+ }
+ payload = Payload(tmpByteArray.toByteArray(), null)
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ return (payload)
+ }
+
+ companion object
+ {
+ private val TAG = SimpleLiveViewSlicer::class.java.simpleName
+ private const val CONNECTION_TIMEOUT = 2000 // [msec]
+ private fun bytesToInt(byteData: ByteArray, startIndex: Int, count: Int): Int
+ {
+ var ret = 0
+ try
+ {
+ for (i in startIndex until startIndex + count)
+ {
+ ret = ret shl 8 or (byteData[i].toUByte().toInt() and 0xff)
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ return ret
+ }
+
+ private fun readBytes(inputStream: InputStream, length: Int): ByteArray?
+ {
+ var ret: ByteArray?
+ try
+ {
+ val tmpByteArray = ByteArrayOutputStream()
+ val buffer = ByteArray(1024)
+ while (true)
+ {
+ val trialReadlen = Math.min(buffer.size, length - tmpByteArray.size())
+ val readlen = inputStream.read(buffer, 0, trialReadlen)
+ if (readlen < 0)
+ {
+ break
+ }
+ tmpByteArray.write(buffer, 0, readlen)
+ if (length <= tmpByteArray.size())
+ {
+ break
+ }
+ }
+ ret = tmpByteArray.toByteArray()
+ tmpByteArray.close()
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ ret = null
+ }
+ return ret
+ }
+ }
+}
--- /dev/null
+package jp.osdn.gokigen.thetathoughtshutter.utils
+
+import android.app.Activity
+import android.os.Environment
+import android.util.Log
+import jp.osdn.gokigen.thetathoughtshutter.R
+import java.io.File
+import java.io.FileOutputStream
+import java.text.SimpleDateFormat
+import java.util.*
+
+object SimpleLogDumper
+{
+ private val TAG = SimpleLogDumper::class.java.simpleName
+
+ /**
+ * デバッグ用:ログにバイト列を出力する
+ *
+ */
+ fun dumpBytes(header: String, data: ByteArray?)
+ {
+ if (data == null)
+ {
+ Log.v(TAG, "DATA IS NULL")
+ return
+ }
+ if (data.size > 8192)
+ {
+ Log.v(TAG, " --- DUMP DATA IS TOO LONG... " + data.size + " bytes.")
+ return
+ }
+ var index = 0
+ var message: StringBuffer
+ message = StringBuffer()
+ for (item in data)
+ {
+ index++
+ message.append(String.format("%02x ", item))
+ if (index >= 16)
+ {
+ Log.v(TAG, "$header $message")
+ index = 0
+ message = StringBuffer()
+ }
+ }
+ if (index != 0)
+ {
+ Log.v(TAG, "$header $message")
+ }
+ System.gc()
+ }
+
+ fun binaryOutputToFile(activity: Activity, fileNamePrefix: String, rx_body: ByteArray)
+ {
+ try
+ {
+ val calendar = Calendar.getInstance()
+ val extendName = SimpleDateFormat("yyyyMMdd-HHmmss", Locale.getDefault()).format(calendar.time)
+ @Suppress("DEPRECATION") val directoryPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).path + "/" + activity.getString(R.string.app_name2) + "/"
+ val outputFileName = fileNamePrefix + "_" + extendName + ".bin"
+ val filepath = File(directoryPath.toLowerCase(Locale.ROOT), outputFileName.toLowerCase(Locale.ROOT)).path
+ val outputStream = FileOutputStream(filepath)
+ outputStream.write(rx_body, 0, rx_body.size)
+ outputStream.flush()
+ outputStream.close()
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+}
--- /dev/null
+package jp.osdn.gokigen.thetathoughtshutter.utils
+
+import android.app.Activity
+import android.util.Log
+import android.widget.Toast
+import com.google.android.material.snackbar.Snackbar
+import jp.osdn.gokigen.thetathoughtshutter.R
+
+class SnackBarMessage(private val context: Activity, private val isToast: Boolean)
+{
+ private val TAG = toString()
+ fun showMessage(message: String?)
+ {
+ try
+ {
+ Log.v(TAG, message!!)
+ context.runOnUiThread {
+ try
+ {
+ if (!isToast)
+ {
+ // Snackbarでメッセージを通知する
+ Snackbar.make(context.findViewById(R.id.main_layout), message, Snackbar.LENGTH_LONG).show()
+ }
+ else
+ {
+ // Toastでメッセージを通知する
+ Toast.makeText(context, message, Toast.LENGTH_LONG).show()
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ fun showMessage(stringId: Int)
+ {
+ try
+ {
+ showMessage(context.getString(stringId))
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+}
--- /dev/null
+package jp.osdn.gokigen.thetathoughtshutter.utils
+
+import android.util.Log
+import android.util.Xml
+import org.xmlpull.v1.XmlPullParser
+import java.io.StringReader
+import java.util.*
+
+class XmlElement private constructor()
+{
+ //Log.v(TAG, "XmlElement Tag [" + tagName + "]");
+ var tagName = ""
+ get() =//Log.v(TAG, "XmlElement Tag [" + tagName + "]");
+ field
+ private set
+ private var tagValue: String
+ private val childElements: LinkedList<XmlElement> = LinkedList()
+ private val attributes: MutableMap<String, String>
+ private var parentElement: XmlElement? = null
+
+ val parent: XmlElement?
+ get() = parentElement
+
+ //Log.v(TAG, "XmlElement Value [" + tagValue + "]");
+ var value: String
+ get() =//Log.v(TAG, "XmlElement Value [" + tagValue + "]");
+ tagValue
+ private set(value)
+ {
+ tagValue = value
+ }
+
+ private fun putChild(childItem: XmlElement)
+ {
+ childElements.add(childItem)
+ childItem.setParent(this)
+ }
+
+ fun findChild(name: String): XmlElement
+ {
+ for (child in childElements)
+ {
+ if (child.tagName == name)
+ return child
+ }
+ return XmlElement()
+ }
+
+ fun findChildren(name: String): List<XmlElement>
+ {
+ val tagItemList: MutableList<XmlElement> = ArrayList()
+ for (child in childElements)
+ {
+ if (child.tagName == name)
+ {
+ tagItemList.add(child)
+ }
+ }
+ return tagItemList
+ }
+
+ private fun setParent(parent: XmlElement)
+ {
+ parentElement = parent
+ }
+
+ private fun putAttribute(name: String, value: String)
+ {
+ attributes[name] = value
+ }
+
+ fun getAttribute(name: String, defaultValue: String?): String?
+ {
+ var ret = attributes[name]
+ if (ret == null)
+ {
+ ret = defaultValue
+ }
+ return ret
+ }
+
+ companion object
+ {
+ private val TAG = XmlElement::class.java.simpleName
+ private val NULL_ELEMENT = XmlElement()
+ private fun parse(xmlPullParser: XmlPullParser): XmlElement
+ {
+ var rootElement = NULL_ELEMENT
+ try
+ {
+ var parsingElement: XmlElement? = NULL_ELEMENT
+ MAINLOOP@ while (true) {
+ when (xmlPullParser.next())
+ {
+ XmlPullParser.START_DOCUMENT -> Log.v(
+ TAG, "------- START DOCUMENT -----"
+ )
+ XmlPullParser.START_TAG -> {
+ val childItem = XmlElement()
+ childItem.tagName = xmlPullParser.name
+ if (parsingElement === NULL_ELEMENT) {
+ rootElement = childItem
+ } else {
+ parsingElement!!.putChild(childItem)
+ }
+ parsingElement = childItem
+
+ // Set Attribute
+ var i = 0
+ while (i < xmlPullParser.attributeCount) {
+ parsingElement.putAttribute(
+ xmlPullParser.getAttributeName(i),
+ xmlPullParser.getAttributeValue(i)
+ )
+ i++
+ }
+ }
+ XmlPullParser.TEXT -> parsingElement!!.value = xmlPullParser.text
+ XmlPullParser.END_TAG -> parsingElement = parsingElement!!.parent
+ XmlPullParser.END_DOCUMENT -> {
+ Log.v(TAG, "------- END DOCUMENT -------")
+ break@MAINLOOP
+ }
+ else -> break@MAINLOOP
+ }
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ rootElement = NULL_ELEMENT
+ }
+ return rootElement
+ }
+
+ fun parse(xmlStr: String): XmlElement
+ {
+ try
+ {
+ val xmlPullParser = Xml.newPullParser()
+ xmlPullParser.setInput(StringReader(xmlStr))
+ return parse(xmlPullParser)
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ return XmlElement()
+ }
+ }
+
+ init
+ {
+ //Log.v(TAG, "XmlElement()");
+ attributes = HashMap()
+ tagValue = ""
+ }
+}
<resources>
<string name="app_name">ThetaThoughtShutter</string>
+ <string name="app_name2">TTShut</string>
<string name="blank"> </string>
+ <string name="bluetooth_setting_is_off">Bluetooth Setting is OFF</string>
+ <string name="not_support_bluetooth">Does not support a Bluetooth.</string>
+ <string name="bluetooth_scan_start_failure">Bluetooth scan start failure.</string>
+ <string name="scan_fail_via_bluetooth">Bluetooth scan failure.</string>
+ <string name="bluetooth_scan_finished">Bluetooth scan finished.</string>
</resources>
\ No newline at end of file