2021-01-31 11:05:37 +01:00
# Android App Activity.kt
## Logging
```kotlin
Log.i("tag", "logValue") //info log
Log.d("tag", "logValue") //debug log
Log.w("tag", "logValue") //warning log
Log.e("tag", "logValue") //error log
Log.c("tag", "logValue") //critical log
```
## Activity Life Cycle
2021-07-01 20:37:27 +02:00

2021-01-31 11:05:37 +01:00
```kotlin
package com.its.< appname >
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
//entry point of the activity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.< activity_xml > )
}
override fun onStart() {
}
override fun onResume() {
}
override fun onPause() {
}
override fun onStop() {
}
override fun onRestart() {
}
override fun onDestroy() {
}
}
```
### Passing data between activities
2021-09-20 19:35:32 +02:00
In *laughing* activity:
2021-01-31 11:05:37 +01:00
```kotlin
private fun openActivity() {
//target to open Intent(opener, opened)
val intent = Intent(this, Activity::class.java)
2021-09-20 19:35:32 +02:00
//pass data to launched activity
2021-01-31 11:05:37 +01:00
intent.putExtra("identifier", value)
startActivity(intent) //launch another activity
}
```
In *launched* activity:
```kotlin
val identifier = intent.get< Type > Extra("identifier")
```
## Hooks
### Resources Hooks
```kotlin
2021-09-20 19:35:32 +02:00
R.< resourceType > .< resourceName > //access to resource
2021-01-31 11:05:37 +01:00
ContextCompat.getColor(this, colorResource) //extract color from resources
getString(stringResource) //extract string from resources
```
### XML hooks
Changes in xml made in kotlin are applied after the app is drawn thus overwriting instructions in xml.
In `activity.xml` :
```xml
< View
android:id="@+id/< id > "/>
```
in `Activity.kt` :
```kotlin
var element = findViewById(R.id.< id > ) //old method
2021-09-20 19:35:32 +02:00
< id > .popery = value //access and modify view contents
2021-01-31 11:05:37 +01:00
```
## Activity Components
### Snackbar
2021-09-20 19:35:32 +02:00
Component derived from material design. If using old API material design dependency must be set in gradle.
2021-01-31 11:05:37 +01:00
In `build.gradle (Module:app)` :
```gradle
dependencies {
implementation 'com.google.android.material:material:< sem_ver > '
}
```
In `Activity.kt` :
```kotlin
import com.google.android.material.snackbar.Snackbar
Snackbar
.make(activityID, message, Snackbar.TIME_ALIVE) //create snackbar
.setAction("Button Name", { action }) //add button to snackbar
.show()
```
## Opening External Content
### Opening URLs
```kotlin
val url = "https://www.google.com"
val intent = Intent(Intent.ACTION_VIEW)
intent.setData(Uri.parse(url))
startActivity(intent)
```
### Sharing Content
```kotlin
val intent = Intent(Intent.ACTION_SEND)
intent.setType("text/plain") //specifying shared content type
2021-09-20 19:35:32 +02:00
intent.putExtra(Intent.EXTRA_MAIL, "mail@address ") //open mail client and pre-compile field if share w/ mail
2021-01-31 11:05:37 +01:00
intent.putExtra(Intent.EXTRA_SUBJECT, "subject")
intent.putExtra(Intent.EXTRA_TEXT, "text") //necessary since type is text
2021-09-20 19:35:32 +02:00
startActivity(Intent.startChooser(intent, "chooser title")) //let user choose the share app
2021-01-31 11:05:37 +01:00
```
### App Google Maps
[Documentation ](https://developers.google.com/maps/documentation/urls/android-intents )
```kotlin
val uri = Uri.parse("geo: < coordinates > ")
val intent = Intent(Intent.ACTION_VIEW, uri)
intent.setPackage("com.google.android.apps.maps") //app to be opened
startActivity(intent)
```
### Make a call (wait for user)
Preset phone number for the call, user needs to press call button to initiate dialing.
```kotlin
fun call() {
val intent = Intent(Intent.ACTION_DIAL)
intent.setData(Uri.parse("tel: < phone number > "))
startActivity(intent)
}
```
### Make a call (directly)
In `AndroidManifest.xml` :
```xml
< uses-permission android:name = "android.permission.CALL_PHONE" / >
```
```kotlin
//https://developer.android.com/training/permissions/requesting
//intercept OS response to permission popup
2021-09-20 19:35:32 +02:00
override fun onRequestPermissionResult(requestCode: Int, permissions: Array< out String > , grantResults: IntArray) {
2021-01-31 11:05:37 +01:00
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
fun checkCallPermission() {
//check if permission to make a call has been granted
if (ContextCompact.checkSelfPermission(context!!, android.Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
2021-09-20 19:35:32 +02:00
// if permission has not been granted request it (opens OS popup, no listener available)
// request code needs to be specific for the permission
ActivityCompat.requestPermissions(context!!, arrayOf(android.Manifest.permission.CALL_PHONE), requestCode)
2021-01-31 11:05:37 +01:00
} else {
call() //if permission has been already given
}
}
2021-09-20 19:35:32 +02:00
@SuppressLint ("MissingPermission") // suppress warning of unhandled permission (handled in checkCallPermission)
2021-01-31 11:05:37 +01:00
fun call() {
val intent = Intent(Intent.ACTION_DIAL, Uri.parse("tel: < phone number > "))
startActivity(intent)
}
```
## Lists (with RecyclerView)
A [LayoutManager][1] is responsible for measuring and positioning item views within a `RecyclerView` as well as determining the policy for when to recycle item views that are no longer visible to the user.
By changing the `LayoutManager` a `RecyclerView` can be used to implement a standard vertically *scrolling list* , a *uniform grid* , *staggered grids* , *horizontally scrolling collections* and more.
Several stock layout managers are provided for general use.
[Adapters][2] provide a binding from an app-specific data set to views that are displayed within a `RecyclerView` .
[1]:https://developer.android.com/reference/kotlin/androidx/recyclerview/widget/RecyclerView.LayoutManager
[2]:https://developer.android.com/reference/kotlin/androidx/recyclerview/widget/RecyclerView.Adapter
```kotlin
var array: ArrayList< T > ? = null //create ArrayList of elements to be displayed
var adapter: RecyclerViewItemAdapter? = null //create adapter to draw the list in the Activity
array.add(item) //add item to ArrayList
// create LayoutManager for the recyclerView
val layoutManager = < ViewGroup > LayoutManager(context, < ViewGroup > LayoutManager.VERTICAL, reverseLayout: Bool)
recycleView.LayoutManager = layoutManager // assign LayoutManager for the recyclerView
// handle adapter var containing null value
if (array != null) {
2021-09-20 19:35:32 +02:00
adapter = RecyclerViewItemAdapter(array!!) // valorize adapter with a adapter object
recyclerView.adapter = adapter // assign adapter to the recyclerView
2021-01-31 11:05:37 +01:00
}
2021-09-20 19:35:32 +02:00
// add or remove item
2021-01-31 11:05:37 +01:00
// tell the adapter that something is changed
adapter?.notifyDataSetChanged()
```
## WebView
[WebView Docs ](https://developerandroid.com/reference/android/webkit/WebView )
```kotlin
webView.webViewClient = object : WebViewClient() {
2021-09-20 19:35:32 +02:00
// avoid opening browsed by default, open webview instead
2021-01-31 11:05:37 +01:00
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
view?.loadUrl(url) // handle every url
return true
}
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
// stay in WebView until website changes
if (Uri.parse(url).host == "www.website.domain") {
return false
}
Intent(Intent.ACTION_VIEW, Uri.parse(url).apply){
2021-09-20 19:35:32 +02:00
startActivity(this) // open browser/app when the website changes to external URL
2021-01-31 11:05:37 +01:00
}
return true
}
webView.settings.javaScriptEnabled = true // enable javascript
webView.addJavaScriptInterface(webAppInterface(this, "Android")) // create bridge between js on website an app
// if in webView use android back butto to go to previous webpage (if possible)
override fun onKeyDown(keyCode: Int, event: KeyEvent?) :Boolean {
if (keyCode == KeyEvent.KEYCODE_BACK & & webVew,canGoBack()) { // if previous url exists & back button pressed
webView.goBack() // go to previous URL
return true
}
}
}
```
## Web Requests (Using Volley)
[Volley Docs ](https://developer.android.com/training/volley )
### Import & Permissions
Import in `build.gradle` :
```kotlin
implementation 'com.android.volley:volley:1.1.1'
```
2021-09-20 19:35:32 +02:00
Permissions in `AndroidManifest.xml` :
2021-01-31 11:05:37 +01:00
```xml
< uses-permission android:name = "android.permission.INTERNET" / >
```
### Make the request
2021-09-20 19:35:32 +02:00
Subsequent requests should be delayed to avoid allowing the user to make too frequent requests.
2021-01-31 11:05:37 +01:00
```kotlin
private lateinit var queue: RequestQueue
override fun onCreate(savedInstanceState: Bundle?){
queue = Volley.newRequestQueue(context)
}
// response is a String
private fun simpleRequest() {
var url = "www.website.domain"
var stringRequest = StringRequest(Request.Method.GET, url, Response.Listener< String > {
Log.d()
},
Response.ErrorListener{
Log.e()
})
stringRequest.TAG = "TAG" // assign a tag to the request
queue.add(stringRequest) // add request to the queue
}
// response is JSON Object
private fun getJsonObjectRequest(){
// GET -> jsonRequest = null
// POST -> jsonRequest = JsonObject
var stringRequest = JsonObjectRequest(Request.Method.GET, url, jsonRequest, Response.Listener< String > {
Log.d()
// read end use JSON
},
Response.ErrorListener{
Log.e()
})
queue.add(stringRequest) // add request to the queue
}
// response is array of JSON objects
private fun getJSONArrayRequest(){
// GET -> jsonRequest = null
// POST -> jsonRequest = JsonObject
var stringRequest = JsonArrayRequest(Request.Method.GET, url, jsonRequest, Response.Listener< String > {
Log.d()
// read end use JSON
},
Response.ErrorListener{
Log.e()
})
queue.add(stringRequest) // add request to the queue
}
override fun onStop() {
super.onStop()
2021-09-20 19:35:32 +02:00
queue?.cancelAll("TAG") // delete all request with a particular tag when the activity is closed (avoid crash)
2021-01-31 11:05:37 +01:00
}
```
### Parse JSON Request
```kotlin
2021-09-20 19:35:32 +02:00
Response.Listener { response ->
2021-01-31 11:05:37 +01:00
var value = response.getSting("key")
}
```
## Data Persistance
### Singleton
2021-09-20 19:35:32 +02:00
Object instantiated during app init and is destroyed only on app closing. It can be used for data persistance since is not affected by the destruction of an activity.
2021-01-31 11:05:37 +01:00
```kotlin
// Context: Interface to global information about an application environment.
2021-09-20 19:35:32 +02:00
class Singleton constructor(context: Context) {
2021-01-31 11:05:37 +01:00
companion object {
@Volatile
private var INSTANCE: Singleton? = null
2021-09-20 19:35:32 +02:00
// synchronized makes sure that all instances of the singleton are actually the only existing one
fun getInstance(context: Context) = INSTANCE ?: synchronized(this) {
2021-01-31 11:05:37 +01:00
INSTANCE ?: Singleton(context).also {
INSTANCE = it
}
}
}
}
```
### SharedPreferences
[SharedPreferences Docs ](https://developer.android.com/training/data-storage/shared-preferences )
**Get a handle to shared preferences**:
- `getSharedPreferences()` — Use this if you need multiple shared preference files identified by name, which you specify with the first parameter. You can call this from any Context in your app.
- `getPreferences()` — Use this from an Activity if you need to use only one shared preference file for the activity. Because this retrieves a default shared preference file that belongs to the activity, you don't need to supply a name.
```kotlin
val sharedPref = activity?.getSharedPreferences( getString(R.string.preference_file_key), Context.MODE_PRIVATE )
val sharedPref = activity?.getPreferences(Context.MODE_PRIVATE)
```
**Write to shared preferences**:
To write to a shared preferences file, create a `SharedPreferences.Editor` by calling `edit()` on your SharedPreferences.
Pass the keys and values to write with methods such as `putInt()` and `putString()` . Then call `apply()` or `commit()` to save the changes.
```kotlin
val sharedPref = activity?.getPreferences(Context.MODE_PRIVATE) ?: return
with (sharedPref.edit()) {
putInt(getString(R.string.key), value)
commit() // or apply()
}
```
`apply()` changes the in-memory `SharedPreferences` object immediately but writes the updates to disk *asynchronously* .
Alternatively, use `commit()` to write the data to disk *synchronously* . But because *commit()* is synchronous, avoid calling it from your main thread because it could pause the UI rendering.
**Read from shared preferences**:
To retrieve values from a shared preferences file, call methods such as getInt() and getString(), providing the key for the wanted value, and optionally a default value to return if the key isn't present.
```kotlin
val sharedPref = activity?.getPreferences(Context.MODE_PRIVATE) ?: return
val defaultValue = resources.getInteger(R.integer.default_value_key)
val value = sharedPref.getInt(getString(R.string.key), defaultValue)
```