Passing data from a class which is Lifecycle observer to the Activity - android

I have a class BatteryInfo which is a life-cycle observer and in this class, there is a BroadCastReceiver which is responsible for getting all battery information. This class is working perfectly with the lifecycle of Activity from where I have called it. This means it is registering the broadcast on activity created and unRegister on closing activity. But I am confused about how to access this broadcast live information in Activity.
class BatteryInfo(
private val _context: Context,
private val _lifecycle: Lifecycle,
): LifecycleObserver {
private var _enabled = false
init {
_lifecycle.addObserver(this)
}
private val broadcastBatteryInfoListener = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
intent?.let {
val level = it.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val temperature = it.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1)
val voltage = it.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1)
val technology = it.getIntExtra(BatteryManager.EXTRA_TECHNOLOGY, -1)
val plugged = it.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)
val health = it.getIntExtra(BatteryManager.EXTRA_HEALTH, -1)
// Log.d("TAG", String.format("%.1f", voltage / 1000f) + " V")
}
}
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
fun start() {
// if (_enabled) {
_context.registerReceiver(broadcastBatteryInfoListener,
IntentFilter(Intent.ACTION_BATTERY_CHANGED)
)
// }
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stop() {
_context.unregisterReceiver(broadcastBatteryInfoListener)
}
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun destroy() {
_lifecycle.removeObserver(this)
}
// connect if not connected
fun enable() {
_enabled = true
if (_lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
start()
}
}
}
I am Calling this class from MainActivity onCreate method like this
BatteryInfo(this, lifecycle)

A simple listener would work :
data class Stats(val level : Int,
val temp : Int,
val voltage : Int,
val technology : Int,
val plugged : Int,
val health : Int)
class BatteryInfo(
private val _context: Context,
private val _lifecycle: Lifecycle
): LifecycleObserver {
private var _enabled = false
private var listener: ((Stats) -> Unit)? = null
init {
_lifecycle.addObserver(this)
}
private val broadcastBatteryInfoListener = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
intent?.let {
val level = it.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val temperature = it.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1)
val voltage = it.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1)
val technology = it.getIntExtra(BatteryManager.EXTRA_TECHNOLOGY, -1)
val plugged = it.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)
val health = it.getIntExtra(BatteryManager.EXTRA_HEALTH, -1)
listener?.invoke(Stats(level, temperature, voltage, technology, plugged, health))
// Log.d("TAG", String.format("%.1f", voltage / 1000f) + " V")
}
}
}
fun setListener(listener: ((Stats) -> Unit)?) {
this.listener = listener
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
fun start() {
// if (_enabled) {
_context.registerReceiver(broadcastBatteryInfoListener,
IntentFilter(Intent.ACTION_BATTERY_CHANGED)
)
// }
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stop() {
_context.unregisterReceiver(broadcastBatteryInfoListener)
}
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun destroy() {
_lifecycle.removeObserver(this)
listener = null
}
// connect if not connected
fun enable() {
_enabled = true
if (_lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
start()
}
}
}
class SomeActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
BatteryInfo(this, lifecycle)
.apply { setListener { stats: Stats -> Log.d(TAG, stats.toString()) } }
}
}

I know its not you answer but its a different approach of getting things done. It also removes all of your boilerplate code.
You can use an object to extract all your data from intent and return an instance of your data class.
Read more about object in kotlin here - https://kotlinlang.org/docs/tutorials/kotlin-for-py/objects-and-companion-objects.html
object BatteryInfoUtils {
fun getBatteryInfoFromIntent(intent: Intent): Stats { intent?.let {
val level = it.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val temperature = it.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1)
val voltage = it.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1)
val technology = it.getIntExtra(BatteryManager.EXTRA_TECHNOLOGY, -1)
val plugged = it.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)
val health = it.getIntExtra(BatteryManager.EXTRA_HEALTH, -1)
return Stats(level, temperature, voltage, technology, plugged, health)
}
}
And inside MainActivity onCreate()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val stats = getBatteryInfo()
}
fun getBatteryInfo(): Stats? {
var stats: Stats? = null
val intent: Intent? = IntentFilter(Intent.ACTION_BATTERY_CHANGED).let { ifilter ->
registerReceiver(null, ifilter)
}
intent?.let { batteryIntent ->
stats = BatteryInfoUtils.getBatteryInfoFromIntent(batteryIntent)
}
return stats
}

I have done this with the help of MutableLiveData & LiveData but I am not sure it is a perfect solution or a hacky solution. I want to do a perfect solution that follows MVVM, clean-architecture, and SOLID principle without the creation of multiple objects because it is very costly.
class BatteryInfo(
private val _context: Context,
private val _lifecycle: Lifecycle
) : LifecycleObserver {
val map = HashMap<String, Int>()
private var _enabled = false
private val _batteryInfoMap = MutableLiveData<HashMap<String, Int>>()
val batteryInfo: LiveData<HashMap<String, Int>>
get() = _batteryInfoMap
init {
_lifecycle.addObserver(this)
}
private val broadcastBatteryInfoListener = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
intent?.let {
val level = it.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val temperature = it.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1)
val voltage = it.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1)
val technology = it.getIntExtra(BatteryManager.EXTRA_TECHNOLOGY, -1)
val plugged = it.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)
val health = it.getIntExtra(BatteryManager.EXTRA_HEALTH, -1)
map["level"] = level
map["temperature"] = temperature
_batteryInfoMap.value = map
}
}
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
fun start() {
if (_enabled) {
_context.registerReceiver(
broadcastBatteryInfoListener,
IntentFilter(Intent.ACTION_BATTERY_CHANGED)
)
}
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stop() {
_context.unregisterReceiver(broadcastBatteryInfoListener)
}
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun destroy() {
_lifecycle.removeObserver(this)
}
fun enable() {
_enabled = true
if (_lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
start()
}
}
}
And inside MainActivity onCreate()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val into = BatteryInfo(this, lifecycle)
into.enable()
into.batteryInfo.observe(this, Observer {
Log.d("TAG", "${it.values}")
})
}

Related

how do i get the set notification in BLE nordic

i am trying to set notification callback in BLE nordic, where i am using the Android BLE library (Nordic Github). But i not ablet to get the notification event when i am changing the value of the characteristics.
`
class BleManagerHP1T(context: Context) : BleManager(context) {
override fun getGattCallback(): BleManagerGattCallback = GattCallback()
override fun log(priority: Int, message: String) {
if (BuildConfig.DEBUG || priority == Log.ERROR) {
Log.println(priority, GattService.TAG, message)
}
}
private inner class GattCallback : BleManagerGattCallback() {
private var myCharacteristic: BluetoothGattCharacteristic? = null
private var rxCharacteristic: BluetoothGattCharacteristic? = null
#SuppressLint("MissingPermission")
override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean {
val service = gatt.getService(GattService.MyServiceProfile.MY_SERVICE_UUID)
myCharacteristic =
service?.getCharacteristic(GattService.MyServiceProfile.MY_CHARACTERISTIC_UUID)
val myCharacteristicProperties = myCharacteristic?.properties ?: 0
Log.d(TAG, "isRequiredServiceSupported: notify ${(myCharacteristicProperties and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0)}")
rxCharacteristic = service?.getCharacteristic(GattService.MyServiceProfile.RX_CHARACTERISTIC_UUID)
val obj = JSONObject()
obj.put("OPCODE","PROVISION")
rxCharacteristic?.value = obj.toString().encodeToByteArray()
val rxRead = gatt.writeCharacteristic(rxCharacteristic)
Log.d(TAG, "isRequiredServiceSupported: Read $rxRead")
return (myCharacteristicProperties and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0)
}
override fun initialize() {
enableNotifications(myCharacteristic).enqueue()
requestMtu(260).enqueue();
setNotificationCallback(myCharacteristic).with { _, data ->
Log.d(TAG, "initialize: TX char Notification Called")
if (data.value != null) {
val value = String(data.value!!, Charsets.UTF_8)
Log.d(TAG, "initialize: TX char value $value")
}
}
requestMtu(260).enqueue();
enableNotifications(rxCharacteristic).enqueue()
setNotificationCallback(rxCharacteristic).with { _, data ->
Log.d(TAG, "initialize: RX char Notification Called")
if (data.value != null) {
val value = String(data.value!!, Charsets.UTF_8)
Log.d(TAG, "initialize: RX char value $value")
}
}
beginAtomicRequestQueue()
.add(enableNotifications(myCharacteristic)
.fail { _: BluetoothDevice?, status: Int ->
log(Log.ERROR, "Could not subscribe: $status")
disconnect().enqueue()
}
)
.done {
log(Log.INFO, "Target initialized")
}
.enqueue()
}
override fun onServicesInvalidated() {
myCharacteristic = null
}
}
override fun readCharacteristic(characteristic: BluetoothGattCharacteristic?): ReadRequest {
return Request.newReadRequest(characteristic)
}
}
`
gatt connection is establishing perfectly fine, using this code.
`
val bleManager = BleManagerHP1T(this#ControllerActivity)
synchronized (this) {
bleManager.connect(deviceMainList[position]).useAutoConnect(false).enqueue()
}
`
here is the gatt service file .
`
class GattService : Service() {
private val defaultScope = CoroutineScope(Dispatchers.Default)
private lateinit var bluetoothObserver: BroadcastReceiver
private var myCharacteristicChangedChannel: SendChannel<String>? = null
private val clientManagers = mutableMapOf<String, ClientManager>()
// val connect = BleManager()
#RequiresApi(Build.VERSION_CODES.O)
override fun onCreate() {
super.onCreate()
// Setup as a foreground service
val notificationChannel = NotificationChannel(
GattService::class.java.simpleName,
resources.getString(R.string.gatt_service_name),
NotificationManager.IMPORTANCE_DEFAULT
)
val notificationService =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationService.createNotificationChannel(notificationChannel)
val notification = NotificationCompat.Builder(this, GattService::class.java.simpleName)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(resources.getString(R.string.gatt_service_name))
.setContentText(resources.getString(R.string.gatt_service_running_notification))
.setAutoCancel(true)
startForeground(1, notification.build())
// Observe OS state changes in BLE
bluetoothObserver = object : BroadcastReceiver() {
#SuppressLint("MissingPermission")
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
BluetoothAdapter.ACTION_STATE_CHANGED -> {
val bluetoothState = intent.getIntExtra(
BluetoothAdapter.EXTRA_STATE,
-1
)
when (bluetoothState) {
BluetoothAdapter.STATE_ON -> enableBleServices()
BluetoothAdapter.STATE_OFF -> disableBleServices()
}
}
BluetoothDevice.ACTION_BOND_STATE_CHANGED -> {
val device =
intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
Log.d(TAG, "Bond state changed for device ${device?.address}: ${device?.bondState}")
when (device?.bondState) {
BluetoothDevice.BOND_BONDED -> addDevice(device)
BluetoothDevice.BOND_NONE -> removeDevice(device)
}
}
}
}
}
registerReceiver(bluetoothObserver, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED))
registerReceiver(bluetoothObserver, IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED))
// Startup BLE if we have it
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
if (bluetoothManager.adapter?.isEnabled == true) enableBleServices()
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(bluetoothObserver)
disableBleServices()
}
override fun onBind(intent: Intent?): IBinder? =
when (intent?.action) {
DATA_PLANE_ACTION -> {
DataPlane()
}
else -> null
}
override fun onUnbind(intent: Intent?): Boolean =
when (intent?.action) {
DATA_PLANE_ACTION -> {
myCharacteristicChangedChannel = null
true
}
else -> false
}
/**
* A binding to be used to interact with data of the service
*/
inner class DataPlane : Binder() {
fun setMyCharacteristicChangedChannel(sendChannel: SendChannel<String>) {
myCharacteristicChangedChannel = sendChannel
}
}
#SuppressLint("MissingPermission")
private fun enableBleServices() {
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
if (bluetoothManager.adapter?.isEnabled == true) {
Log.i(TAG, "Enabling BLE services")
bluetoothManager.adapter.bondedDevices.forEach { device -> addDevice(device) }
} else {
Log.w(TAG, "Cannot enable BLE services as either there is no Bluetooth adapter or it is disabled")
}
}
private fun disableBleServices() {
clientManagers.values.forEach { clientManager ->
clientManager.close()
}
clientManagers.clear()
}
private fun addDevice(device: BluetoothDevice) {
if (!clientManagers.containsKey(device.address)) {
val clientManager = ClientManager()
clientManager.connect(device).useAutoConnect(true).enqueue()
clientManagers[device.address] = clientManager
}
}
private fun removeDevice(device: BluetoothDevice) {
clientManagers.remove(device.address)?.close()
}
/*
* Manages the entire GATT service, declaring the services and characteristics on offer
*/
companion object {
/**
* A binding action to return a binding that can be used in relation to the service's data
*/
const val DATA_PLANE_ACTION = "data-plane"
const val TAG = "gatt-service"
}
private inner class ClientManager : BleManager(this#GattService) {
override fun getGattCallback(): BleManagerGattCallback = GattCallback()
override fun log(priority: Int, message: String) {
if (BuildConfig.DEBUG || priority == Log.ERROR) {
Log.println(priority, TAG, message)
Log.d(TAG, "log: $message")
}
}
private inner class GattCallback : BleManagerGattCallback() {
private var myCharacteristic: BluetoothGattCharacteristic? = null
override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean {
val service = gatt.getService(MyServiceProfile.MY_SERVICE_UUID)
myCharacteristic =
service?.getCharacteristic(MyServiceProfile.MY_CHARACTERISTIC_UUID)
val myCharacteristicProperties = myCharacteristic?.properties ?: 0
return (myCharacteristicProperties and BluetoothGattCharacteristic.PROPERTY_READ != 0) &&
(myCharacteristicProperties and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0)
}
override fun initialize() {
setNotificationCallback(myCharacteristic).with { _, data ->
if (data.value != null) {
val value = String(data.value!!, Charsets.UTF_8)
defaultScope.launch {
myCharacteristicChangedChannel?.send(value)
}
}
}
beginAtomicRequestQueue()
.add(enableNotifications(myCharacteristic)
.fail { _: BluetoothDevice?, status: Int ->
log(Log.ERROR, "Could not subscribe: $status")
disconnect().enqueue()
}
)
.done {
log(Log.INFO, "Target initialized")
}
.enqueue()
}
override fun onServicesInvalidated() {
myCharacteristic = null
}
}
}
object MyServiceProfile {
val MY_SERVICE_UUID: UUID = UUID.fromString("8d67d51a-801b-43cb-aea2-bbec9d1211fd")
val MY_CHARACTERISTIC_UUID: UUID = UUID.fromString("8d67d51c-801b-43cb-aea2-bbec9d1211fd")
val RX_CHARACTERISTIC_UUID: UUID = UUID.fromString("8d67d51b-801b-43cb-aea2-bbec9d1211fd")
}
}
`

how to fix 404 error, exception com.android.volley.ClientError?

i was making app with 2 buttons where are 2 different lists (i used recycler view) & duration debugging i found Error while refreshing device settings: network time: 263, HTTP status code: 404, exception com.android.volley.ClientError. Retrying. network time: 263, HTTP status code: 404, exception com.android.volley.ClientError
This is my code in adapter.
class CardAdapter(val context: Context, private val layout: Int):
RecyclerView.Adapter<CardAdapterViewHolder>() {
private val datasetDishes = com.example.favthings.data.Data.dishes
private val datasetAct = com.example.favthings.data.Data.activity
class CardAdapterViewHolder(view: View?): RecyclerView.ViewHolder(view!!) {
val dish_name: TextView = view?.findViewById(R.id.dish_name)!!
val dish_description: TextView = view?.findViewById(R.id.dish_description)!!
val dish_img: ImageView = view?.findViewById(R.id.dish_img)!!
val act_title: TextView = view?.findViewById(R.id.act_title)!!
val act_description:TextView = view?.findViewById(R.id.act_description)!!
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardAdapterViewHolder {
val adapter = LayoutInflater.from(parent.context)
val inflatedAdapter = if(layout == 1 )
{
adapter.inflate(R.layout.activity_list_item , parent, false)
}else{
adapter.inflate(R.layout.activity_dishes_item , parent, false)
}
return CardAdapterViewHolder(inflatedAdapter)
}
override fun getItemCount(): Int{
return datasetDishes.size
return datasetAct.size
}
override fun onBindViewHolder(holder: CardAdapterViewHolder, position: Int) {
val item_dish = datasetDishes[position]
val item_act = datasetAct[position]
holder.act_title.text = item_act.strResource_activity.toString()
holder.act_description.text = item_act.strResource_description.toString()
holder.dish_name.text = item_dish.strResourse_dish.toString()
holder.dish_img.setImageResource(item_dish.imgResourse)
holder.dish_description.text = item_dish.strResourse_dishDescr.toString()
}
}
this my code in one of the activities
class Dish : AppCompatActivity() {
private lateinit var binding: ActivityDishesBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDishesBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.dishRecyclerView.adapter = CardAdapter(
applicationContext,
Layout.DISH
)
// Specify fixed size to improve performance
binding.dishRecyclerView.setHasFixedSize(true)
// Enable up button for backward navigation
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
}
and finally my main activity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val buttonActivity: Button = findViewById(R.id.buttonActivity)
buttonActivity.setOnClickListener {
val intent = Intent(this, FavActivities::class.java).apply {
startActivity(this)
}
}
val buttonPlaces: Button = findViewById(R.id.buttonPlaces)
buttonPlaces.setOnClickListener {
val intent1 = Intent(this, Dish::class.java).apply {
startActivity(this)
}
}
val buttonMusic: Button = findViewById(R.id.buttonMusic)
buttonMusic.setOnClickListener {
val intent2 = Intent(this, Music::class.java).apply {
startActivity(this)
}
}
}
i found some solution. where i should put it? what to write where are commentes "exception" and "do stuff with body"?
fun onErrorResponse(error: VolleyError?) {
if (error == null || error.networkResponse == null) {
return
}
val body: String
//get status code here
val statusCode: String = java.lang.String.valueOf(error.networkResponse.statusCode)
//get response body and parse with appropriate encoding
try {
val UTF_8: Charset = Charset.forName("UTF-8")
body = String(error.networkResponse.data, UTF_8)
} catch (e: UnsupportedEncodingException) {
// exception
}
//do stuff with the body
}
i will be soo grateful for any explanitions.

How to turn BroadcastReceiver into StreamHandler while writing native plugin?

I've never written android code before. Now I want to write a Flutter plugin that collects Wi-Fi data.
Following this guide and found a video on YouTube but author uses StreamHandler, but I couldn't find a way to implement StreamHandler related code.
Wrote these code so far in the kotlin side, as my understanding I have to use wifiScanReceiver as StreamHandler but it is a BroadcastReceiver. Therefore the dataChannel!!.setStreamHandler(wifiScanReceiver) gives error.
class MainActivity(wifiManager: WifiManager) : FlutterActivity() {
private val METHOD_CHANNEL_NAME = "com.baran.collect_wifi/method"
private val DATA_CHANNEL_NAME = "com.baran.collect_wifi/data"
private var methodChannel : MethodChannel? = null
private lateinit var wifiManager: WifiManager
private var dataChannel : EventChannel? = null
private var eventSink: EventChannel.EventSink? = null
#RequiresApi(Build.VERSION_CODES.M)
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
//Setup channels
setupChannels(this, flutterEngine.dartExecutor.binaryMessenger)
}
override fun onDestroy() {
teardownChannels()
super.onDestroy()
}
private val wifiScanReceiver = object : BroadcastReceiver() {
#RequiresApi(Build.VERSION_CODES.M)
override fun onReceive(context: Context, intent: Intent) {
val success = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)
if (success) {
scanSuccess()
} else {
scanFailure()
}
}
}
private fun scanSuccess() {
}
private fun scanFailure() {
// handle failure: new scan did NOT succeed
// consider using old scan results: these are the OLD results!
val results = wifiManager.scanResults
}
#RequiresApi(Build.VERSION_CODES.M)
private fun setupChannels(context: Context, messenger: BinaryMessenger){
val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val intentFilter = IntentFilter()
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
context.registerReceiver(wifiScanReceiver, intentFilter)
methodChannel = MethodChannel(messenger, METHOD_CHANNEL_NAME)
methodChannel!!.setMethodCallHandler{
call, _ ->
if(call.method == "startScan"){
val success = wifiManager.startScan()
if (!success) {
// scan failure handling
scanFailure()
}
}
}
dataChannel = EventChannel(messenger, DATA_CHANNEL_NAME)
dataChannel!!.setStreamHandler(wifiScanReceiver)
}
private fun teardownChannels() {
methodChannel!!.setMethodCallHandler(null)
}
}
And this is the flutter code so far
import 'dart:async';
import 'package:flutter/services.dart';
class CollectWifi {
static const _channel = MethodChannel('com.baran.collect_wifi/method');
static const _dataChannel = EventChannel('com.baran.collect_wifi/data');
}
I don't know if I'm going correctly
you can not pass WifiManager in to setStreamHandler
Activity does not take WifiManager as input parameter
I updated some point in your source code as below
class MainActivity : FlutterActivity() {
private val METHOD_CHANNEL_NAME = "com.baran.collect_wifi/method"
private val DATA_CHANNEL_NAME = "com.baran.collect_wifi/data"
private var methodChannel: MethodChannel? = null
private var dataChannel: EventChannel? = null
private var eventSink: EventChannel.EventSink? = null // using send data to flutter layer
val wifiManager = application.getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiScanReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val success = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)
if (success) {
scanSuccess()
} else {
scanFailure()
}
}
}
private fun scanSuccess() {
val results = wifiManager.scanResults
eventSink?.success("Wifi Connected")// send event to flutter layer
}
private fun scanFailure() {
// handle failure: new scan did NOT succeed
// consider using old scan results: these are the OLD results!
val results = wifiManager.scanResults
eventSink?.error("404","Wifi Not connected",null) // send event to flutter layer
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intentFilter = IntentFilter()
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
context.registerReceiver(wifiScanReceiver, intentFilter)
val success = wifiManager.startScan()
if (!success) {
// scan failure handling
scanFailure()
}
}
override fun onDestroy() {
unregisterReceiver(wifiScanReceiver) // remember unRegister to prevent memory leak
super.onDestroy()
}
#RequiresApi(Build.VERSION_CODES.M)
private fun setupChannels(context: Context, messenger: BinaryMessenger){
val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val intentFilter = IntentFilter()
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
context.registerReceiver(wifiScanReceiver, intentFilter)
methodChannel = MethodChannel(messenger, METHOD_CHANNEL_NAME)
methodChannel!!.setMethodCallHandler{
call, _ ->
if(call.method == "startScan"){
val success = wifiManager.startScan()
if (!success) {
// scan failure handling
scanFailure()
}
}
}
dataChannel = EventChannel(messenger, DATA_CHANNEL_NAME)
//dataChannel.setStreamHandler(setStreamHandler(wifiScanReceiver)
dataChannel?.setStreamHandler(object :EventChannel.StreamHandler{
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
eventSink = events // object using send data to flutter layer
}
override fun onCancel(arguments: Any?) {
}
})
}
private fun teardownChannels() {
methodChannel!!.setMethodCallHandler(null)
}
}

CustomTabsClient.bindCustomTabsService always return false

I try to implement Custom Tab Intent with warmup capability. So it needs to create CustomTabsServiceConnection.
However, when I bind the service it always return false.
Please check my implementation below:
// Custom chrome tabs
private lateinit var customTabsIntent: CustomTabsIntent
private lateinit var customTabsClient: CustomTabsClient
private var connection: CustomTabsServiceConnection? = null
override fun onStart() {
super.onStart()
val ok = CustomTabsClient.bindCustomTabsService(this, CUSTOM_TAB_PACKAGE_NAME, ServiceConnection())
Timber.i("Is OK = $ok")
}
inner class ServiceConnection: CustomTabsServiceConnection() {
override fun onCustomTabsServiceConnected(
name: ComponentName,
client: CustomTabsClient
) {
customTabsClient = client
customTabsClient.warmup(0)
// Connection callback
val session = customTabsClient.newSession(object : CustomTabsCallback() {
override fun onNavigationEvent(navigationEvent: Int, extras: Bundle?) {
super.onNavigationEvent(navigationEvent, extras)
Timber.i("Event = $navigationEvent")
}
})
if (session != null) {
session.mayLaunchUrl(Uri.parse(loginUrl), null, null)
// Init custom tab intent
val customTabBuilder = CustomTabsIntent.Builder(session)
customTabsIntent = customTabBuilder.build()
customTabsIntent.launchUrl(this, Uri.parse(loginUrl))
}
}
override fun onServiceDisconnected(name: ComponentName?) {
connection = null
}
}
override fun onStop() {
super.onStop()
connection?.let {
this.unbindService(it)
}
}
Any idea about this?

How can I make Exoplayer2 run in background? (Kotlin)

I am a noob in Kotlin.
I've made a streaming music player using Exoplayer2.
For running this music player in background, I tried to bind activity to service.
But it is not working.
I dont know why this player is stopped outside the app even though i add the foreground service.
Can anybody help me? :(
MusicService.kt
var mExoPlayer: SimpleExoPlayer? = null
class MusicService : MediaBrowserServiceCompat() {
private var mMediaSession: MediaSessionCompat? = null
private lateinit var mStateBuilder: PlaybackStateCompat.Builder
private var playbackPosition = 0L
private var currentWindow = 0
private var oldUri: Uri? = null
private val mMediaSessionCallback = object : MediaSessionCompat.Callback() {
override fun onPlayFromUri(uri: Uri?, extras: Bundle?) {
super.onPlayFromUri(uri, extras)
uri?.let {
val mediaSource = extractMediaSourceFromUri(uri)
if (uri != oldUri)
play(mediaSource)
else play()
oldUri = uri
}
}
override fun onPause() {
super.onPause()
pause()
}
override fun onStop() {
super.onStop()
stop()
}
}
override fun onCreate() {
super.onCreate()
initializePlayer()
initializeExtractor()
initializeAttributes()
mMediaSession = MediaSessionCompat(baseContext, "tag for debugging").apply {
setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS )
mStateBuilder = PlaybackStateCompat.Builder()
.setActions(PlaybackStateCompat.ACTION_PLAY or PlaybackStateCompat.ACTION_PLAY_PAUSE)
setPlaybackState(mStateBuilder.build())
setCallback(mMediaSessionCallback)
setSessionToken(sessionToken)
isActive = true
}
}
private var mAttrs: AudioAttributes? = null
private fun play(mediaSource: MediaSource) {
if (mExoPlayer == null) initializePlayer()
mExoPlayer?.apply {
mAttrs?.let { initializeAttributes() }
mAttrs?.let { setAudioAttributes(it, true) }
prepare(mediaSource)
seekTo(currentWindow, playbackPosition)
playWhenReady = true
}
}
private fun play() {
mExoPlayer?.apply {
mExoPlayer?.playWhenReady = true
updatePlaybackState(PlaybackStateCompat.STATE_PLAYING)
mMediaSession?.isActive = true
}
}
private fun initializePlayer() {
mExoPlayer = ExoPlayerFactory.newSimpleInstance(
this, DefaultRenderersFactory(baseContext)
, DefaultTrackSelector(),
DefaultLoadControl()
)
}
private fun pause() {
mExoPlayer?.apply {
playWhenReady = false
if (playbackState == PlaybackStateCompat.STATE_PLAYING) {
updatePlaybackState(PlaybackStateCompat.STATE_PAUSED)
}
}
}
private fun stop() {
mExoPlayer?.playWhenReady = false
mExoPlayer?.release()
mExoPlayer = null
updatePlaybackState(PlaybackStateCompat.STATE_NONE)
mMediaSession?.isActive = false
mMediaSession?.release()
}
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
stopSelf()
}
override fun onDestroy() {
super.onDestroy()
stop()
}
private fun updatePlaybackState(state: Int) {
mMediaSession?.setPlaybackState(
PlaybackStateCompat.Builder().setState(
state
, 0L
, 1.0f // Speed playing
).build()
)
}
private fun initializeAttributes() {
mAttrs = AudioAttributes.Builder().setUsage(C.USAGE_MEDIA)
.setContentType(C.CONTENT_TYPE_MUSIC)
.build()
}
private lateinit var mExtractorFactory: ExtractorMediaSource.Factory
private fun initializeExtractor() {
val userAgent = Util.getUserAgent(baseContext, "Application Name")
mExtractorFactory = ExtractorMediaSource.Factory(DefaultDataSourceFactory(this, userAgent))
.setExtractorsFactory(DefaultExtractorsFactory())
}
private fun extractMediaSourceFromUri(uri: Uri): MediaSource {
return mExtractorFactory.createMediaSource(uri)
}
override fun onLoadChildren(parentId: String, result: Result<MutableList<MediaBrowserCompat.MediaItem>>) {
}
override fun onGetRoot(clientPackageName: String, clientUid: Int, rootHints: Bundle?): BrowserRoot? {
return BrowserRoot("", null)
}
playerAct.kt
var audioUrlPass = ""
class playerAct: AppCompatActivity() { private val songUrl: String = audioUrlPass
private lateinit var mMediaBrowserCompat: MediaBrowserCompat
private val connectionCallback: MediaBrowserCompat.ConnectionCallback = object : MediaBrowserCompat.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
mMediaBrowserCompat.sessionToken.also { token ->
val mediaController = MediaControllerCompat(this#playerAct, token)
MediaControllerCompat.setMediaController(this#playerAct, mediaController)
}
playPauseBuild()
}
override fun onConnectionFailed() {
super.onConnectionFailed()
}
}
private val mControllerCallback = object : MediaControllerCompat.Callback() {
}
fun playPauseBuild() {
val mediaController = MediaControllerCompat.getMediaController(this#playerAct)
main_pcv.showTimeoutMs = 0
exo_play.setOnClickListener {
val state = mediaController.playbackState.state
if (state == PlaybackStateCompat.STATE_PAUSED ||
state == PlaybackStateCompat.STATE_STOPPED ||
state == PlaybackStateCompat.STATE_NONE
) {
mediaController.transportControls.playFromUri(Uri.parse(songUrl), null)
}
else if (state == PlaybackStateCompat.STATE_PLAYING ||
state == PlaybackStateCompat.STATE_BUFFERING ||
state == PlaybackStateCompat.STATE_CONNECTING
) {
mediaController.transportControls.pause()
}
}
mediaController.registerCallback(mControllerCallback)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.player_layout)
val componentName = ComponentName(this, MusicService::class.java)
mMediaBrowserCompat = MediaBrowserCompat(
this, componentName, //Identifier for the service
connectionCallback,
null
)
menuButton.setOnClickListener {
super.onBackPressed()
}
btnTime1.setOnClickListener {
val intent = Intent(this, TimerActivity::class.java)
startActivity(intent)
}
playerSeekBar.setOnSeekBarChangeListener(
object : SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekbar: SeekBar?, progress: Int, fromUser: Boolean) {
if (fromUser){
val volumeNum = progress / 100.0f
mExoPlayer!!.setVolume(volumeNum)
}
}
override fun onStartTrackingTouch(seekbar: SeekBar?) { }
override fun onStopTrackingTouch(seekbar: SeekBar?) { }
})
}
override fun onStart() {
super.onStart()controller
Intent(this, MusicService::class.java).also {
mMediaBrowserCompat.connect()
}
}
override fun onStop() {
super.onStop()
val controllerCompat = MediaControllerCompat.getMediaController(this)
controllerCompat?.unregisterCallback(mControllerCallback)
mMediaBrowserCompat.disconnect()
}
}
In order to start the foreground service, you will need a notification shown within 5 seconds of starting it. Check the following guide in the android documentation: https://developer.android.com/guide/topics/media-apps/audio-app/building-a-mediabrowserservice#mediastyle-notifications

Categories

Resources