- Details
 - Written by: Stanko Milosev
 - Category: Android
 - Hits: 1169
 
using Microsoft.AspNetCore.Mvc;
namespace WebApi.Controllers;
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    [HttpGet]
    public string Get()
    {
        return "test";
    }
    [HttpGet("{id}")]
    public string Get(int id)
    {
        return "value";
    }
    [HttpPost]
    public string Post([FromBody] string value)
    {
        return $"Sent: {value}";
    }
}
Now example in Kotlin.
In \app\src\main\AndroidManifest.xml I have added internet permissions:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />In \app\src\main\res\layout\activity_main.xml I have added the button:
<?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">
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/button"
        android:onClick="postHttp"
        tools:layout_editor_absoluteX="166dp"
        tools:layout_editor_absoluteY="441dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
        />
</androidx.constraintlayout.widget.ConstraintLayout>
In \app\build.gradle I have added 
implementation "com.squareup.retrofit2:retrofit:2.9.0" implementation "com.squareup.okhttp3:okhttp:4.9.0" implementation 'com.squareup.retrofit2:converter-scalars:2.1.0'Code to trust all certificates taken from here.
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
    override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
        Log.i(MainActivity::class.simpleName, "checkClientTrusted")
    }
    override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
        Log.i(MainActivity::class.simpleName, "checkServerTrusted")
    }
    override fun getAcceptedIssuers() = arrayOf<X509Certificate>()
})
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
// Create an ssl socket factory with our all-trusting manager
val sslSocketFactory = sslContext.socketFactory
// connect to server
val client = OkHttpClient.Builder().sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager).hostnameVerifier{ _, _ -> true }.build()
Next building Retrofit:
val retro = Retrofit.Builder()
    .baseUrl("https://10.0.2.2:7037")
    .client(client)
    .addConverterFactory(ScalarsConverterFactory.create())
    .build()
Here notice baseUrl: https://10.0.2.2:7037 - 10.0.2.2 is localhost for Android Studio emulator
Add WebApiService interface:
interface WebApiService {
    @Headers("Content-Type: text/json")
    @POST("/api/Values")
    fun postMethod(@Body value: String): Call<String>
}
Create request:
val service = retro.create(WebApiService::class.java)
val webApiRequest = service.postMethod("\"test\"")
Here notice "\"test\"" since I am trying to send a raw string, otherwise would be rejected.
Last but not least, onResponse and onFailure:
val alertDialogBuilder = AlertDialog.Builder(this@MainActivity)
webApiRequest.enqueue(object : Callback<String> {
    override fun onResponse(call: Call<String>, response: Response<String>) {
        if (!response.isSuccessful) {
            alertDialogBuilder.setMessage(response.errorBody()!!.charStream().readText())
                .setCancelable(false)
                .setNeutralButton("OK", DialogInterface.OnClickListener { dialog, _ ->
                    dialog.dismiss()
                })
            val alert = alertDialogBuilder.create()
            alert.setTitle("Error")
            alert.show()
        }
        else {
            alertDialogBuilder.setMessage("Response: ${response.body().toString()}")
                .setCancelable(false)
                .setNeutralButton("OK", DialogInterface.OnClickListener { dialog, _ ->
                    dialog.dismiss()
                })
            val alert = alertDialogBuilder.create()
            alert.setTitle("Error")
            alert.show()
        }
    }
    override fun onFailure(call: Call<String>, t: Throwable) {
        alertDialogBuilder.setMessage(t.message)
            .setCancelable(false)
            .setNeutralButton("OK", DialogInterface.OnClickListener {
                    dialog, _ -> dialog.dismiss()
            })
        val alert = alertDialogBuilder.create()
        alert.setTitle("Error")
        alert.show()
    }
})
.NET solution download from here and Android source code download from here here.
    
    
    
        - Details
 - Written by: Stanko Milosev
 - Category: Android
 - Hits: 1348
 
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />Then in the MainActivity I have added the method "checkOptimization" and "openBatteryOptimization ":
@SuppressLint("NewApi", "BatteryLife")
private fun checkOptimization(context: Context) {
	val packageName = applicationContext.packageName
	val pm = applicationContext.getSystemService(POWER_SERVICE) as PowerManager
	if (!pm.isIgnoringBatteryOptimizations(packageName)) {
		val intent = Intent()
		intent.action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
		intent.data = Uri.parse("package:" + context.packageName)
		context.startActivity(intent)
	}
}
fun openBatteryOptimization(context: Context) {
	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
		val intent = Intent()
		intent.action = Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS
		context.startActivity(intent)
	} else {
		//Timber.d("Battery optimization not necessary")
	}
}
Both methods "checkOptimization" and "openBatteryOptimization" I have added to "onCreate" of MainActivity. For 
Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGSIn import list I have added:
import android.provider.Settings import android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONSIn Gradle I added:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"Service and receiver first I have copied from original article, then I have added following methods:
private val wakeLock: PowerManager.WakeLock by lazy {
	(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
		newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "neverEndingApplication:ServiceWakelock")
	}
}
private fun acquireWakelock() {
	try {
		wakeLock.let {
			wakeLock.setReferenceCounted(false)
			if (!wakeLock.isHeld) {
				wakeLock.acquire()
			}
		}
	} catch (e: RuntimeException) {
	}
}
private fun releaseWakelock() {
	try {
		wakeLock.let {
			if (it.isHeld) {
				it.release()
			}
		}
	} catch (e: RuntimeException) {
	}
}
Still in Service I have added acquireWakelock in onCreate:
override fun onCreate() {
	super.onCreate()
	acquireWakelock()
}
releaseWakelock() I have added when I want to stop the app:
if (intent?.action.equals("stopForeground")) {
	job?.cancel()
	releaseWakelock()
	stopForeground(true)
	stopSelfResult(startId)
}
MainActivity I also copied from my original article.
Example download from here.
    
    
    
        - Details
 - Written by: Stanko Milosev
 - Category: Android
 - Hits: 1437
 
val component = ComponentName(this, MyBroadcastReceiver::class.java) getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);Disable:
val component = ComponentName(this, MyBroadcastReceiver::class.java) getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_DISABLED , PackageManager.DONT_KILL_APP);
- Details
 - Written by: Stanko Milosev
 - Category: Android
 - Hits: 1867
 
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />and android:process=":MyService". My Manifest looks like:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.milosev.myapplication">
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <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/Theme.MyApplication">
        <receiver
            android:name=".MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true" />
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"
            android:process=":MyService" />
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
In Gradle I added:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"So my gradle looks like:
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}
android {
    compileSdk 32
    defaultConfig {
        applicationId "com.milosev.myapplication"
        minSdk 21
        targetSdk 32
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}
dependencies {
    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.6.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"
}
MyBroadcastReceiver.kt looks like:
package com.milosev.myapplication
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.milosev.getgpslocation.MyService
class MyBroadcastReceiver : BroadcastReceiver() {
    @RequiresApi(Build.VERSION_CODES.O)
    override fun onReceive(context: Context, intent: Intent) {
        when (intent?.action) {
            "MyAction" -> {
                val myIntent = Intent(context, MainActivity::class.java).setAction("MyAction")
                LocalBroadcastManager.getInstance(context).sendBroadcast(myIntent)
            }
            "Restart" -> {
                val intent = Intent(context, MyService::class.java)
                intent.action = "startForeground"
                context.startForegroundService(intent)
            }
        }
    }
}
MyService.kt looks like:
package com.milosev.getgpslocation
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.os.CountDownTimer
import android.os.IBinder
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import kotlinx.coroutines.*
class MyService : Service(),CoroutineScope by MainScope() {
    private var job: Job? = null
    override fun onBind(intent: Intent): IBinder {
        TODO("Return the communication channel to the service.")
    }
    @RequiresApi(Build.VERSION_CODES.O)
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        when (intent?.action) {
            "startForeground" -> {
                val channelId = createNotificationChannel("my_service", "My Background Service")
                val notificationBuilder = NotificationCompat.Builder(this, channelId)
                val notification = notificationBuilder.setOngoing(true)
                    .setContentTitle("test")
                    .setContentText("test")
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setPriority(1)
                    .setCategory(Notification.CATEGORY_SERVICE)
                    .build()
                startForeground(101, notification)
                val context = this
                job = launch {
                    while(true) {
                        val myIntent = Intent(context, MyBroadcastReceiver::class.java).setAction("MyAction")
                        sendBroadcast(myIntent);
                        delay(1_000)
                    }
                }
            }
        }
        if (intent?.action.equals("stopForeground")) {
            job?.cancel()
            stopForeground(true)
            stopSelfResult(startId)
        }
        return START_STICKY;
    }
    @RequiresApi(Build.VERSION_CODES.O)
    private fun createNotificationChannel(channelId: String, channelName: String): String {
        val chan = NotificationChannel(
            channelId,
            channelName, NotificationManager.IMPORTANCE_LOW
        )
        chan.lightColor = Color.RED
        chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
        val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        service.createNotificationChannel(chan)
        return channelId
    }
    override fun onDestroy() {
        super.onDestroy()
        val myIntent = Intent(this, MyBroadcastReceiver::class.java).setAction("Restart")
        sendBroadcast(myIntent);
    }
    override fun onTaskRemoved(rootIntent: Intent?) {
        super.onTaskRemoved(rootIntent)
        val myIntent = Intent(this, MyBroadcastReceiver::class.java).setAction("Restart")
        sendBroadcast(myIntent);
    }
}
POI:
override fun onDestroy() {
	super.onDestroy()
	val myIntent = Intent(this, MyBroadcastReceiver::class.java).setAction("Restart")
	sendBroadcast(myIntent);
}
override fun onTaskRemoved(rootIntent: Intent?) {
	super.onTaskRemoved(rootIntent)
	val myIntent = Intent(this, MyBroadcastReceiver::class.java).setAction("Restart")
	sendBroadcast(myIntent);
}
In main activity:
@RequiresApi(Build.VERSION_CODES.O)
override fun onResume() {
	super.onResume()
	LocalBroadcastManager.getInstance(this)
		.registerReceiver(broadCastReceiver, IntentFilter("MyAction"))
	val intent = Intent(this, MyService::class.java)
	intent.action = "startForeground"
	startForegroundService(intent)
}
Notice that there is a problem with onResume because it will start the service immediately.
Edit: 2022-06-05:
Unfortunately after latest update of Huawei my app is again getting killed. Here you can find informations about this issuse.