image/svg+xml $ $ ing$ ing$ ces$ ces$ Res Res ea ea Res->ea ou ou Res->ou r r ea->r ch ch ea->ch r->ces$ r->ch ch->$ ch->ing$ T T T->ea ou->r

Vibreur

Utilisation de l'API

Exemple : un service de vibration

// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License

package fr.upem.coursand.morse

import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.IBinder
import android.os.Vibrator
import android.widget.Toast
import androidx.core.content.ContextCompat.getSystemService

/** A service that execute vibration sequences */
class VibratorService : Service() {
    companion object {
        val VIBRATION_ACTION = javaClass.name + ".vibrate"

        /** Test if the vibrator is available on the current device */
        fun isVibratorAvailable(context: Context) =
                (context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator).let { it.hasVibrator() }
    }

    lateinit var vibrator: Vibrator
    lateinit var handler: Handler
    val vibrationEnder = Runnable { vibrator.cancel() }

    override fun onCreate() {
        super.onCreate()
        vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
        handler = Handler() // create an handler to control the end of vibration
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (intent?.action == VIBRATION_ACTION) {
            handler.removeCallbacks(vibrationEnder) // remove the planified end of previous vibration pttern
            vibrationEnder.run() // cancel now the possible current vibration pattern
            val pattern = intent.getLongArrayExtra("pattern")
            val duration = intent.getLongExtra("duration", -1) // duration in millis, -1 if infinite
            // execute the vibration pattern
            if (duration >= 0)
                Toast.makeText(this, "Start vibrator service for ${duration} seconds", Toast.LENGTH_SHORT).show()
            vibrator.vibrate(pattern, 0)
            if (duration != -1L) // schedule the end of vibration pattern with the handler
                handler.postDelayed(vibrationEnder, duration)
        }
        return START_NOT_STICKY
    }

    override fun onDestroy() {
        super.onDestroy()
        handler.removeCallbacks(vibrationEnder)
        vibrationEnder.run()
    }

    override fun onBind(intent: Intent): IBinder {
        throw NotImplementedError()
    }
}

LED de signalisation

LED torche

Exemple : un service de séquence d'illumination (s'utilisant sur le même modèle que le service de vibration)

// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License

package fr.upem.coursand.morse

import android.Manifest
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.hardware.Camera
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.os.Build
import android.os.IBinder
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import android.util.Log
import android.widget.Toast
import java.util.concurrent.Executor
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Future

class TorchService : Service() {
    companion object {
        val START_TORCH_ACTION = javaClass.name + ".startTorch"

        /** test if the torch is available on the current device and if the camera permission is granted */
        fun isTorchAvailable(context: Context) =
                context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH) &&
                ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED
    }

    var camera: Camera? = null
    lateinit var executor: ExecutorService
    var currentTorchTask: Future<*>? = null

    override fun onCreate() {
        super.onCreate()
        executor = Executors.newSingleThreadExecutor() // create a thread executor to control the torch
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (intent?.action == START_TORCH_ACTION) {
            val pattern = intent.getLongArrayExtra("pattern")!!
            val duration = intent.getLongExtra("duration", -1) // duration in millis, -1 if infinite
            if (duration >= 0)
                Toast.makeText(this, "Start torch service for ${duration} seconds", Toast.LENGTH_SHORT).show()
            currentTorchTask?.cancel(true) // cancel the current torch task if existing
            currentTorchTask = executor.submit {
                Log.i(javaClass.name, "Starting torch with pattern ${pattern}")
                try {
                    var index = 0
                    while (!Thread.interrupted()) {
                        when (index % 2) {
                            0 -> Thread.sleep(pattern[index])
                            1 -> activateTorch(pattern[index])
                        }
                        index = (index + 1) % pattern.size
                    }
                } finally {
                    closeTorch()
                }
            }
        }
        return START_NOT_STICKY
    }

    private fun activateTorch(duration: Long) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
            activateTorch1(duration)
        else
            activateTorch2(duration)
    }

    /** Oldest way to activate torch for API < M */
    private fun activateTorch1(duration: Long) {
        if (camera == null) camera = Camera.open()
        Log.i(javaClass.name, "Camera open")
        if (duration > 0L)
            camera?.apply {
                val params = parameters
                params.flashMode = Camera.Parameters.FLASH_MODE_TORCH
                parameters = params
                startPreview()
                Log.i(javaClass.name, "Activating torch")
                try {
                    Thread.sleep(duration)
                    val params2 = parameters
                    params.flashMode = Camera.Parameters.FLASH_MODE_OFF
                    parameters = params2
                } finally {
                    stopPreview() // executed even if there is an InterruptedException
                    Log.i(javaClass.name, "Disabling torch")
                }
            }

    }


    /** New way to activate the torch for API >= M */
    @RequiresApi(Build.VERSION_CODES.M)
    private fun activateTorch2(duration: Long) {
        if (duration > 0L) {
            val cm = getSystemService(Context.CAMERA_SERVICE) as CameraManager
            if (cm != null) {
                val cameraId = cm.cameraIdList.find { cm.getCameraCharacteristics(it)[CameraCharacteristics.FLASH_INFO_AVAILABLE] == true }
                if (cameraId != null) {
                    cm.setTorchMode(cameraId, true)
                    try {
                        Thread.sleep(duration)
                    } finally {
                        cm.setTorchMode(cameraId, false)
                    }
                }
            }
        }
    }

    private fun closeTorch() {
        camera?.release()
        camera = null
    }

    override fun onDestroy() {
        super.onDestroy()
        executor.shutdownNow() // stop the running thread for the torch
    }


    override fun onBind(intent: Intent): IBinder {
        TODO("Return the communication channel to the service.")
    }
}

Exemple : activité d'émission de messages en Morse par vibrations ou LED

Activité s'appuyant sur les services précédemment écrits (VibrationService et TorchService)

// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License

package fr.upem.coursand.morse

import android.Manifest
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import fr.upem.coursand.R
import fr.upem.coursand.launcher.ActivityMetadata
import kotlinx.android.synthetic.main.activity_morse.*

@ActivityMetadata(permissions= [Manifest.permission.CAMERA, "android.permission.FLASHLIGHT"])
class MorseActivity : AppCompatActivity() {

    val TIME_QUANTUM = 100L // in millis

    private fun createIntent(vibrator: Boolean) =
        Intent(this, if (vibrator) VibratorService::class.java else TorchService::class.java)

    // Create the Intent used to start the service
    private fun createStartIntent(): Intent? {
        val intent = when {
            vibratorView.isChecked -> createIntent(true).setAction(VibratorService.VIBRATION_ACTION)
            torchView.isChecked -> createIntent(false).setAction(TorchService.START_TORCH_ACTION)
            else -> null
        }
        val pattern = messageView.text.toString().toMorsePattern(TIME_QUANTUM)
        intent?.putExtra("pattern", pattern)
        intent?.putExtra("duration", durationView.text.toString().toLongOrNull()?.times(1000L) ?: -1L) // in millis
        return intent
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_morse)
        vibratorView.isEnabled = VibratorService.isVibratorAvailable(this)
        torchView.isEnabled = TorchService.isTorchAvailable(this)

        // start the service for vibration or torch (according to the checked radio button)
        startButton.setOnClickListener {
            val intent = createStartIntent()
            if (intent != null) startService(intent) }

        // stop all the services
        // calling stopService on a non-started service does nothing
        stopButton.setOnClickListener { listOf(false, true).forEach { stopService(createIntent(it)) } }
    }
}