Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Let's make Android Bike

Let's make Android Bike

Video: https://academy.realm.io/jp/posts/droidkaigi17-android-bike/

[Slides in Japanese] Talked at DroidKaigi 2017, intended to share my findings on making a cycling computer app that using a Bluetooth Low Energy bike speed/cadence sensor and a pressure sensor to get altitude difference.

Yuki Fujisaki

March 09, 2017
Tweet

More Decks by Yuki Fujisaki

Other Decks in Programming

Transcript

  1. Android BikeΛ࡞Ζ͏
    @tnj

    View full-size slide

  2. @tnj
    • 2010೥͔ΒAndroidΤϯδχΞ
    • mixiͷެࣜΫϥΠΞϯτΞϓϦ্ཱͪ͛
    • DeployGateͱ͍͏ձࣾΛ΍ͬͯ·͢

    View full-size slide

  3. ࠓ೔ͷ࿩ʢ໿30෼ʣ

    View full-size slide

  4. ͜ΜͳΞϓϦ࡞ͬͯͨ
    • αΠίϯΞϓϦɺࣗసंͷϝʔλʔ
    • खܰʹ࢖͑ͯɺͦΕͳΓʹݟͨ໨ָ͕ͯ͘͠ɺ
    ࣮༻తͳ΍ͭ
    • ͦΕͳΓʹಈ͚͘ͲɺϓϩτλΠϓ
    • ͜ΕΛ࡞ΔதͰɺ͍ΖΜͳ஌ݟͱ᠘Λಘͨ
    • ஌ࣝ΍ମݧΛڞ༗͍ͨ͠

    View full-size slide

  5. ର৅
    • ͬ͘͟ΓAndroid։ൃʹ৮ΕͯΔ͚Ͳ
    • Bluetoothͱ͔ηϯαͱ͔࢖ͬͨΞϓϦ͸

    ࡞ͬͨ͜ͱ͕ͳ͍
    • IoTಐఃͷਓ

    View full-size slide

  6. ʹ1ϲ݄લͷࣗ෼
    ʮηϯα΍σόΠεͰԿ͔Ͱ͖Δͱָͦ͠͏ʯ
    ͱࢥͬͯΔ͚Ͳɺ·ͩ৮ͬͨ͜ͱ͕ͳ͍
    ͜ͷηογϣϯͷऴΘΓࠒʹ͸
    • ͳΜ͔࡞Γ͍ͨͳͬͯؾ෼ʹͳͬͯ΋Β͑Δ
    • ͍ΖΜͳΞϓϦΛ࡞Δ͖͔͚ͬʹͳΔͱ͍͍ͳ

    View full-size slide

  7. ΞδΣϯμ
    • ͖͔͚ͬ
    • εϚϗͷؾѹηϯαͰߴ౓ܭΛ࡞Δ
    • BLEͷηϯαͰεϐʔυ/έΠσϯεΛಘΔ
    • αΠίϯʹಛԽͨ͠ը໘Λ࡞Δ
    • Θ͔ͬͨ͜ͱ
    • ·ͱΊͱࠓޙ

    View full-size slide

  8. εϥΠυɺίʔυɺΞϓϦ

    ্͕ͬͯ·͢
    URL ͸ Twitter: @tnj Ͱ

    View full-size slide

  9. ࡢ೥11݄ʹࣗసंങͬͨ
    GIANT TCR Advanced 2 (2016)

    View full-size slide

  10. ళһ
    ʮͦ͏͍͑͹αΠίϯͱ͔͚ͭ·͢ʁʯ

    View full-size slide

  11. ࣗ෼
    ʮ͋ɺཉ͍͠Ͱ͢ʯ

    View full-size slide

  12. ళһ
    ʮ͜ͷ্Ґػछͩͱඪ४૷උͷ΍ͭɺ
    ͚ͭͱ͘ͱεϚϗͱ͔Ͱ

    ݟΕͨΓ͠·͢Αʙʯ

    View full-size slide

  13. Θ͔ͬͨ͜ͱ

    View full-size slide

  14. 21ੈلͷࣗసं͸

    BluetoothΛ஻Δ

    View full-size slide

  15. ࣗసं͕BluetoothΛ஻Δ
    • ंମ͕࠷ॳ͔ΒηϯαReady
    • ্Ґػछͩͱඪ४૷උͱ͔
    • ANT+͕ྲྀߦ͚ͬͯͨͲ

    ࠷ۙ͸Bluetooth Low EnergyԽ͕ਐΜͰΔ

    View full-size slide

  16. ͍ΖΜͳΞϓϦΛࢼͨ͠

    View full-size slide

  17. …͚Ͳɺͬ͘͠Γ͜ͳ͍
    • ͳΜ͔ݟͨ໨͕޷͖͡Όͳ͔ͬͨΓ

    ૢ࡞͕ҟৗʹ೉͔ͬͨ͠Γ
    • ηϯαରԠ͕έΠσϯε͚ͩͩͬͨΓ
    • ඪߴ/ࣼ౓ͷද͕ࣔ·ͬͨ͘ΞςʹͳΒͳ͍

    View full-size slide

  18. ͳ͍ͳΒɺ࡞Δ͔͠ͳ͍

    View full-size slide

  19. Ͳ͏΍ͬͯ
    • ஋͸Bluetooth LEηϯαͰऔͬͨ΋ͷΛग़͢
    • ؾѹηϯα࢖͑͹ߴ౓औΕΔͰ͠ΐ
    • දࣔ΋ಛԽͨ͠ܗͰ޷͖উख࡞Ζ͏

    View full-size slide

  20. ͱʹ͔͘ϓϩτλΠϓ
    ࣗ෼ͷ࣋ͬͯΔ୺຤(Nexus 6P)Ͱ
    • BLEͷσόΠεݕࡧɺ؅ཧͱ஋ͷऔಘ
    • ηϯαͷ؅ཧͱ஋ͷऔಘ
    • Ґஔ৘ใͷऔಘ
    ͯ͠දࣔ͢Δͧ

    View full-size slide

  21. ͬͦ͘͞Bluetooth LE࢖͏ͧ

    View full-size slide

  22. ୺຤ͷηϯαΛ࢖ͬͯΈΑ͏
    • ᠘͕গͳͦ͏…
    • ৺͕ંΕʹͦ͘͏…
    • ੒ޭମݧେࣄ

    View full-size slide

  23. Bosch BMP280
    5Ҏ߱ͷNexusγϦʔζ͸ؾѹηϯα౥ࡌ

    View full-size slide

  24. ؾѹܭΞϓϦΛࢼͯ͠ΈΔ
    চ͔Β಄্΁ͷҠಈ(໿2m)Ͱ΋༗ҙʹมԽ

    View full-size slide

  25. ͱΓ࣮͋͑ͣػͰऔͬͯΈΑ͏
    ΍Δ͜ͱ͸ׂͱγϯϓϧ
    1. SensorManager Ͱ

    TYPE_PRESSURE ͳηϯαΛ΋Βͬͯ͘Δ
    2. SensorEventListener Λ࣮૷͢Δ
    3. ϦεφΛ SensorManager ʹొ࿥͢Δ

    View full-size slide

  26. 1. SensorManagerͰ

    TYPE_PRESSURE ͳηϯαΛ

    ΋Βͬͯ͘Δ
    sensorManager = (SensorManager)
    context.getSystemService(Context.SENSOR_SERVICE);
    pressure =
    sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);

    View full-size slide

  27. 2. SensorEventListener Λ

    ࣮૷͢Δ
    @Override
    public void onSensorChanged(SensorEvent event) {
    if (event.sensor == pressure) {
    // event.values[0] ʹ float Ͱؾѹ஋͕ೖ͍ͬͯΔ
    Log.v(TAG, "Pressure: " + event.values[0]);
    }
    }

    View full-size slide

  28. 3. ϦεφΛొ࿥͢Δ
    sensorManager.registerListener(
    this, pressure, SensorManager.SENSOR_DELAY_NORMAL
    );

    View full-size slide

  29. ࣮ߦ!
    • ݁ߏͳ੎͍Ͱ onSensorChanged ͕ݺ͹Εͯ
    ஋͕औΕΔ

    View full-size slide

  30. ؾѹ͔Βߴ౓ΛٻΊΔ
    • ۙࣅࣜʢΧγΦܭࢉػʮߴਫ਼౓ܭࢉαΠτʯΑΓʣ
    • ݫີʹ͸ւ໘ؾѹͱؾԹ͕ඞཁ
    • BMP280಺ͷؾԹηϯαͰऔΕΔ͔ͳʁʁ

    View full-size slide

  31. ؾԹηϯα΋ಉ͡Α͏ʹ…
    • ͱࢥ͚ͬͨͲݟ͔ͭΒͳ͍
    • BMP280ʹ͸Թ౓ηϯα΋ࡌͬͯΔ͸ͣʁʁʁ
    • TYPE_AMBIENT_TEMPERATURE Ͱ͸

    ͳʹ΋औΕͳ͍
    • TYPE_TEMPERATURE (deprecated) Ͱ΋

    औΕͳ͍

    View full-size slide

  32. શ෦ྻڍͯ͠୳ͯ͠Έͨ
    sensor.getName();
    // => BMP280 temperature
    sensor.getStringType();
    // => com.google.sensor.internal_temperature
    sensor.getType();
    // => Type=65536
    • TYPE_INTERNAL_TEMPERATURE(API 24+) ͩͱ 65537 ❓

    View full-size slide

  33. ద౰ʹϏϡʔʹηοτͨ͠Γ

    View full-size slide

  34. ͳΜ͔ͦΕͳΓʹಈ͍ͯΔ
    • ͔͠͠஋͕ඈΜͩΓ͢Δ
    • ͍͖ͳΓ5mͱ͔ඈͿ͜ͱ͕͋Δ
    • ͖ͭΊʹLow Pass FilterΛֻ͚Δ
    • ؆қ࣮૷
    lastPressure =
    lastPressure * 0.95f + currentPressure * 0.05f;

    View full-size slide

  35. ͦΜͳʹ༳Εͳ͘ͳΔ

    View full-size slide

  36. ؾԹߴ͗͢໰୊
    • ຊମ಺ͷԹ౓ʹͳͬͪΌ͏͔ΒͶ…
    • ΋͏গ͠ௐ΂ͯΈͨ
    • ؾԹɾւ໘ؾѹͳ͠Ͱ΋ͦΕͳΓͷਫ਼౓
    • ͦ΋ͦ΋ͦ͏͍͏ϝιου͕
    SensorManagerʹ༻ҙ͞Εͯͨ
    SensorManager.getAltitude(pressure, basePressure);

    View full-size slide

  37. ஌ݟJavadocʹ
    ߴ͞ͷࠩΛٻΊΔͱ͖ʹ͸ɺͦΕͧΕͷ஍఺Ͱࢉ
    ग़ͨ͠ߴ͞ಉ࢜ΛҾ͘΂͠ɻւ໘ؾѹ͕෼͔Βͳ
    ͚Ε͹ɺPRESSURE_STANDARD_ATMOSPHEREఆ
    ਺Λ࢖͏ͱҰൠతͳؾѹͳΒ͍͍ͩͨྑ͍݁Ռ͕
    ಘΒΕΔɻ
    To calculate altitude differences, you must calculate the difference
    between the altitudes at both points. If you don't know the altitude
    as sea level, you can use {@link #PRESSURE_STANDARD_ATMOSPHERE} instead,
    which will give good results considering the range of pressure typically
    involved.

    View full-size slide

  38. ຬΛ࣋ͯ͠Bluetooth LE΁

    View full-size slide

  39. GIANT NEW RIDESENSE (ANT+/BLE)
    ϗΠʔϧͱΫϥϯΫʹ࣓ੴ͕͋ͬͯɺͦΕ͕ηϯαʹۙ઀
    ͢Δ௨Δճ਺Λ਺͑Δ࢓૊Έ

    View full-size slide

  40. Bluetooth Low Energy
    (Bluetooth 4.0)
    • Ϙλϯి஑1ݸͰ਺ϲ݄ಈ͔ͤΔ͙Β͍௿ফඅ
    ిྗʹৼͬͨن֨
    • ௨৴ස౓ɺ௨৴ྔ͕࠷௿ݶʢ௨৴ػثͷి
    ஑ফඅͷཁʣ
    • ࢖͏νϟϯωϧݮͬͨΓɺϓϩτίϧ͸Ҏ
    લͱޓ׵ੑͳ͍

    View full-size slide

  41. ΫϥΠΞϯταʔόϞσϧ
    • ࠓճͩͱ
    • ࣗసं(ϖϦϑΣϥϧ)͕αʔό
    • Nexus 6P(ηϯτϥϧ)͕ΫϥΠΞϯτ
    • ϖϦϑΣϥϧ͕ʮԶͩʔͬʂʯͬͯΞυόλ
    ΠδϯάΛਨΕྲྀͯ͠ΔͷΛɺηϯτϥϧ͕
    εΩϟϯͯ͠ݟ͚ͭΔ

    View full-size slide

  42. AndroidͱBLE
    • Android 4.3Ҏ্Ͱ࢖͑Δ
    • Ͱ΋5Ͱ΋6Ͱ΋ͦΕͧΕڍಈ͕ҧ͍ͬͯΔ
    • άάͬͯΔͱઌۦऀͷࢡΛ௒͍͑ͯ͘ײ͕͍͢͝
    • ͓खܰʹ͍ͨ͠ͷͰࠓճ͸7͔͠ݟ·ͤΜʂʂ

    View full-size slide

  43. Ͱɺ
    • σʔλͬͯͲ͏औಘ͢Δͷʁܗࣜ͸ʁ
    • BLEʹ͸GATT͍ͬͯ͏ඪ४࢓༷͕͋Γ·͢

    View full-size slide

  44. GATT
    • Generic ATTribute Profile
    • ͳʹ͜ͷॳݟࡴ͠ײ͋;ΕΔলུه๏

    View full-size slide

  45. https://www.bluetooth.com/ja-jp/specifications/generic-attributes-overview

    View full-size slide

  46. GATT.Service.Characteristic
    • GATT
    • has_many Services

    (ࣗసंαʔϏεɺόοςϦʔαʔϏεͱ͔)
    • has_many Characteristics

    (ճస਺΍ɺൃੜ࣌ࠁͳͲ࣮ࡍͷ஋)
    • Service, Characteristic͸ͦΕͧΕܾ·ͬͨUUID
    ׂ͕Γ౰ͯΒΕ͓ͯΓUUIDͰݕࡧͰ͖Δ

    View full-size slide

  47. ࠓճ࢖͏Service: CSC
    • Cycling Speed and Cadence Service
    • ʮBLEରԠʯͬͯࣗసंεϐʔυ/έΠσϯε
    ηϯα͸͜ΕΛ࣮૷ͯ͠੡඼Խͯ͠Δ

    View full-size slide

  48. …ͷதͷCharacteristic
    CSC Measurement Characteristic
    • ϗΠʔϧͱΫϥϯΫͦΕͧΕͷ
    • ճస਺
    • ࠷ޙʹΠϕϯτ͕ൃੜͨ࣌ؒ͠
    • ී௨͸1ඵִؒͰૹ৴͞ΕΔ
    • ΫϥϯΫͷ஋ͰέΠσϯε͕෼͔Δ
    • ϗΠʔϧճస਺ʹλΠϠܘΛֻ͚ͯ΍Ε͹଎౓͕෼͔Δ

    View full-size slide

  49. 19ϖʔδͷ࢓༷ॻPDF

    View full-size slide

  50. Specશ෦ಡΜͰ

    ΍ͬͯ΋͍͍͚Ͳ…
    ͭΒͦ͏

    View full-size slide

  51. ٹੈओ
    nRF Toolbox for BLE

    View full-size slide

  52. nRF Toolbox for BLE
    • Nordic Semiconductor
    • ௒௿ফඅిྗͷۙڑ཭ແઢʹڧ͍௨৴Ϟ
    δϡʔϧͷձࣾ
    • ֤छϓϩϑΝΠϧͷσϞΞϓϦ
    • Φʔϓϯιʔε (TV΋Wear΋ରԠ)
    • New BSDϥΠηϯε

    View full-size slide

  53. ಡΈͳ͕Βཧղ
    ࢖͍ճ͢ʹ͸ن໛͕େ͖͍ͷͰ࣮૷͸ࣗલ

    View full-size slide

  54. ͕ʔͬͱ࣮૷
    • ·ͣҐஔ৘ใͷPermissionऔಘ͕ඞཁ
    (Android 6.0Ҏ߱)
    • Runtime Permission΍ʂ

    View full-size slide

  55. by @hotchemi

    View full-size slide

  56. 1. Activityʹ
    @RuntimePermissions ͚ͬͯͭͯ
    @RuntimePermissions
    public class MainActivity {

    View full-size slide

  57. 2. onRequestPermissionsResult
    Λதܧͯ͠४උ׬ྃ
    @Override
    public void onRequestPermissionsResult(
    int requestCode, @NonNull String[] permissions,
    @NonNull int[] grantResults) {
    MainActivityPermissionsDispatcher
    .onRequestPermissionsResult(this, requestCode,
    grantResults);
    };

    View full-size slide

  58. 3. ύʔϛογϣϯ͕ඞཁͳ

    ؔ਺ʹΞϊςʔγϣϯॻ͍ͯ
    @NeedsPermission(Manifest.permission.ACCESS_FINE_LOCATION)
    void startBleScan() {

    View full-size slide

  59. 4. ؔ਺ݺͼग़͠Λ

    ஔ͖׵͑ͯ΍Δ
    // startBleScan(); ΛҎԼʹஔ͖׵͑
    MainActivityPermissionsDispatcher
    .startBleScanWithCheck(this);

    View full-size slide

  60. 5. ͓͠·͍
    ͏·͍͜ͱ΍ͬͯ͘ΕΔ

    View full-size slide

  61. ૣ଎σόΠεΛ୳ͯ͠σʔλΛ
    औಘ͍ͨ͠ʂʂ
    ΍Δ͜ͱ
    1. पғͷσόΠεΛεΩϟϯ͢Δ
    2. ݟ͔ͭͬͨσόΠε಺ͷGATTαʔόʹ઀ଓ͢Δ
    3. GATTαʔό͔ΒCSC ServiceΛऔಘ͢Δ
    4. CSC Service͕࣋ͬͯΔCSC Measurement
    CharacteristicΛऔಘ
    5. ߋ৽͕͋ͬͨΒ௨஌(Notification)ΛૹͬͯͶͱཁٻ͢Δ

    View full-size slide

  62. 1. ·ͣ͸εΩϟφʔΛ΋Β͏
    // Bluetooth Service Λ΋Βͬͯ
    BluetoothManager bluetoothManager = (BluetoothManager)
    context.getSystemService(Context.BLUETOOTH_SERVICE);
    // Bluetooth ΞμϓλΛ΋Βͬͯ
    BluetoothAdapter bluetoothAdapter =
    bluetoothManager.getAdapter();
    // Ξμϓλ͕࣋ͬͯΔ BLE ͷεΩϟφΛ΋Βͬͯ͘Δ
    bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();

    View full-size slide

  63. ͰɺεΩϟϯ͢Δ
    // εΩϟϯઃఆ
    ScanSettings settings = new ScanSettings.Builder()
    .setScanMode(ScanSettings.SCAN_MODE_BALANCED) // ଞLOW_LATENCYͱ͔
    .build();
    // CSC Service ͚ͩݟ͚ͭͯཉ͍͠ͷͰϑΟϧλΛઃఆ͢Δ
    List filters = new ArrayList<>();
    filters.add(new ScanFilter.Builder()
    .setServiceUuid(new ParcelUuid(SERVICE_UUID))
    .build());
    // εΩϟϯΑΖ͘͠!
    bluetoothLeScanner.startScan(filters, settings, callback);

    View full-size slide

  64. 2. σόΠε಺ͷ GATT αʔό
    ʹ઀ଓ͢Δ
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
    super.onScanResult(callbackType, result);
    // CSC Service Λ࣋ͭσόΠε!
    BluetoothDevice device = result.getDevice();
    // GATT αʔόʔʹܨ͍Ͱ͘Ε!
    BluetoothGatt gatt = device.connectGatt(context, false, new
    GattCallback());
    }
    σόΠε͕ݟ͔ͭΔͱݺͼग़͞ΕΔίʔϧόοΫ಺ͰDPOOFDU(BUU͢Δɻ

    View full-size slide

  65. 4. ݟ͔ͭͬͨService͔Β͓໨౰ͯ
    ͷCharacteristicΛ΋Β͏
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    BluetoothGattService service =
    gatt.getService(SERVICE_UUID);
    if (service != null) {
    characteristic =
    service.getCharacteristic(CHARACTERISTIC_UUID);
    Ұཡ͕औಘͰ͖ͨΒɺ$4$4FSWJDFΛऔಘͯ͠ɺ

    $4$.FBTVSFNFOU$IBSBDUFSJTUJDΛऔಘ

    View full-size slide

  66. 5. ௨஌ͯ͠ͶͬͯઃఆΛ͢Δ
    // 1. Bluetooth Stackʹड͚औͬͨΒίʔϧόοΫͯ͠Ͷ! ͬͯ఻͑Δ
    gatt.setCharacteristicNotification(characteristic, true);
    // 2. Device ʹ΋ߋ৽௨஌ͯ͘͠ΕΑͳ! ͬͯ఻͑Δ
    // (descriptorʹॻ͖ࠐΉͱૹ৴͞ΕΔ)
    final BluetoothGattDescriptor descriptor =
    characteristic.getDescriptor(
    CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
    if (descriptor != null) {
    descriptor
    .setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    gatt.writeDescriptor(descriptor);
    }

    View full-size slide

  67. σʔλऔಘ։࢝ʂ
    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt,
    BluetoothGattCharacteristic characteristic) {
    // σʔλΛௐཧ͢Δ
    }
    PO$IBSBDUFSJTUJD$IBOHFE͕ݺͼग़͞ΕΔΑ͏ʹͳΔʂ

    ૹ৴ִؒ͸େମඵʹճ

    View full-size slide

  68. σʔλΛௐཧ͢Δ
    • ΍ͬͯ͘Δσʔλ͸όΠτྻ
    • byte getValue()
    • όΠτྻΛ·Δͬͱฦ͢
    • ಛఆͷόΠτΦϑηοτ͔Β೚ҙͷܕͰऔΓग़͢
    ؔ਺͕༻ҙ͞ΕͯΔ
    • int getIntValue(formatType, offset)

    View full-size slide

  69. CSC Measurement͕ૹΔσʔλ
    • ઌ಄͔Β
    • 1byte - flags (1: ϗΠʔϧ͋Δ͔, 2: ΫϥϯΫ͋Δ͔)
    • ϗΠʔϧ͋ΔͳΒ
    • uint32 ྦྷੵճసճ਺
    • uint16 ࠷ޙͷΠϕϯτ͕͋ͬͨ࣌ؒ
    • ΫϥϯΫ͋ΔͳΒ
    • uint16 ྦྷੵճసճ਺
    • uint16 ࠷ޙͷΠϕϯτ͕͋ͬͨ࣌ؒ

    View full-size slide

  70. int offset = 0;
    byte flags = characteristic.getValue()[offset];
    offset += 1;
    boolean wheelRevPresent = (flags & 1) > 0;
    boolean crankRevPreset = (flags & 2) > 0;
    if (wheelRevPresent) {
    int wheelRevolutions = characteristic.getIntValue(
    BluetoothGattCharacteristic.FORMAT_UINT32, offset);
    offset += 4;
    int wheelEventTime = characteristic.getIntValue(
    BluetoothGattCharacteristic.FORMAT_UINT16, offset);
    offset += 2;
    }
    if (crankRevPreset) {
    int crankRevolutions = characteristic.getIntValue(
    BluetoothGattCharacteristic.FORMAT_UINT16, offset);
    offset += 2;
    int crankEventTime = characteristic.getIntValue(
    BluetoothGattCharacteristic.FORMAT_UINT16, offset);
    offset += 2;
    }

    View full-size slide

  71. औΕͨʂ
    • ճస਺ʹλΠϠͷܘΛֻ͚ͯ΍Ε͹଎౓͕ग़Δʂ
    • λΠϠͷܘ͸ 2096mm (700x23C)

    View full-size slide

  72. ࣍ɺը໘࡞ͬͯͧ͘ʂ

    View full-size slide

  73. ը໘Λ࡞͍ͬͯͧ͘
    • େલఏ: എܠ͸ࠇ
    • AMOLEDͩͱൃޫͯ͠Δ෦෼͔͠ిྗফඅ͠ͳ͍
    • ϨΠΞ΢τ͸ ContraintLayout (Support Library)
    • ཁૉؒͰؔ܎ੑΛηοτ͍ͯ͘͠
    • ৘ใΛิ׬͢ΔΞχϝʔγϣϯ
    • ਺ࣈΛग़͚ͩ͢Ͱͳ͘ɺಈ͖Ͱঢ়گΛ఻͑Δ
    • ઐ༻ͷViewΛ࡞Δ

    View full-size slide

  74. ConstraintLayout

    View full-size slide

  75. ConstraintLayout
    • ྫ: ୯Ґදࣔ
    • ࠨ୺Λ΄͔ͷߦͱἧ͑ͭͭ
    • ॎͷҐஔ͸ಉ͡ߦͷ਺஋ͷϕʔεϥΠϯʹἧ͑Δ
    • …ͳΜͯࢦఆ͕؆୯ʹͰ͖ͯΑ͍
    • খ਺఺ҎԼͷද͚ࣔͩผͷ TextView ʹͯ͠ϑΥϯταΠζΛ
    খ͘͢͞Δ…ͳΜͯ΍ͬͯ΋มʹζϨͨΓ͠ͳ͍
    • GUIͰҰ௨ΓઃఆͰ͖Δ͚Ͳɺͪΐͬͱෆ۩߹͋ͬͨΓ͢Δ
    ͷͰඍௐ੔͸XMLͰ΍Δ͔ͳʔͬͯײ͡

    View full-size slide

  76. ValueAnimator
    • 1ඵʹ1ճདྷΔ਺ࣈΛ࣍ʑ setText() ͢Δ͚ͩ
    Ͱ΋͍͍͚Ͳɺຯؾͳ͍
    • νϥݟͰ૿͑ͯΔͷ͔ݮͬͯΔͷ͔෼͔Βͳ͍
    • ਺ࣈΛঃʑʹมԽͤ͞Δ

    View full-size slide

  77. ͜͏΍ͬͯ
    ValueAnimator animator = ValueAnimator.ofFloat(0.0f, 0.0f);
    animator.setDuration(1000);
    animator.setInterpolator(new LinearInterpolator());
    animator.addUpdateListener((animation) -> {
    float value = (Float) animation.getAnimatedValue();
    textView.setText(String.format(Locale.US, "%.1f", value));
    });

    View full-size slide

  78. ஋͕͘Δͨͼʹɺݱࡏ஋

    →৽͍͠஋Ͱ setFloatValues ͢Δ
    private void setAnimatorValue(ValueAnimator animator, float
    newValue) {
    float currentValue = (Float) animator.getAnimatedValue();
    animator.setFloatValues(currentValue, newValue);
    animator.start();
    }

    View full-size slide

  79. …ͱ1ඵؒͰ਺ࣈ͕௚ઢతʹ૿ݮ͢Δ
    Լʢճస਺ʣ͸ͦͷ··ɺ

    ্ʢεϐʔυʣ͕༗ޮʹͯ͠Δ΍ͭ

    View full-size slide

  80. ͪͳΈʹ ValueAnimator
    • ඵؒ60ճݺͼग़͞Ε·͢
    • ͪΐ͏Ͳ͍͍͚Ͳඞཁͳ͍ͱ͖͸
    setText() ͠ͳ͍ͱ͔͢ΔͱΑ͍

    View full-size slide

  81. ಠࣗͷViewΛ࡞ͬͯ഑ஔ͢Δ
    • ΋͏গ͠໘ന͍ද͕ࣔཉ͍͠ͳʔάϥϑͱ͔
    • ৭ʑϥΠϒϥϦ͋Δ͚Ͳɺ؆୯ͳϞϊͩͬͨ
    Β࡞ͬͯ͠·ͬͨ΄͏͕͍ܰ଎͍

    View full-size slide

  82. ๮ͱɺ͘Δ͘Δ͢Δ΍ͭ

    View full-size slide

  83. ॎઢҰຊͷ଎౓ϝʔλʔ
    (HorizontalBarGraphView)
    • Viewͷ෯ʹରͯ͠ݱࡏ஋ʹ౰ͨΔׂ߹ͷҐஔʹ
    ॎઢΛҾ͚͹Α͍ʂ
    • Ҏ্ͩʂ
    @Override
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    int x = (int) (
    (Math.min(current, max) - min) / (max - min)
    * canvas.getWidth()
    );
    canvas.drawLine(x, 0, x, getHeight(), paint);
    }

    View full-size slide

  84. ͋ͱ͸஋Ληοτͯ͠
    invalidate()
    public void setCurrent(float current) {
    this.current = current;
    invalidate();
    }

    View full-size slide

  85. ͘Δ͘ΔճΔ΍ͭ
    (CircularRevolutionsView)
    • ࣮ࡍͷճసΛ࠶ݱ͢Δͱ͓΋͠Ζͦ͏ͩͳʁ
    • ValueAnimator Ͱ 0.0f → 1.0f ΛӬٱϧʔ
    ϓɺ 1 ճసʹ͔͚Δ࣌ؒ (duration) Λߋ৽͠
    ͍ͯ͘
    • ͋ͱ͸ԁΛඳ͘ɺεϜʔζʹճͯ͠΋͍͍͚
    Ͳ45°ͣͭʹ͢ΔͱͦΕͬΆ͍͔ͳʁ

    View full-size slide

  86. int centerX = width / 2 + x;
    int centerY = height / 2 + y;
    // ਅΜதͷখؙ͍͞
    canvas.drawCircle(centerX, centerY, width / 16, paint);
    if (rpm != 0.0f) {
    // 1पΛ8ஈ֊ʹؙΊΔ
    int step = (int) ((Float) animator.getAnimatedValue() * 8);
    float degree = step / (float) split;
    // ͦͷҐஔʹؙΛඳ͘
    canvas.drawCircle(
    centerX - (float) Math.cos(2 * Math.PI * degree) * (width / 2) * 0.75f,
    centerY - (float) Math.sin(2 * Math.PI * degree) * (height / 2) * 0.75f,
    width / 8,
    paint);
    }

    View full-size slide

  87. ݁Ռɺ͜͏ͳΓ·ͨ͠

    View full-size slide

  88. ৭ʑಈ͍ͯͨͷ͠ʔʂ

    View full-size slide

  89. ͢ΜͳΓͰ͖ͨΑ͏ʹ

    ݟ͑ΔͰ͠ΐ͏ʁ

    View full-size slide

  90. ΋ͪΖΜϋϚΓ·ͨ͠

    View full-size slide

  91. Android 7.1.1

    Bluetoothܨ͕Βͳ͍໰୊
    • ։ൃ͠Α͏ͱࢥͬͨΒಥવ͢΂ͯͷΞϓϦͰ
    ηϯαͷ஋ΛೝࣝͰ͖ͳ͘ͳͬͨ

    ։ൃͰ͖ͳ͍…
    • 7.0.0ʹ໭ͨ͠Βܨ͕ͬͨ
    • 7.1.2Ͱ࣏ͬͯΔ໛༷

    View full-size slide

  92. ߴ͕͞෼͔ΔΑ͏ʹͳͬͨ

    ࣌ͷ࿩

    View full-size slide

  93. ࣮ࡍʹ૸Δͧʂ
    …͋ΕΕߴ౓ͷ਺ࣈ͕શવ͓͔͍ͧ͠ʁ

    ؾѹηϯα͡Όμϝͳͷ͔ʁ

    View full-size slide

  94. εϚϗϗϧμʔɺା…ʁ
    • ؾѹͱ͸
    • ାʹѹഭ͞ΕΔ͚ͩͰߴ౓͕ٸ߱Լʙ
    • ͋ͱɺ൓ࣹͯ͠ɺݟ͑ͳ͍

    View full-size slide

  95. վળ
    ାঢ়͡Όͳ͍Ϛ΢ϯτͰղܾ

    View full-size slide

  96. ࢥ͍ࠐΈͰ࣌ؒϩε
    • ʮؾѹηϯαʔͩΊ͔ʔʯͱࢥ͍͜Μ͡Όͬͨ
    • ٸ͍Ͱ܏ࣼΛOrientation(ʹՃ଎౓ͱॏྗ)Ͱग़ͦ͏ͱࢼΈ
    ͨΓͨ͠
    • ٸ͍Ͱίϐϖͯͦ͠ΕͬΆ͍ͷ͕Ͱ͖ͨʂ૸ͬͯΈΑ͏ʂʂ
    • ಈ͖ͩͨ͠ॠؒ஋͕ඈΜͰ͍͘…
    • Ճ଎౓ͷ஋Λॏྗͷ஋ͱֻ͚߹Θͤͯຊମͷ֯౓Λݕ஌͢Δ
    ࢓૊Έ
    • ಈ͍ͨΒਖ਼͘͠൑ఆͰ͖ͳ͍ͷ౰ͨΓલ͡ΌΜ…

    View full-size slide

  97. ͳͲͳͲ͋Γ·͕ͨ͠

    View full-size slide

  98. ݴ͍͔ͨͬͨ͜ͱ
    • ·ΘΓͷϞϊΛConnectedʹͯ͘͠ͱָ͍͠
    • ࢥ͍ͬͯΔΑΓ͍ΖΜͳ΋ͷ͕Android͔Β
    ૢ࡞Ͱ͖ΔΑ͏ʹͳ͍ͬͯΔ

    View full-size slide

  99. ࠓճ͸ࣗసं͚ͩͬͨͲՈి΋
    • ྫ͑͹͓͏ͪʹ͋ΔSONYͷBRAVIA
    • IPΞυϨεΛϒϥ΢βͰ։͘

    View full-size slide

  100. ࢥΘ͵ॴʹ
    • API͕͋ͬͨΓ͢Δ
    • ಛʹެࣜͰެ։͞Εͯͳ͍͚Ͳ֤छݴޠͰϥΠϒϥϦ͕
    ࣮૷͞ΕͯΔ
    • APIͳͯ͘΋੺֎ઢϦϞίϯ͋Δ΋ͷ͸IRKitͱ͔Ͱૢ࡞Ͱ
    ͖Δ
    • HTTPϦΫΤετ1ຊͰ͍ΖΜͳ΋ͷΛίϯτϩʔϧ
    • ࣗ෼ͷཉ͍͠Ϟϊͭ͘ΔͨΊʹ৭ʑܨ͍ͰΔ͚ͩͰݟ͑ͯ
    ͘Δੈք͕͋ΔΑʂ

    View full-size slide

  101. ·ͱΊ
    • Android BikeͬͯͷΛ΍ͬͯΈͨ
    • BLEͱ୺຤ͷηϯαΛ࢖ͬͨαΠίϯ࡞Δͷͨͷ͠ʔʂ
    • ࠓޙ΋ػೳ଍͍͖͍ͯͨ͠
    • SDK࡞֦ͬͯுՄೳʹɺServiceԽɺ૸ͬͨه࿥Λอଘ
    (Google Fitͱ͔Stravaͱ͔)ɺࣗಈͰىಈͯ͠ه࿥։࢝
    (Awareness API)ɺ௿ফඅిྗԽ
    • ΦʔϓϯιʔεͰ͢ʂʂʂϓϧϦΫΑΖ͘͠ʂʂʂʂʂ
    • https://github.com/tnj/android-bike-proto

    View full-size slide