Skip to content

Accelerometer


title: Accelerometer Examples tags: - Sensor chapter: Accelerometer post_status: publish date: 2022-10-12 summary: Learn Accelerometer usage in android via simple examples. custom_field: chapter: Accelerometer Tutorial chapter_description: An accelerometer is a tool that measures proper acceleration. Let us look at example usage in android.


Step by step Accelerometer examples for android.

Learn programmatic usage of accelerometer in your mobile app.

What is an Accelerometer?

An accelerometer is a tool that measures proper acceleration. Proper acceleration is the acceleration of a body in its own instantaneous rest frame; this is different from coordinate acceleration, which is acceleration in a fixed coordinate system

Majprity of high-end modern smartphones tend to be fitted with an accelerometer. Thus we need to learn how to use it programmatically.

Here are some examples:

Kotlin OpenGL with Accelerometer Sensor

Simple android project with implementation of openGL SE circle shape, accelerometer sensor for measuring phone rotation.

Full Example

Let us look at a full android OpenGL sample project.

Step 1. Dependencies

We need to add some dependencies in our app/build.gradle file as shown below:

(a). build.gradle

Our app-level build.gradle.

We Prepare our dependencies as shown below. You may use later versions.

At the top of our app/build.gradle we will apply the following 3 plugins:

  1. Our com.android.application plugin.
  2. Our kotlin-android plugin.
  3. Our kotlin-android-extensions plugin.

We then declare our app dependencies under the dependencies closure. We will need the following 4 dependencies:

  1. Our Kotlin-stdlib library.
  2. Our Core-ktx library.
  3. Our Appcompat library.
  4. Our Constraintlayout library.

Here is our full app/build.gradle:

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 30
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "pl.polsl.openglapp"
        minSdkVersion 26
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

}

Step 2. Our Android Manifest

(a). AndroidManifest.xml

Our AndroidManifest file.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="pl.polsl.openglapp">
    <uses-feature android:glEsVersion="0x00020000" android:required="true" />
    <supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" />
    <supports-gl-texture android:name="GL_OES_compressed_paletted_texture" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity"
                  android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Step 3. Design Layouts

(a). activity_main.xml

Our activity_main layout.

Design your XML layout using the following 2 UI widgets:

  1. androidx.constraintlayout.widget.ConstraintLayout
  2. TextView
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Step 4. Write Code

Finally we need to write our code as follows:

(a). GLCircle.kt

Our GLCircle class.

Create a Kotlin file named GLCircle.kt.

We will then add imports from android SDK and other packages. Here are some of the imports we will use in this class:

  1. Resources from the android.content.res package.
  2. GLES20 from the android.opengl package.
  3. Matrix from the android.opengl package.
  4. Log from the android.util package.

We will be creating the following methods:

  1. getModelMatrix(): FloatArray.
  2. draw(parameter) - We pass a FloatArray? object as a parameter.

(a). Our getModelMatrix() function

Write the getModelMatrix() function as follows:

    fun getModelMatrix(): FloatArray {
        return mModelMatrix;
    }

(b). Our draw() function

Write the draw() function as follows:

    fun draw(mvpMatrix: FloatArray?){
        GLES20.glUseProgram(mProgram)
        positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition").also {
        GLES20.glEnableVertexAttribArray(it)

            GLES20.glVertexAttribPointer(
                it,
                COORDS_PER_VERTEX,
                GLES20.GL_FLOAT,
                false,
                vertexStride,
                mVertexBuffer
            )

            mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor")
            GLES20.glUniform4fv(mColorHandle, 1, color, 0)

            mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix")
            GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0)
            GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 364)
            GLES20.glDisableVertexAttribArray(it)
        }
    }

Here is the full code:

package replace_with_your_package_name

import GLRenderer.Companion.loadShader
import android.content.res.Resources
import android.opengl.GLES20
import android.opengl.Matrix
import android.util.Log
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin

const val COORDS_PER_VERTEX = 3
var circleCoords = floatArrayOf(     // in counterclockwise order:
    0.0f, 0.622008459f, 0.0f,        // top
    -0.5f, -0.311004243f, 0.0f,      // bottom left
    0.5f, -0.311004243f, 0.0f        // bottom right
)

class Circle {
    public val mModelMatrix = FloatArray(16)
    val color = floatArrayOf(0f, 0f, 1f, 1.0f)
    private var positionHandle: Int = 0
    private var mColorHandle: Int = 0
    private var mMVPMatrixHandle = 0
    private val mVertexBuffer: FloatBuffer
    private val vertexCount: Int = circleCoords.size / COORDS_PER_VERTEX
    private val vertexStride: Int = COORDS_PER_VERTEX * 4
    private val vertices = FloatArray(364 * 3)
    private var mProgram: Int

    private val vertexShaderCode =
        "uniform mat4 uMVPMatrix;" +
        "attribute vec4 vPosition;" +
                "void main() {" +
                " gl_Position = uMVPMatrix * vPosition;" +
                "}"

    private val fragmentShaderCode =
        "precision mediump float;" +
                "uniform vec4 vColor;" +
                "void main() {" +
                "  gl_FragColor = vColor;" +
                "}"


    init {
        Matrix.setIdentityM(mModelMatrix, 0)
        vertices[0] = 0F
        vertices[1] = 0F
        vertices[2] = 0F
        val ratio = Resources.getSystem().displayMetrics.widthPixels.toFloat() / Resources.getSystem().displayMetrics.heightPixels.toFloat()
        for (i in 1..363) {
            vertices[i * 3 + 0] = (ratio * 0.1 * cos(3.14 / 180 * i.toFloat())).toFloat()
            vertices[i * 3 + 1] = (ratio * 0.1* sin(3.14 / 180 * i.toFloat())).toFloat()
            vertices[i * 3 + 2] = 0F
        }
        val vertexByteBuffer = ByteBuffer.allocateDirect(vertices.size * 4)
        vertexByteBuffer.order(ByteOrder.nativeOrder())
        mVertexBuffer = vertexByteBuffer.asFloatBuffer()
        mVertexBuffer.put(vertices)
        mVertexBuffer.position(0)
        val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
        val fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)
        mProgram = GLES20.glCreateProgram()
        GLES20.glAttachShader(mProgram, vertexShader)
        GLES20.glAttachShader(mProgram, fragmentShader)
        GLES20.glLinkProgram(mProgram)
    }

    fun getModelMatrix(): FloatArray {
        return mModelMatrix;
    }

    fun draw(mvpMatrix: FloatArray?){
        GLES20.glUseProgram(mProgram)
        positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition").also {
        GLES20.glEnableVertexAttribArray(it)

            GLES20.glVertexAttribPointer(
                it,
                COORDS_PER_VERTEX,
                GLES20.GL_FLOAT,
                false,
                vertexStride,
                mVertexBuffer
            )

            mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor")
            GLES20.glUniform4fv(mColorHandle, 1, color, 0)

            mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix")
            GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0)
            GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 364)
            GLES20.glDisableVertexAttribArray(it)
        }
    }


}

(b). GLRenderer.kt

Our GLRenderer class.

Create a Kotlin file named GLRenderer.kt.

We will then add imports from android SDK and other packages. Here are some of the imports we will use in this class:

  1. Context from the android.content package.
  2. Sensor from the android.hardware package.
  3. SensorEvent from the android.hardware package.
  4. SensorEventListener from the android.hardware package.
  5. SensorManager from the android.hardware package.
  6. GLES20 from the android.opengl package.
  7. GLSurfaceView from the android.opengl package.
  8. Matrix from the android.opengl package.
  9. Log from the android.util package.

Next create a class that derives from GLSurfaceView.Renderer, and add its contents as follows:

We will be overriding the following functions:

  1. onSurfaceCreated(unused: GL10, config: EGLConfig).
  2. onDrawFrame(unused: GL10).
  3. onSensorChanged(event: SensorEvent?).
  4. onAccuracyChanged(p0: Sensor?, p1: Int).
  5. onSurfaceChanged(unused: GL10, width: Int, height: Int).

We will be creating the following methods:

  1. loadShader(type: Int, shaderCode: String): Int.

(a). Our loadShader() function

Write the loadShader() function as follows:

        fun loadShader(type: Int, shaderCode: String): Int{
            return GLES20.glCreateShader(type).also{ shader ->
                GLES20.glShaderSource(shader, shaderCode)
                GLES20.glCompileShader(shader)
            }
        }

Here is the full code:

import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10

import android.opengl.GLES20
import android.opengl.GLSurfaceView
import android.opengl.Matrix
import android.util.Log
import pl.polsl.openglapp.Circle
import kotlin.math.abs

class GLRenderer(private var context: Context) : GLSurfaceView.Renderer, SensorEventListener {
    private val vPMatrix = FloatArray(16)
    private lateinit var mCircle: Circle;
    private val projectionMatrix = FloatArray(16)
    private val viewMatrix = FloatArray(16)
    private var ratio = 0f
    private var rotationMatrix = FloatArray(16)
    private val sensorManager: SensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
    private var rotation: Float = 0f
    private var currentVelocity = 1
    private var velocity = 1
    private var direction = 1
    private var timestamp: Float = 0f

    override fun onSurfaceCreated(unused: GL10, config: EGLConfig) {
        val sensorAcc: Sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
        sensorManager.registerListener(this, sensorAcc, SensorManager.SENSOR_DELAY_NORMAL)

        GLES20.glClearColor(0.5f, 1.0f, 0.5f, 1.0f)
        mCircle = Circle ()
    }

    override fun onDrawFrame(unused: GL10) {
        val scratch = FloatArray(16)

        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
        Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, -3f, 0f, 0f, 0f, 0f, 1.0f, 0.0f)
        Matrix.multiplyMM(vPMatrix, 0, projectionMatrix, 0, viewMatrix, 0)


        if(mCircle.getModelMatrix()[12] >= ratio * 0.9){
            if(direction == 1){
                velocity/=2
            }else{
                velocity = 1
            }
        } else if(mCircle.getModelMatrix()[12] <= -ratio * 0.9){
            if(direction == -1){
                velocity/=2
            }else{
                velocity = 1
            }
        }

        Matrix.translateM(mCircle.getModelMatrix(), 0, direction * (abs(rotation)/150) * velocity, 0f, 0f)
        Matrix.multiplyMM(scratch, 0, vPMatrix, 0, mCircle.getModelMatrix(), 0)
        mCircle.draw(scratch)
    }

    override fun onSensorChanged(event: SensorEvent?){
        Log.d("Sensor","onSensorChange")

        if (timestamp != 0f && event != null) {
            rotationMatrix = event.values
            rotation = rotationMatrix[1]
            if(rotation < 0){
                direction = 1
            } else if(rotation > 0){
                direction = -1
            }
            Log.d("xAxis: ", (rotationMatrix[1]).toString())
        }

        timestamp = event?.timestamp?.toFloat() ?: 0f
    }

    override fun onAccuracyChanged(p0: Sensor?, p1: Int) {
    }

    override fun onSurfaceChanged(unused: GL10, width: Int, height: Int) {
        GLES20.glViewport(0, 0, width, height)
        ratio = width.toFloat() / height.toFloat()
        Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)
    }

    companion object{
        fun loadShader(type: Int, shaderCode: String): Int{
            return GLES20.glCreateShader(type).also{ shader ->
                GLES20.glShaderSource(shader, shaderCode)
                GLES20.glCompileShader(shader)
            }
        }
    }
}

(c). GLView.kt

Our GLView class.

Create a Kotlin file named GLView.kt.

We will then add imports from android SDK and other packages. Here are some of the imports we will use in this class:

  1. Context from the android.content package.
  2. GLSurfaceView from the android.opengl package.

Next create a class that derives from GLSurfaceView(context) and add its contents as follows:

Here is the full code:

package replace_with_your_package_name

import GLRenderer
import android.content.Context
import android.opengl.GLSurfaceView

class GLView(context: Context) : GLSurfaceView(context) {

    private val renderer: GLRenderer

    init {
        setEGLContextClientVersion(2)
        renderer = GLRenderer(context)
        setRenderer(renderer)
    }
}

(d). MainActivity.kt

Our MainActivity class.

Create a Kotlin file named MainActivity.kt.

We will then add imports from android SDK and other packages. Here are some of the imports we will use in this class:

  1. GLSurfaceView from the android.opengl package.
  2. AppCompatActivity from the androidx.appcompat.app package.
  3. Bundle from the android.os package.

Next create a class that derives from AppCompatActivity and add its contents as follows:

We will be overriding the following functions:

  1. onCreate(savedInstanceState: Bundle?).

Here is the full code:

package replace_with_your_package_name

import android.opengl.GLSurfaceView
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    private lateinit var gLView: GLSurfaceView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        gLView = GLView(this)
        setContentView(gLView)
    }
}

Reference

Download the code below:

No. Link
1. Download Full Code
2. Read more here.
3. Follow code author here.

Simple Accelerometer Example

Practice of accelerometer in android devices, let's move that ball.

Here is the demo project:

Accelerometer Tutorial

Step 1. Our Android Manifest

We will need to look at our AndroidManifest.xml.

(a). AndroidManifest.xml

Our AndroidManifest file.

Here we will add the following permission:

  1. Our VIBRATE permission.

Here is the full Android Manifest file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.projects.enzoftware.metalball">
    <uses-permission android:name="android.permission.VIBRATE" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MetalBall">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Step 2. Design Layouts

Here are the layouts for this project:

(a). activity_metal_ball.xml

Our activity_metal_ball layout.

Inside your /res/layout/ directory create an xml layout file named activity_metal_ball.xml.

Add these two widgets:

  1. android.support.constraint.ConstraintLayout
  2. TextView
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.projects.enzoftware.metalball.MetalBall">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

Step 3. Write Code

Finally we need to write our code as follows:

(a). MetalBall.kt

Our MetalBall class.

Create a Kotlin file named MetalBall.kt.

We will then add imports from android SDK and other packages. Here are some of the imports we will use in this class:

  1. Service from the android.app package.
  2. BluetoothClass from the android.bluetooth package.
  3. Context from the android.content package.
  4. ActivityInfo from the android.content.pm package.
  5. Bitmap from the android.graphics package.
  6. BitmapFactory from the android.graphics package.
  7. Canvas from the android.graphics package.
  8. Point from the android.graphics package.
  9. Sensor from the android.hardware package.
  10. SensorEvent from the android.hardware package.
  11. SensorEventListener from the android.hardware package.
  12. SensorManager from the android.hardware package.
  13. Build from the android.os package.
  14. AppCompatActivity from the android.support.v7.app package.
  15. Bundle from the android.os package.
  16. Vibrator from the android.os package.
  17. * from the android.view package.

Next create a class that derives from AppCompatActivity and add its contents as follows:

We will be overriding the following functions:

  1. onCreate(savedInstanceState: Bundle?).
  2. onAccuracyChanged(sensor: Sensor?, accuracy: Int).
  3. onSensorChanged(event: SensorEvent?).
  4. onResume().
  5. onPause().
  6. run().
  7. surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int).
  8. surfaceDestroyed(holder: SurfaceHolder?).
  9. surfaceCreated(holder: SurfaceHolder?).
  10. draw(canvas: Canvas?).
  11. onDraw(canvas: Canvas?).

We will be creating the following methods:

  1. setRunning(parameter) - This function will take a Boolean object as a parameter.
  2. updateMe(inx : Float , iny : Float).

(a). Our setRunning() function

Write the setRunning() function as follows:

        fun setRunning(run : Boolean){
            this.run = run
        }

(b). Our updateMe() function

Write the updateMe() function as follows:

    fun updateMe(inx : Float , iny : Float){
        lastGx += inx
        lastGy += iny

        cx += lastGx
        cy += lastGy

        if(cx > (Windowwidth - picWidth)){
            cx = (Windowwidth - picWidth).toFloat()
            lastGx = 0F
            if (noBorderX){
                vibratorService!!.vibrate(100)
                noBorderX = false
            }
        }
        else if(cx < (0)){
            cx = 0F
            lastGx = 0F
            if(noBorderX){
                vibratorService!!.vibrate(100)
                noBorderX = false
            }
        }
        else{ noBorderX = true }

        if (cy > (Windowheight - picHeight)){
            cy = (Windowheight - picHeight).toFloat()
            lastGy = 0F
            if (noBorderY){
                vibratorService!!.vibrate(100)
                noBorderY = false
            }
        }

        else if(cy < (0)){
            cy = 0F
            lastGy = 0F
            if (noBorderY){
                vibratorService!!.vibrate(100)
                noBorderY= false
            }
        }
        else{ noBorderY = true }

        invalidate()

    }

Here is the full code:

package replace_with_your_package_name

import android.app.Service
import android.bluetooth.BluetoothClass
import android.content.Context
import android.content.pm.ActivityInfo
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Point
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Build
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Vibrator
import android.view.*

class MetalBall : AppCompatActivity() , SensorEventListener {

    private var mSensorManager : SensorManager ?= null
    private var mAccelerometer : Sensor ?= null
    var ground : GroundView ?= null


    override fun onCreate(savedInstanceState: Bundle?) {
        requestWindowFeature(Window.FEATURE_NO_TITLE)
        super.onCreate(savedInstanceState)
        // get reference of the service
        mSensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        // focus in accelerometer
        mAccelerometer = mSensorManager!!.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
        // setup the window
        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE

        window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                        WindowManager.LayoutParams.FLAG_FULLSCREEN)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
            window.decorView.systemUiVisibility =   View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
            View.SYSTEM_UI_FLAG_FULLSCREEN
            View.SYSTEM_UI_FLAG_IMMERSIVE
        }

        // set the view
        ground = GroundView(this)
        setContentView(ground)
    }


    override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
    }

    override fun onSensorChanged(event: SensorEvent?) {
        if (event != null) {
            ground!!.updateMe(event.values[1] , event.values[0])
        }
    }

    override fun onResume() {
        super.onResume()
        mSensorManager!!.registerListener(this,mAccelerometer,
                                        SensorManager.SENSOR_DELAY_GAME)
    }

    override fun onPause() {
        super.onPause()
        mSensorManager!!.unregisterListener(this)
    }

    class DrawThread (surfaceHolder: SurfaceHolder , panel : GroundView) : Thread() {
        private var surfaceHolder :SurfaceHolder ?= null
        private var panel : GroundView ?= null
        private var run = false

        init {
            this.surfaceHolder = surfaceHolder
            this.panel = panel
        }

        fun setRunning(run : Boolean){
            this.run = run
        }

        override fun run() {
            var c: Canvas ?= null
            while (run){
                c = null
                try {
                    c = surfaceHolder!!.lockCanvas(null)
                    synchronized(surfaceHolder!!){
                        panel!!.draw(c)
                    }
                }finally {
                    if (c!= null){
                        surfaceHolder!!.unlockCanvasAndPost(c)
                    }
                }
            }
        }

    }

}


class GroundView(context: Context?) : SurfaceView(context), SurfaceHolder.Callback{

    // ball coordinates
    var cx : Float = 10.toFloat()
    var cy : Float = 10.toFloat()

    // last position increment

    var lastGx : Float = 0.toFloat()
    var lastGy : Float = 0.toFloat()

    // graphic size of the ball

    var picHeight: Int = 0
    var picWidth : Int = 0

    var icon:Bitmap ?= null

    // window size

    var Windowwidth : Int = 0
    var Windowheight : Int = 0

    // is touching the edge ?

    var noBorderX = false
    var noBorderY = false

    var vibratorService : Vibrator ?= null
    var thread : MetalBall.DrawThread?= null



    init {
        holder.addCallback(this)
        //create a thread
        thread = MetalBall.DrawThread(holder, this)
        // get references and sizes of the objects
        val display: Display = (getContext().getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay
        val size:Point = Point()
        display.getSize(size)
        Windowwidth = size.x
        Windowheight = size.y
        icon = BitmapFactory.decodeResource(resources,R.drawable.ball)
        picHeight = icon!!.height
        picWidth = icon!!.width
        vibratorService = (getContext().getSystemService(Service.VIBRATOR_SERVICE)) as Vibrator
    }

    override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
    }

    override fun surfaceDestroyed(holder: SurfaceHolder?) {
    }

    override fun surfaceCreated(holder: SurfaceHolder?) {
        thread!!.setRunning(true)
        thread!!.start()
    }

    override fun draw(canvas: Canvas?) {
        super.draw(canvas)
        if (canvas != null){
            canvas.drawColor(0xFFAAAAA)
            canvas.drawBitmap(icon,cx,cy,null)
        }
    }

    override public fun onDraw(canvas: Canvas?) {

        if (canvas != null){
            canvas.drawColor(0xFFAAAAA)
            canvas.drawBitmap(icon,cx,cy,null)
        }
    }

    fun updateMe(inx : Float , iny : Float){
        lastGx += inx
        lastGy += iny

        cx += lastGx
        cy += lastGy

        if(cx > (Windowwidth - picWidth)){
            cx = (Windowwidth - picWidth).toFloat()
            lastGx = 0F
            if (noBorderX){
                vibratorService!!.vibrate(100)
                noBorderX = false
            }
        }
        else if(cx < (0)){
            cx = 0F
            lastGx = 0F
            if(noBorderX){
                vibratorService!!.vibrate(100)
                noBorderX = false
            }
        }
        else{ noBorderX = true }

        if (cy > (Windowheight - picHeight)){
            cy = (Windowheight - picHeight).toFloat()
            lastGy = 0F
            if (noBorderY){
                vibratorService!!.vibrate(100)
                noBorderY = false
            }
        }

        else if(cy < (0)){
            cy = 0F
            lastGy = 0F
            if (noBorderY){
                vibratorService!!.vibrate(100)
                noBorderY= false
            }
        }
        else{ noBorderY = true }

        invalidate()

    }
}

Reference

Download the code below:

No. Link
1. Download Full Code
2. Read more here.
3. Follow code author here.

Accelerometer Experimentation

Experimentation with how to use the accelerometer to make cool effects on Android in Kotlin.

Below is a full android sample to demonstrate Accelerometer concept.

Step 1. Design Layouts

(a). activity_main.xml

Our activity_main layout.

Design your XML layout using the following 2 UI widgets and ViewGroups:

  1. RelativeLayout
  2. TextView
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#212121"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv_square"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        android:background="#EF5350"
        android:gravity="center"
        android:text="Hello"
        android:textColor="@color/white"
        android:textSize="20sp" />


</RelativeLayout>

Step 2. Write Code

Finally we need to write our code as follows:

(a). MainActivity.kt

Our MainActivity class.

Create a Kotlin file named MainActivity.kt.

We will then add imports from android SDK and other packages. Here are some of the imports we will use in this class:

  1. Color from the android.graphics package.
  2. Sensor from the android.hardware package.
  3. SensorEvent from the android.hardware package.
  4. SensorEventListener from the android.hardware package.
  5. SensorManager from the android.hardware package.
  6. AppCompatActivity from the androidx.appcompat.app package.
  7. Bundle from the android.os package.
  8. TextView from the android.widget package.
  9. AppCompatDelegate from the androidx.appcompat.app package.

Next create a class that derives from AppCompatActivity and add its contents as follows:

We will be overriding the following functions:

  1. onCreate(savedInstanceState: Bundle?).
  2. onSensorChanged(event: SensorEvent?).
  3. onAccuracyChanged(sensor: Sensor?, accuracy: Int).
  4. onDestroy().

We will be creating the following methods:

  1. setUpSensorStuff().

(a). Our setUpSensorStuff() function

WesetUpSensorStuff() as follows:

    private fun setUpSensorStuff() {
        // Create the sensor manager
        sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager

        // Specify the sensor you want to listen to
        sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also { accelerometer ->
            sensorManager.registerListener(
                this,
                accelerometer,
                SensorManager.SENSOR_DELAY_FASTEST,
                SensorManager.SENSOR_DELAY_FASTEST
            )
        }
    }

Here is the full code:

package replace_with_your_package_name

import android.graphics.Color
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatDelegate

class MainActivity : AppCompatActivity(), SensorEventListener {

    private lateinit var sensorManager: SensorManager
    private lateinit var square: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Keeps phone in light mode
        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)

        square = findViewById(R.id.tv_square)

        setUpSensorStuff()
    }

    private fun setUpSensorStuff() {
        // Create the sensor manager
        sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager

        // Specify the sensor you want to listen to
        sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also { accelerometer ->
            sensorManager.registerListener(
                this,
                accelerometer,
                SensorManager.SENSOR_DELAY_FASTEST,
                SensorManager.SENSOR_DELAY_FASTEST
            )
        }
    }

    override fun onSensorChanged(event: SensorEvent?) {
        // Checks for the sensor we have registered
        if (event?.sensor?.type == Sensor.TYPE_ACCELEROMETER) {
            //Log.d("Main", "onSensorChanged: sides ${event.values[0]} front/back ${event.values[1]} ")

            // Sides = Tilting phone left(10) and right(-10)
            val sides = event.values[0]

            // Up/Down = Tilting phone up(10), flat (0), upside-down(-10)
            val upDown = event.values[1]

            square.apply {
                rotationX = upDown * 3f
                rotationY = sides * 3f
                rotation = -sides
                translationX = sides * -10
                translationY = upDown * 10
            }

            // Changes the colour of the square if it's completely flat
            val color = if (upDown.toInt() == 0 && sides.toInt() == 0) Color.GREEN else Color.RED
            square.setBackgroundColor(color)

            square.text = "up/down ${upDown.toInt()}\nleft/right ${sides.toInt()}"
        }
    }

    override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
        return
    }

    override fun onDestroy() {
        sensorManager.unregisterListener(this)
        super.onDestroy()
    }
}

Reference

Download the code below:

No. Link
1. Download Full Code
2. Read more here.
3. Follow code author here.