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.

0ab502179956dab407e6e146bce304f9?s=128

Yuki Fujisaki

March 09, 2017
Tweet

Transcript

  1. Android BikeΛ࡞Ζ͏ @tnj

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

  3. ࠓ೔ͷ࿩ʢ໿30෼ʣ

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

    • ஌ࣝ΍ମݧΛڞ༗͍ͨ͠
  6. ର৅ • ͬ͘͟ΓAndroid։ൃʹ৮ΕͯΔ͚Ͳ • Bluetoothͱ͔ηϯαͱ͔࢖ͬͨΞϓϦ͸
 ࡞ͬͨ͜ͱ͕ͳ͍ • IoTಐఃͷਓ

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

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

    Θ͔ͬͨ͜ͱ • ·ͱΊͱࠓޙ
  9. εϥΠυɺίʔυɺΞϓϦ
 ্͕ͬͯ·͢ URL ͸ Twitter: @tnj Ͱ

  10. ͖͔͚ͬ

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

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

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

  14. ళһ ʮ͜ͷ্Ґػछͩͱඪ४૷උͷ΍ͭɺ ͚ͭͱ͘ͱεϚϗͱ͔Ͱ
 ݟΕͨΓ͠·͢Αʙʯ

  15. Θ͔ͬͨ͜ͱ

  16. 21ੈلͷࣗసं͸
 BluetoothΛ஻Δ

  17. ࣗసं͕BluetoothΛ஻Δ • ंମ͕࠷ॳ͔ΒηϯαReady • ্Ґػछͩͱඪ४૷උͱ͔ • ANT+͕ྲྀߦ͚ͬͯͨͲ
 ࠷ۙ͸Bluetooth Low EnergyԽ͕ਐΜͰΔ

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

  19. …͚Ͳɺͬ͘͠Γ͜ͳ͍ • ͳΜ͔ݟͨ໨͕޷͖͡Όͳ͔ͬͨΓ
 ૢ࡞͕ҟৗʹ೉͔ͬͨ͠Γ • ηϯαରԠ͕έΠσϯε͚ͩͩͬͨΓ • ඪߴ/ࣼ౓ͷද͕ࣔ·ͬͨ͘ΞςʹͳΒͳ͍

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

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

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

  23. ͬͦ͘͞Bluetooth LE࢖͏ͧ

  24. …ͷ·͑ʹ

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

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

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

  28. ͱΓ࣮͋͑ͣػͰऔͬͯΈΑ͏ ΍Δ͜ͱ͸ׂͱγϯϓϧ 1. SensorManager Ͱ
 TYPE_PRESSURE ͳηϯαΛ΋Βͬͯ͘Δ 2. SensorEventListener Λ࣮૷͢Δ

    3. ϦεφΛ SensorManager ʹొ࿥͢Δ
  29. 1. SensorManagerͰ
 TYPE_PRESSURE ͳηϯαΛ
 ΋Βͬͯ͘Δ sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); pressure

    = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
  30. 2. SensorEventListener Λ
 ࣮૷͢Δ @Override public void onSensorChanged(SensorEvent event) {

    if (event.sensor == pressure) { // event.values[0] ʹ float Ͱؾѹ஋͕ೖ͍ͬͯΔ Log.v(TAG, "Pressure: " + event.values[0]); } }
  31. 3. ϦεφΛొ࿥͢Δ sensorManager.registerListener( this, pressure, SensorManager.SENSOR_DELAY_NORMAL );

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

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

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

    TYPE_TEMPERATURE (deprecated) Ͱ΋
 औΕͳ͍
  35. શ෦ྻڍͯ͠୳ͯ͠Έͨ sensor.getName(); // => BMP280 temperature sensor.getStringType(); // => com.google.sensor.internal_temperature

    sensor.getType(); // => Type=65536 • TYPE_INTERNAL_TEMPERATURE(API 24+) ͩͱ 65537 ❓
  36. ద౰ʹϏϡʔʹηοτͨ͠Γ

  37. None
  38. ͳΜ͔ͦΕͳΓʹಈ͍ͯΔ • ͔͠͠஋͕ඈΜͩΓ͢Δ • ͍͖ͳΓ5mͱ͔ඈͿ͜ͱ͕͋Δ • ͖ͭΊʹLow Pass FilterΛֻ͚Δ •

    ؆қ࣮૷ lastPressure = lastPressure * 0.95f + currentPressure * 0.05f;
  39. ͦΜͳʹ༳Εͳ͘ͳΔ

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

    SensorManager.getAltitude(pressure, basePressure);
  41. ஌ݟ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.
  42. ຬΛ࣋ͯ͠Bluetooth LE΁

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

  44. Bluetooth Low Energy (Bluetooth 4.0) • Ϙλϯి஑1ݸͰ਺ϲ݄ಈ͔ͤΔ͙Β͍௿ফඅ ిྗʹৼͬͨن֨ • ௨৴ස౓ɺ௨৴ྔ͕࠷௿ݶʢ௨৴ػثͷి

    ஑ফඅͷཁʣ • ࢖͏νϟϯωϧݮͬͨΓɺϓϩτίϧ͸Ҏ લͱޓ׵ੑͳ͍
  45. ΫϥΠΞϯταʔόϞσϧ • ࠓճͩͱ • ࣗసं(ϖϦϑΣϥϧ)͕αʔό • Nexus 6P(ηϯτϥϧ)͕ΫϥΠΞϯτ • ϖϦϑΣϥϧ͕ʮԶͩʔͬʂʯͬͯΞυόλ

    ΠδϯάΛਨΕྲྀͯ͠ΔͷΛɺηϯτϥϧ͕ εΩϟϯͯ͠ݟ͚ͭΔ
  46. AndroidͱBLE • Android 4.3Ҏ্Ͱ࢖͑Δ • Ͱ΋5Ͱ΋6Ͱ΋ͦΕͧΕڍಈ͕ҧ͍ͬͯΔ • άάͬͯΔͱઌۦऀͷࢡΛ௒͍͑ͯ͘ײ͕͍͢͝ • ͓खܰʹ͍ͨ͠ͷͰࠓճ͸7͔͠ݟ·ͤΜʂʂ

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

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

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

  50. GATT.Service.Characteristic • GATT • has_many Services
 (ࣗసंαʔϏεɺόοςϦʔαʔϏεͱ͔) • has_many Characteristics


    (ճస਺΍ɺൃੜ࣌ࠁͳͲ࣮ࡍͷ஋) • Service, Characteristic͸ͦΕͧΕܾ·ͬͨUUID ׂ͕Γ౰ͯΒΕ͓ͯΓUUIDͰݕࡧͰ͖Δ
  51. ࠓճ࢖͏Service: CSC • Cycling Speed and Cadence Service • ʮBLEରԠʯͬͯࣗసंεϐʔυ/έΠσϯε

    ηϯα͸͜ΕΛ࣮૷ͯ͠੡඼Խͯ͠Δ
  52. …ͷதͷCharacteristic CSC Measurement Characteristic • ϗΠʔϧͱΫϥϯΫͦΕͧΕͷ • ճస਺ • ࠷ޙʹΠϕϯτ͕ൃੜͨ࣌ؒ͠

    • ී௨͸1ඵִؒͰૹ৴͞ΕΔ • ΫϥϯΫͷ஋ͰέΠσϯε͕෼͔Δ • ϗΠʔϧճస਺ʹλΠϠܘΛֻ͚ͯ΍Ε͹଎౓͕෼͔Δ
  53. 19ϖʔδͷ࢓༷ॻPDF

  54. Specશ෦ಡΜͰ
 ΍ͬͯ΋͍͍͚Ͳ… ͭΒͦ͏

  55. ٹੈओ nRF Toolbox for BLE

  56. nRF Toolbox for BLE • Nordic Semiconductor • ௒௿ফඅిྗͷۙڑ཭ແઢʹڧ͍௨৴Ϟ δϡʔϧͷձࣾ

    • ֤छϓϩϑΝΠϧͷσϞΞϓϦ • Φʔϓϯιʔε (TV΋Wear΋ରԠ) • New BSDϥΠηϯε
  57. ಡΈͳ͕Βཧղ ࢖͍ճ͢ʹ͸ن໛͕େ͖͍ͷͰ࣮૷͸ࣗલ

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

  59. by @hotchemi

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

  61. 2. onRequestPermissionsResult Λதܧͯ͠४උ׬ྃ @Override public void onRequestPermissionsResult( int requestCode, @NonNull

    String[] permissions, @NonNull int[] grantResults) { MainActivityPermissionsDispatcher .onRequestPermissionsResult(this, requestCode, grantResults); };
  62. 3. ύʔϛογϣϯ͕ඞཁͳ
 ؔ਺ʹΞϊςʔγϣϯॻ͍ͯ @NeedsPermission(Manifest.permission.ACCESS_FINE_LOCATION) void startBleScan() {

  63. 4. ؔ਺ݺͼग़͠Λ
 ஔ͖׵͑ͯ΍Δ // startBleScan(); ΛҎԼʹஔ͖׵͑ MainActivityPermissionsDispatcher .startBleScanWithCheck(this);

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

  65. ૣ଎σόΠεΛ୳ͯ͠σʔλΛ औಘ͍ͨ͠ʂʂ ΍Δ͜ͱ 1. पғͷσόΠεΛεΩϟϯ͢Δ 2. ݟ͔ͭͬͨσόΠε಺ͷGATTαʔόʹ઀ଓ͢Δ 3. GATTαʔό͔ΒCSC ServiceΛऔಘ͢Δ

    4. CSC Service͕࣋ͬͯΔCSC Measurement CharacteristicΛऔಘ 5. ߋ৽͕͋ͬͨΒ௨஌(Notification)ΛૹͬͯͶͱཁٻ͢Δ
  66. 1. ·ͣ͸εΩϟφʔΛ΋Β͏ // Bluetooth Service Λ΋Βͬͯ BluetoothManager bluetoothManager = (BluetoothManager)

    context.getSystemService(Context.BLUETOOTH_SERVICE); // Bluetooth ΞμϓλΛ΋Βͬͯ BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); // Ξμϓλ͕࣋ͬͯΔ BLE ͷεΩϟφΛ΋Βͬͯ͘Δ bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
  67. ͰɺεΩϟϯ͢Δ // εΩϟϯઃఆ ScanSettings settings = new ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_BALANCED) //

    ଞLOW_LATENCYͱ͔ .build(); // CSC Service ͚ͩݟ͚ͭͯཉ͍͠ͷͰϑΟϧλΛઃఆ͢Δ List<ScanFilter> filters = new ArrayList<>(); filters.add(new ScanFilter.Builder() .setServiceUuid(new ParcelUuid(SERVICE_UUID)) .build()); // εΩϟϯΑΖ͘͠! bluetoothLeScanner.startScan(filters, settings, callback);
  68. 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͢Δɻ
  69. 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Λऔಘ
  70. 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); }
  71. σʔλऔಘ։࢝ʂ @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { //

    σʔλΛௐཧ͢Δ } PO$IBSBDUFSJTUJD$IBOHFE͕ݺͼग़͞ΕΔΑ͏ʹͳΔʂ
 ૹ৴ִؒ͸େମඵʹճ
  72. σʔλΛௐཧ͢Δ • ΍ͬͯ͘Δσʔλ͸όΠτྻ • byte getValue() • όΠτྻΛ·Δͬͱฦ͢ • ಛఆͷόΠτΦϑηοτ͔Β೚ҙͷܕͰऔΓग़͢

    ؔ਺͕༻ҙ͞ΕͯΔ • int getIntValue(formatType, offset)
  73. CSC Measurement͕ૹΔσʔλ • ઌ಄͔Β • 1byte - flags (1: ϗΠʔϧ͋Δ͔,

    2: ΫϥϯΫ͋Δ͔) • ϗΠʔϧ͋ΔͳΒ • uint32 ྦྷੵճసճ਺ • uint16 ࠷ޙͷΠϕϯτ͕͋ͬͨ࣌ؒ • ΫϥϯΫ͋ΔͳΒ • uint16 ྦྷੵճసճ਺ • uint16 ࠷ޙͷΠϕϯτ͕͋ͬͨ࣌ؒ
  74. 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; }
  75. औΕͨʂ • ճస਺ʹλΠϠͷܘΛֻ͚ͯ΍Ε͹଎౓͕ग़Δʂ • λΠϠͷܘ͸ 2096mm (700x23C)

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

  77. ը໘Λ࡞͍ͬͯͧ͘ • େલఏ: എܠ͸ࠇ • AMOLEDͩͱൃޫͯ͠Δ෦෼͔͠ిྗফඅ͠ͳ͍ • ϨΠΞ΢τ͸ ContraintLayout (Support

    Library) • ཁૉؒͰؔ܎ੑΛηοτ͍ͯ͘͠ • ৘ใΛิ׬͢ΔΞχϝʔγϣϯ • ਺ࣈΛग़͚ͩ͢Ͱͳ͘ɺಈ͖Ͱঢ়گΛ఻͑Δ • ઐ༻ͷViewΛ࡞Δ
  78. ConstraintLayout

  79. ConstraintLayout • ྫ: ୯Ґදࣔ • ࠨ୺Λ΄͔ͷߦͱἧ͑ͭͭ • ॎͷҐஔ͸ಉ͡ߦͷ਺஋ͷϕʔεϥΠϯʹἧ͑Δ • …ͳΜͯࢦఆ͕؆୯ʹͰ͖ͯΑ͍

    • খ਺఺ҎԼͷද͚ࣔͩผͷ TextView ʹͯ͠ϑΥϯταΠζΛ খ͘͢͞Δ…ͳΜͯ΍ͬͯ΋มʹζϨͨΓ͠ͳ͍ • GUIͰҰ௨ΓઃఆͰ͖Δ͚Ͳɺͪΐͬͱෆ۩߹͋ͬͨΓ͢Δ ͷͰඍௐ੔͸XMLͰ΍Δ͔ͳʔͬͯײ͡
  80. ValueAnimator • 1ඵʹ1ճདྷΔ਺ࣈΛ࣍ʑ setText() ͢Δ͚ͩ Ͱ΋͍͍͚Ͳɺຯؾͳ͍ • νϥݟͰ૿͑ͯΔͷ͔ݮͬͯΔͷ͔෼͔Βͳ͍ • ਺ࣈΛঃʑʹมԽͤ͞Δ

  81. ͜͏΍ͬͯ 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)); });
  82. ஋͕͘Δͨͼʹɺݱࡏ஋
 →৽͍͠஋Ͱ setFloatValues ͢Δ private void setAnimatorValue(ValueAnimator animator, float newValue)

    { float currentValue = (Float) animator.getAnimatedValue(); animator.setFloatValues(currentValue, newValue); animator.start(); }
  83. …ͱ1ඵؒͰ਺ࣈ͕௚ઢతʹ૿ݮ͢Δ Լʢճస਺ʣ͸ͦͷ··ɺ
 ্ʢεϐʔυʣ͕༗ޮʹͯ͠Δ΍ͭ

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

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

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

  87. ॎઢҰຊͷ଎౓ϝʔλʔ (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); }
  88. ͋ͱ͸஋Ληοτͯ͠ invalidate() public void setCurrent(float current) { this.current = current;

    invalidate(); }
  89. ͘Δ͘ΔճΔ΍ͭ (CircularRevolutionsView) • ࣮ࡍͷճసΛ࠶ݱ͢Δͱ͓΋͠Ζͦ͏ͩͳʁ • ValueAnimator Ͱ 0.0f → 1.0f

    ΛӬٱϧʔ ϓɺ 1 ճసʹ͔͚Δ࣌ؒ (duration) Λߋ৽͠ ͍ͯ͘ • ͋ͱ͸ԁΛඳ͘ɺεϜʔζʹճͯ͠΋͍͍͚ Ͳ45°ͣͭʹ͢ΔͱͦΕͬΆ͍͔ͳʁ
  90. 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); }
  91. ݁Ռɺ͜͏ͳΓ·ͨ͠

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

  93. ͢ΜͳΓͰ͖ͨΑ͏ʹ
 ݟ͑ΔͰ͠ΐ͏ʁ

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

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

    • 7.1.2Ͱ࣏ͬͯΔ໛༷
  96. ߴ͕͞෼͔ΔΑ͏ʹͳͬͨ
 ࣌ͷ࿩

  97. ࣮ࡍʹ૸Δͧʂ …͋ΕΕߴ౓ͷ਺ࣈ͕શવ͓͔͍ͧ͠ʁ
 ؾѹηϯα͡Όμϝͳͷ͔ʁ

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

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

  100. ࢥ͍ࠐΈͰ࣌ؒϩε • ʮؾѹηϯαʔͩΊ͔ʔʯͱࢥ͍͜Μ͡Όͬͨ • ٸ͍Ͱ܏ࣼΛOrientation(ʹՃ଎౓ͱॏྗ)Ͱग़ͦ͏ͱࢼΈ ͨΓͨ͠ • ٸ͍Ͱίϐϖͯͦ͠ΕͬΆ͍ͷ͕Ͱ͖ͨʂ૸ͬͯΈΑ͏ʂʂ • ಈ͖ͩͨ͠ॠؒ஋͕ඈΜͰ͍͘…

    • Ճ଎౓ͷ஋Λॏྗͷ஋ͱֻ͚߹Θͤͯຊମͷ֯౓Λݕ஌͢Δ ࢓૊Έ • ಈ͍ͨΒਖ਼͘͠൑ఆͰ͖ͳ͍ͷ౰ͨΓલ͡ΌΜ…
  101. ͳͲͳͲ͋Γ·͕ͨ͠

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

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

  104. !?

  105. ࢥΘ͵ॴʹ • API͕͋ͬͨΓ͢Δ • ಛʹެࣜͰެ։͞Εͯͳ͍͚Ͳ֤छݴޠͰϥΠϒϥϦ͕ ࣮૷͞ΕͯΔ • APIͳͯ͘΋੺֎ઢϦϞίϯ͋Δ΋ͷ͸IRKitͱ͔Ͱૢ࡞Ͱ ͖Δ •

    HTTPϦΫΤετ1ຊͰ͍ΖΜͳ΋ͷΛίϯτϩʔϧ • ࣗ෼ͷཉ͍͠Ϟϊͭ͘ΔͨΊʹ৭ʑܨ͍ͰΔ͚ͩͰݟ͑ͯ ͘Δੈք͕͋ΔΑʂ
  106. ·ͱΊ • Android BikeͬͯͷΛ΍ͬͯΈͨ • BLEͱ୺຤ͷηϯαΛ࢖ͬͨαΠίϯ࡞Δͷͨͷ͠ʔʂ • ࠓޙ΋ػೳ଍͍͖͍ͯͨ͠ • SDK࡞֦ͬͯுՄೳʹɺServiceԽɺ૸ͬͨه࿥Λอଘ

    (Google Fitͱ͔Stravaͱ͔)ɺࣗಈͰىಈͯ͠ه࿥։࢝ (Awareness API)ɺ௿ফඅిྗԽ • ΦʔϓϯιʔεͰ͢ʂʂʂϓϧϦΫΑΖ͘͠ʂʂʂʂʂ • https://github.com/tnj/android-bike-proto