I am new to Agora.io and I am creating an android app for 1 to 1 video calling. Everything is working fine in my app - I can join/leave the channel and even see my own local image through my camera. However, in the call, only the audio is being transmitted and video is not being transmitted.
When I debug the app, I can see that the event 'onFirstRemoteVideoFrame' is not being triggered. I tried changing it to 'onFirstRemoteVideoDecoded'(which I saw in many tutorials but android studio says the method is depreciated) but it is still not working.
Also, please note that when I add the line '-keep class io.agora.**{;}' in my proguard-rules.pro file, it says that it can't find the class. So instead I am using '-keep class io.agora.{*;}'.
Below is the java code for my activity. I am using agora 3.1.3.
package com.guideu.helloagora;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;
import io.agora.rtc.IRtcEngineEventHandler;
import io.agora.rtc.RtcEngine;
import io.agora.rtc.video.VideoCanvas;
import io.agora.rtc.video.VideoEncoderConfiguration;
public class MainActivity extends AppCompatActivity {
private static final String TAG=MainActivity.class.getSimpleName();
private static final int PERMISSION_REQ_ID=22;
private static final String[] REQUESTED_PERMISSIONS={
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
private RtcEngine mRtcEngine; // Agora engine reference
private FrameLayout mLocalContainer;
private RelativeLayout mRemoteContainer;
private SurfaceView mLocalView;
private SurfaceView mRemoteView;
private ImageView mCallBtn;
private ImageView mMuteBtn;
private ImageView mSwitchCameraBtn;
private boolean mCallEnd;
private boolean mMuted;
//Agora engine event handler
private final IRtcEngineEventHandler mRTCHandler=new IRtcEngineEventHandler() {
#Override
public void onJoinChannelSuccess(String channel, final int uid, int elapsed) {
super.onJoinChannelSuccess(channel, uid, elapsed);
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.i("agora","Join channel success, uid: " + (uid & 0xFFFFFFFFL));
}
});
}
#Override
public void onUserOffline(final int uid, int reason) {
super.onUserOffline(uid, reason);
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.i("agora","User offline, uid: " + (uid & 0xFFFFFFFFL));
removeRemoteView();
}
});
}
#Override
public void onFirstRemoteVideoFrame(final int uid, int width, int height, int elapsed) {
super.onFirstRemoteVideoFrame(uid, width, height, elapsed);
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.i("agora","First remote video decoded, uid: " + (uid & 0xFFFFFFFFL));
setupRemoteVideo(uid);
}
});
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initUI();
if(checkSelfPermission(REQUESTED_PERMISSIONS[0],PERMISSION_REQ_ID) &&
checkSelfPermission(REQUESTED_PERMISSIONS[1],PERMISSION_REQ_ID) &&
checkSelfPermission(REQUESTED_PERMISSIONS[2],PERMISSION_REQ_ID)){
//init engine
initEngineAndJoinChannel();
}
}
#Override
protected void onDestroy(){
super.onDestroy();
if(!mCallEnd){
leaveChannel();
}
RtcEngine.destroy();
}
private void initUI(){
mLocalContainer=findViewById(R.id.local_video_view_container);
mRemoteContainer=findViewById(R.id.remote_video_view_container);
mCallBtn=findViewById(R.id.btn_call);
mMuteBtn=findViewById(R.id.btn_mute);
mSwitchCameraBtn=findViewById(R.id.btn_switch_camera);
}
private void initEngineAndJoinChannel(){
//initialize engine
initializeEngine();
//setup video config
setupVideoConfig();
//setup local video
setupLocalVideo();
//join channel
joinChannel();
}
private void initializeEngine(){
try {
mRtcEngine = RtcEngine.create(getBaseContext(), getString(R.string.agora_app_id), mRTCHandler);
}
catch (Exception e){
Log.e(TAG,Log.getStackTraceString(e));
throw new RuntimeException("Need to check rtc sdk init fatal error\n" + Log.getStackTraceString(e));
}
}
private void setupVideoConfig(){
mRtcEngine.enableVideo();
mRtcEngine.setVideoEncoderConfiguration(new VideoEncoderConfiguration(
VideoEncoderConfiguration.VD_640x360,
VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_15,
VideoEncoderConfiguration.STANDARD_BITRATE,
VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_FIXED_PORTRAIT
));
}
private void setupLocalVideo(){
mRtcEngine.enableVideo();
mLocalView=RtcEngine.CreateRendererView(getBaseContext());
mLocalView.setZOrderMediaOverlay(true);
mLocalContainer.addView(mLocalView);
VideoCanvas localVideoCanvas=new VideoCanvas(mLocalView,VideoCanvas.RENDER_MODE_HIDDEN,0);
mRtcEngine.setupLocalVideo(localVideoCanvas);
}
private void setupRemoteVideo(int uid){
/*int count=mRemoteContainer.getChildCount();
View view=null;
for(int i=0;i<count;i++){
View v=mRemoteContainer.getChildAt(i);
if(v.getTag() instanceof Integer && ((int) v.getTag())==uid){
view=v;
}
}
if(view!=null){
return;
}*/
mRemoteView=RtcEngine.CreateRendererView(getBaseContext());
mRemoteContainer.addView(mRemoteView);
mRtcEngine.setupRemoteVideo(new VideoCanvas(mRemoteView,VideoCanvas.RENDER_MODE_HIDDEN,uid));
mRemoteView.setTag(uid);
}
private void removeRemoteView(){
if(mRemoteView!=null){
mRemoteContainer.removeView(mRemoteView);
}
mRemoteView=null;
}
private void joinChannel(){
String token=getString(R.string.agora_access_token);
if(TextUtils.isEmpty(token)){
token=null;
}
mRtcEngine.joinChannel(token,"HelloAgora","",0);
}
private void leaveChannel(){
mRtcEngine.leaveChannel();
}
public void onLocalAudioMuteClicked(View view){
mMuted=!mMuted;
mRtcEngine.muteLocalAudioStream(mMuted);
int res=mMuted?R.drawable.btn_mutecall:R.drawable.btn_unmute;
mMuteBtn.setImageResource(res);
}
public void onSwitchCameraClicked(View view){
mRtcEngine.switchCamera();
}
public void onCallClicked(View view){
if(mCallEnd){
startCall();
mCallEnd=false;
mCallBtn.setImageResource(R.drawable.btn_endcall);
}
else{
endCall();
mCallEnd=true;
mCallBtn.setImageResource(R.drawable.btn_startcall);
}
showButtons(!mCallEnd);
}
private void startCall(){
setupLocalVideo();
joinChannel();
}
private void endCall(){
removeLocalVideo();
removeRemoteView();
leaveChannel();
}
private void removeLocalVideo(){
if(mLocalView!=null){
mLocalContainer.removeView(mLocalView);
}
mLocalView=null;
}
private void showButtons(boolean show){
int visibility=show?View.VISIBLE:View.GONE;
mMuteBtn.setVisibility(visibility);
mSwitchCameraBtn.setVisibility(visibility);
}
private boolean checkSelfPermission(String permission, int requestCode){
if(ContextCompat.checkSelfPermission(this,permission)!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this,REQUESTED_PERMISSIONS,requestCode);
Toast.makeText(this,"No Permission",Toast.LENGTH_LONG);
return false;
}
return true;
}
}
'''
You can try "onRemoteVideoStateChanged" callback instead of "onFirstRemoteVideoFrame".
Here is the code snippet:
#Override
public void onRemoteVideoStateChanged(final int uid, int state, int reason, int elapsed) {
super.onRemoteVideoStateChanged(uid, state, reason, elapsed);
if (state == Constants.REMOTE_VIDEO_STATE_STARTING) {
runOnUiThread(new Runnable() {
#Override
public void run() {
setupRemoteVideo(uid);
}
});
}
}
Related
I am new to Android Studio and to BLE. I am working on a program that will scan for a beacon to read information. I am following this tutorial and the closest solution to my problem I found so far was this post. I am trying to get my app to respond to a button press by showing a list of devices that are available to connect through BLE. I keep running into this error from the LeDeviceListAdapter variable initialization...
Unresolved Reference: LeDeviceListAdapter
I figure that I need to add a class to my code, which I have tried to do directly from this source, but I was unsuccessful.
My question is, how do I add the class to my project if that's the solution and which file in the solution do I add it to? If that's not the solution, can someone help me pinpoint what it is?
Also, could it have something to do with the Android API I'm using for the project?
Here is my code...
view.findViewById<Button>(R.id.button_first).setOnClickListener {
val bluetoothManager: BluetoothManager = activity!!.getSystemService(BluetoothManager::class.java)
val bluetoothAdapter: BluetoothAdapter? = bluetoothManager.adapter
if (bluetoothAdapter == null) {
// Device doesn't support Bluetooth
val text = "This device does not support BlueTooth"
val duration = Toast.LENGTH_SHORT
Toast.makeText(context, text, duration)
return#setOnClickListener
}
if (bluetoothAdapter?.isEnabled == false) {
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
}
val bluetoothLeScanner = bluetoothAdapter.bluetoothLeScanner
var scanning = false
val handler = Handler()
// Stops scanning after 10 seconds.
val SCAN_PERIOD: Long = 10000
val leDeviceListAdapter = LeDeviceListAdapter()
// Device scan callback.
val leScanCallback: ScanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
super.onScanResult(callbackType, result)
leDeviceListAdapter.addDevice(result.device)
leDeviceListAdapter.notifyDataSetChanged()
}
}
fun scanLeDevice() {
if (!scanning) { // Stops scanning after a pre-defined scan period.
handler.postDelayed({
scanning = false
bluetoothLeScanner.stopScan(leScanCallback)
}, SCAN_PERIOD)
scanning = true
bluetoothLeScanner.startScan(leScanCallback)
} else {
scanning = false
bluetoothLeScanner.stopScan(leScanCallback)
}
}
}
}
Thank you in advance!
I don't know if I understood correctly the question, but you need to create a new class file with the LeDeviceListAdapter class definition. Then you need to import it inside the file you are using it.
For some info about BLE you can check my question:
QUESTION
And my answer:
ANSWER
In my answer you can find a class (TagBLE and DataTagBLE) to manage the bluetooth device you find ^^ You can use it and implement a list of them to manage the results from your scan then use a RecyclerView with specific RecyclerView Adapter to show all the devices :D
If you need any help just ask me, I can provice you the code I'm using and explain it to you (: For example I can give you the services and activities I'm using to scan and retrieve BLE :D
Also check this answer:
Answer about LeDeviceListAdapter
I had the code for a service that do the background scan for the BLE and a Base Activity that manage getting the TAG INFO. You can use this for references or import it in your project. It will use a service to scan for the BLE and get the results in the activity
BaseTagsScanService.java:
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public abstract class BaseTagsScanService extends Service {
private static final String TAG = BaseTagsScanService.class.getName();
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeScanner mBLEScanner;
private ScanCallback mScanCallback;
private boolean mIsScanning;
/** Abstract Methods **/
protected abstract ScanSettings getScanSettings();
protected abstract List<ScanFilter> getScanFilters();
protected abstract void manageScanResult(ScanResult result);
protected abstract void onScanFail(int errorCode);
/** Override Lifecycle Methods **/
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
initBluetooth();
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
initBluetooth();
return null;
}
#Override
public void onDestroy() {
unsetAll();
super.onDestroy();
}
/** Protected Methods **//** Init Methods **/
protected void initBluetooth(){
initBluetoothAdapter();
if(mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
initBluetoothLeScanner();
initBluetoothScanCallback();
}
}
/** Getter Methods **/
#Nullable
protected BluetoothAdapter getBluetoothAdapter(){
return mBluetoothAdapter;
}
#Nullable
protected BluetoothManager getBluetoothManager(){
return mBluetoothManager;
}
#Nullable
protected BluetoothLeScanner getBLEScanner(){
return mBLEScanner;
}
#Nullable
protected ScanCallback getScanCallback(){
return mScanCallback;
}
/** BLEScanner Methods **/
protected void unsetBLEScanner(){
if(mBLEScanner != null && mIsScanning && mBluetoothAdapter.isEnabled()){
mBLEScanner.stopScan(mScanCallback);
}
mBLEScanner = null;
}
/** Scan Methods **/
protected boolean isScanning(){
return mIsScanning;
}
protected void setIsScanning(boolean isScanning){
mIsScanning = isScanning;
}
protected boolean startScan(){
boolean ret = false;
if(checkStartScan()){
ret = true;
mIsScanning = true;
if(mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
mBLEScanner.startScan(getScanFilters(), getScanSettings(), mScanCallback);
}
}
return ret;
}
protected boolean stopScan(){
boolean ret = false;
if(checkStopScan()){
ret = true;
mIsScanning = false;
if(mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
mBLEScanner.stopScan(mScanCallback);
}
}
return ret;
}
/** ScanFilter Methods **/
protected List<ScanFilter> buildScanFilters(List<String> macs){
List<ScanFilter> scanFilters = new ArrayList<>();
if(macs != null && macs.size() > 0x0){
scanFilters = new ArrayList<>();
for(String mac : macs){
ScanFilter filter = new ScanFilter
.Builder()
.setDeviceAddress(mac)
.build();
scanFilters.add(filter);
}
} else {
scanFilters.add(new ScanFilter.Builder().build());
}
return scanFilters;
}
/** Private Methods **//** Init Methods **/
private void initBluetoothAdapter(){
mBluetoothManager = (BluetoothManager) getBaseContext().getSystemService(Context.BLUETOOTH_SERVICE);
if(mBluetoothManager != null){
mBluetoothAdapter = mBluetoothManager.getAdapter();
}
}
private void initBluetoothLeScanner(){
mBLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
}
private void initBluetoothScanCallback(){
mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
manageScanResult(result);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
}
#Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
onScanFail(errorCode);
Log.e("CheckInOut_Admin", "onScanFailed - errorCode = " + errorCode);
}
};
}
/** Unset Methods **/
private void unsetAll(){
unsetBLEScanner();
unsetBluetoothAdapter();
}
private void unsetBluetoothAdapter(){
mBluetoothAdapter = null;
mBluetoothManager = null;
}
/** Check Scan Methods **/
private boolean checkStartScan(){
return !mIsScanning && mBLEScanner != null;
}
private boolean checkStopScan(){
return mIsScanning && mBLEScanner != null;
}
}
TagsScanService.java:
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Parcelable;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import com.emax.it.lib_base.BaseConstants;
import com.emax.it.lib_base.BaseEnvironment;
import com.emax.it.lib_base.modules.handlers.MessageHandler;
import com.emax.it.lib_base.modules.handlers.MessageManager;
import com.emax.it.lib_base.ui.receivers.BluetoothStatusChangedReceiver;
import com.emax.it.lib_base.ui.receivers.ProviderStatusChangedReceiver;
import com.emax.it.lib_ble.base_activities.BaseTagsScanActivity;
import com.emax.it.lib_ble.models.TagBle;
import com.emax.it.lib_ble.services.base.BaseTagsScanService;
import com.emax.it.lib_ble.utils.BluetoothUtils;
import java.util.ArrayList;
import java.util.List;
public class TagsScanService extends BaseTagsScanService
implements BluetoothStatusChangedReceiver.IOnBluetoothStatusChanged,
ProviderStatusChangedReceiver.IOnProviderStatusChanged,
MessageHandler.IOnHandleMessage {
private static final String TAG = TagsScanService.class.getSimpleName();
// Messages Whats
public static final int MSG_WHAT_SCAN_START = 0x5C1;
public static final int MSG_WHAT_SCAN_STOP = 0x5C2;
public static final int MSG_WHAT_BLUETOOTH_OFF = 0x5C3;
public static final int MSG_WHAT_GPS_OFF = 0x5C4;
public static final int MSG_WHAT_BLE_TAG = 0x5C5;
// Messages Args
public static final int MSG_ARG_ONE_FAIL = 0x0;
public static final int MSG_ARG_ONE_SUCCESS = 0x1;
private BluetoothStatusChangedReceiver mBluetoothReceiver;
private ProviderStatusChangedReceiver mProviderReceiver;
private MessageManager mMessenger;
/** Override BaseTagsScanService Methods **/
#Override
protected ScanSettings getScanSettings(){
return new ScanSettings
.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
}
#Override
protected List<ScanFilter> getScanFilters(){
return new ArrayList<>();
}
#Override
protected void manageScanResult(ScanResult result) {
if(result != null){
BluetoothDevice device = result.getDevice();
if(!TextUtils.isEmpty(device.getAddress())){
sendTag(TagBle.initInstanceFromScanResult(result, BluetoothUtils.getBeaconTxPower(result)), result.getRssi());
}
}
}
#Override
protected void onScanFail(int errorCode) {
switch (errorCode){
case ScanCallback.SCAN_FAILED_ALREADY_STARTED:
if(getBLEScanner() != null){
getBLEScanner().stopScan(getScanCallback());
getBLEScanner().startScan(getScanFilters(), getScanSettings(), getScanCallback());
}
break;
case ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED:
if(getBluetoothAdapter() != null){
getBLEScanner().stopScan(getScanCallback());
getBluetoothAdapter().disable();
new Handler().postDelayed(() -> {
getBluetoothAdapter().enable();
new Handler().postDelayed(() -> getBLEScanner().startScan(getScanFilters(), getScanSettings(), getScanCallback()), BaseConstants.MILLIS_QUARTER_SECOND);
}, BaseConstants.MILLIS_QUARTER_SECOND);
}
break;
}
}
/** Override Lifecycle Methods **/
#Override
public void onCreate() {
super.onCreate();
initOnCreate();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
super.onBind(intent);
initMessageManager();
return mMessenger.getMsgReceiver().getBinder();
}
#Override
public void onDestroy() {
unsetBluetoothStatusReceiver();
unsetProviderStatusReceiver();
super.onDestroy();
}
/** Override BluetoothStatusChangedReceiver.IOnBluetoothStatusChanged Methods **/
public void onBluetoothStatusChanged(int status){
if(status == BluetoothAdapter.STATE_TURNING_OFF){
stopScan();
sendBluetoothOff();
} else if(BluetoothUtils.checkBLE(this)){
initBluetooth();
}
}
/** Override ProviderStatusChangedReceiver.IOnProviderStatusChanged Methods **/
#Override
public void onProviderStatusChanged(int status) {
if(status == ProviderStatusChangedReceiver.IOnProviderStatusChanged.STATUS_DISABLED){
stopScan();
sendGpsOff();
} else if(BluetoothUtils.checkBLE(this)){
initBluetooth();
}
}
/** Override IOnHandleMessage Callback Methods **/
#Override
public void onHandleMessage(Message msg) {
switch(msg.what){
case MSG_WHAT_SCAN_START:
boolean started = startScan();
sendScanStarted(started ? MSG_ARG_ONE_SUCCESS : MSG_ARG_ONE_FAIL, msg.arg2);
break;
case MSG_WHAT_SCAN_STOP:
boolean stopped = stopScan();
sendScanStopped(stopped ? MSG_ARG_ONE_SUCCESS : MSG_ARG_ONE_FAIL, msg.arg2);
break;
}
}
/** Protected Methods **/
protected <T extends Parcelable> void sendTag(T tag, int rssi){
if(mMessenger != null){
Bundle data = new Bundle();
data.putParcelable(BaseTagsScanActivity.EXTRA_BLE_TAG, tag);
data.putInt(BaseTagsScanActivity.EXTRA_RSSI_VALUE, rssi);
mMessenger.sendMessage(MSG_WHAT_BLE_TAG, 0x0, 0x0, data);
}
}
protected MessageManager getMessenger(){
return mMessenger;
}
/** Private Methods **//** Init Methods **/
private void initOnCreate(){
initRegisterBluetoothStatusChangedReceiver();
initRegisterProviderStatusChangedReceiver();
}
private void initMessageManager(){
mMessenger = new MessageManager(this);
}
private void initRegisterBluetoothStatusChangedReceiver(){
initBluetoothAdapterReceiver();
registerBluetoothAdapterReceiver();
}
private void initRegisterProviderStatusChangedReceiver(){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
initProviderStatusChangedReceiver();
registerProviderStatusChangedReceiver();
} else {
mProviderReceiver = null;
}
}
private void initBluetoothAdapterReceiver(){
mBluetoothReceiver = new BluetoothStatusChangedReceiver(this);
}
private void initProviderStatusChangedReceiver(){
mProviderReceiver = new ProviderStatusChangedReceiver(this);
}
/** Register Receivers Methods **/
private void registerBluetoothAdapterReceiver(){
if(mBluetoothReceiver != null && !mBluetoothReceiver.isRegistered()) {
registerReceiver(mBluetoothReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
mBluetoothReceiver.setIsRegistered(true);
}
}
private void registerProviderStatusChangedReceiver(){
if(mProviderReceiver != null && !mProviderReceiver.isRegistered()){
registerReceiver(mProviderReceiver, new IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION));
mProviderReceiver.setIsRegistered(true);
}
}
/** Unset Methods **/
private void unsetBluetoothStatusReceiver(){
if(mBluetoothReceiver != null) {
try {
unregisterReceiver(mBluetoothReceiver);
} catch (RuntimeException rE) {
BaseEnvironment.onExceptionLevelLow(TAG, rE);
}
mBluetoothReceiver = null;
}
}
private void unsetProviderStatusReceiver(){
if(mProviderReceiver != null){
try {
unregisterReceiver(mProviderReceiver);
} catch (RuntimeException rE){
BaseEnvironment.onExceptionLevelLow(TAG, rE);
}
mProviderReceiver = null;
}
}
/** Messenger Methods **/
private void sendScanStarted(int arg1, int arg2){
if(mMessenger != null ){
mMessenger.sendMessage(MSG_WHAT_SCAN_START, arg1, arg2, null);
}
}
private void sendScanStopped(int arg1, int arg2){
if(mMessenger != null){
mMessenger.sendMessage(MSG_WHAT_SCAN_STOP, arg1, arg2,null);
}
}
private void sendBluetoothOff(){
if(mMessenger != null){
mMessenger.sendMessage(MSG_WHAT_BLUETOOTH_OFF, 0x0, 0x0, null);
}
}
private void sendGpsOff(){
if(mMessenger != null){
mMessenger.sendMessage(MSG_WHAT_GPS_OFF, 0x0, 0x0, null);
}
}
}
BaseTagsScanActivity.java:
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcelable;
import android.view.MenuItem;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.emax.it.lib_base.modules.handlers.MessageHandler;
import com.emax.it.lib_base.modules.handlers.MessageManager;
import com.emax.it.lib_base.ui.activities.BaseAppCompatActivity;
import com.emax.it.lib_ble.LibBleConstants;
import com.emax.it.lib_ble.R;
import com.emax.it.lib_ble.models.TagBle;
import com.emax.it.lib_ble.services.TagsScanService;
import com.emax.it.lib_ble.utils.BluetoothUtils;
public abstract class BaseTagsScanActivity extends BaseAppCompatActivity
implements MessageHandler.IOnHandleMessage,
BluetoothUtils.ICheckStatusBLE {
private static final String TAG = BaseTagsScanActivity.class.getSimpleName();
private static final String EXTRA = TAG + ".extra";
// Intents Extras
public static final String EXTRA_BLE_TAG = EXTRA + ".tagBLE";
public static final String EXTRA_RSSI_VALUE = EXTRA + ".valueRSSI";
protected Handler mScanHandler;
protected MessageManager mMessenger;
protected BLEScanServiceConnection mServiceConnection;
protected boolean mBound;
/** Override Lifecycle Methods **/
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initOnCreate();
}
#Override
protected void onStart() {
super.onStart();
BluetoothUtils.doFullCheckEnableBLE(this, this);
}
#Override
protected void onStop() {
super.onStop();
unbindToService();
}
#Override
protected void onDestroy() {
if(mScanHandler != null){
mScanHandler.removeCallbacksAndMessages(null);
mScanHandler = null;
}
mMessenger = null;
unbindToService();
stopTagsScanService();
super.onDestroy();
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == LibBleConstants.REQ_PERMISSION_BLUETOOTH_LOCATION) {
if (grantResults.length > 0x0 && grantResults[0x0] == PackageManager.PERMISSION_GRANTED) {
BluetoothUtils.doFullCheckEnableBLE(this, this);
} else {
Toast.makeText(this, getString(R.string.error_permission_not_granted), Toast.LENGTH_SHORT).show();
}
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode){
case LibBleConstants.REQ_CODE_ENABLE_BT:
if(resultCode == RESULT_OK){
BluetoothUtils.doFullCheckEnableBLE(this, this);
} else {
finish();
}
break;
case LibBleConstants.REQ_CODE_BLE_ENABLE_GPS:
LocationManager lM = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (lM != null && lM.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
onSuccessCheckBLE();
} else {
Toast.makeText(this, getString(R.string.error_scan_tags_require_gps), Toast.LENGTH_LONG).show();
finish();
}
break;
}
}
/** Override Actionbar Listener Methods **/
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
}
return super.onOptionsItemSelected(item);
}
/** Override ICheckStatusBLE Methods **/
#Override
public void onCheckStatusBleOk(){
onSuccessCheckBLE();
}
#Override
public void onCheckStatusBleError(int err) {
finish();
}
/** Protected Methods **/
protected void callBaseOnCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
}
protected void callBaseOnStart(){
super.onStart();
}
protected void initOnCreate(){
if(getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
mBound = false;
initMyServiceConnection();
initScanHandler();
}
protected void initScanHandler(){
mScanHandler = new Handler();
}
/** Service Methods **/
protected void stopTagsScanService(){
stopService(new Intent(this, TagsScanService.class));
}
protected void bindToTagsScanService(){
bindService(new Intent(this, TagsScanService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
}
protected void unbindToService(){
if(mBound) {
unbindService(mServiceConnection);
mBound = false;
}
}
/** Scan Methods **/
protected void startScan(int arg1, int arg2, #Nullable Bundle data){
startScan(TagsScanService.MSG_WHAT_SCAN_START, arg1, arg2, data);
}
protected void startScan(int what, int arg1, int arg2, #Nullable Bundle data){
if(mMessenger != null){
mMessenger.sendMessage(what, arg1, arg2, data);
}
}
protected void stopScan(int arg1, int arg2, #Nullable Bundle data){
if(mMessenger != null){
mMessenger.sendMessage(TagsScanService.MSG_WHAT_SCAN_STOP, arg1, arg2, data);
}
}
protected void startScanHandler(Runnable runnable, long time){
if(mScanHandler == null){
initScanHandler();
}
mScanHandler.postDelayed(runnable, time);
}
/** Check Methods **/
protected boolean checkStartStopScan(){
boolean ret = false;
boolean checkBLE = BluetoothUtils.checkBLE(this);
if(mBound && checkBLE){
ret = true;
} else if(!mBound && checkBLE){
showMessageServiceError();
} else {
BluetoothUtils.doFullCheckEnableBLE(this, this);
}
return ret;
}
/** Handle Message Methods **/
protected void onHandleMessageBluetoothOff(){
BluetoothUtils.doFullCheckEnableBLE(this, this);
}
protected void onHandleMessageGpsOff(){
BluetoothUtils.doFullCheckEnableBLE(this, this);
}
protected <T extends Parcelable> T getTagBleFromBundle(Bundle data){
if(data != null && data.containsKey(EXTRA_BLE_TAG)){
return (T) data.getParcelable(EXTRA_BLE_TAG);
}
return null;
}
protected int getRssiFromBundle(Bundle data){
if(data != null && data.containsKey(BaseTagsScanActivity.EXTRA_RSSI_VALUE)){
return data.getInt(BaseTagsScanActivity.EXTRA_RSSI_VALUE, 0x0);
}
return 0x0;
}
/** Messages Methods **/
protected void showMessageServiceError(){
Toast.makeText(this, R.string.error_scan_service_not_started, Toast.LENGTH_SHORT).show();
}
protected void showMessageScanStartError(){
Toast.makeText(this, R.string.error_start_scan, Toast.LENGTH_LONG).show();
}
protected void showMessageScanStopError(){
Toast.makeText(this, R.string.error_stop_scan, Toast.LENGTH_SHORT).show();
}
/** Private Methods **//** Init Methods **/
private void initMyServiceConnection(){
mServiceConnection = new BLEScanServiceConnection();
}
private void initMyMessenger(IBinder iBinder){
mMessenger = new MessageManager(this, iBinder);
mMessenger.sendHandshake();
}
/** Service Methods **/
private void onSuccessCheckBLE(){
bindToTagsScanService();
}
/** Private Classes **/
protected class BLEScanServiceConnection implements ServiceConnection {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
initMyMessenger(iBinder);
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
mMessenger = null;
mBound = false;
}
}
}
Rest of code you have the classes in my answer above and the message handlers in this answer:
Message Handlers between service and activity
Looks like you haven't implemented correctly,
You first need to create a new class file with the name "LeDeviceListAdapter" in the same package.
Once you created this class, you need to import this class in your activity/fragment.
This adapter is using BaseAdapter class.
// Adapter for holding devices found through scanning.
private class LeDeviceListAdapter extends BaseAdapter {
private ArrayList<BluetoothDevice> mLeDevices;
private LayoutInflater mInflator;
public LeDeviceListAdapter() {
super();
mLeDevices = new ArrayList<BluetoothDevice>();
mInflator = DeviceScanActivity.this.getLayoutInflater();
}
public void addDevice(BluetoothDevice device) {
if(!mLeDevices.contains(device)) {
mLeDevices.add(device);
}
}
public BluetoothDevice getDevice(int position) {
return mLeDevices.get(position);
}
public void clear() {
mLeDevices.clear();
}
#Override
public int getCount() {
return mLeDevices.size();
}
#Override
public Object getItem(int i) {
return mLeDevices.get(i);
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
// General ListView optimization code.
if (view == null) {
view = mInflator.inflate(R.layout.listitem_device, null);
viewHolder = new ViewHolder();
viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
BluetoothDevice device = mLeDevices.get(i);
final String deviceName = device.getName();
if (deviceName != null && deviceName.length() > 0)
viewHolder.deviceName.setText(deviceName);
else
viewHolder.deviceName.setText(R.string.unknown_device);
viewHolder.deviceAddress.setText(device.getAddress());
return view;
}
}
Layout File listitem_device.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="#+id/device_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24dp"/>
<TextView android:id="#+id/device_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12dp"/>
</LinearLayout>
ViewHolder class inside the Adapter class file
static class ViewHolder {
TextView deviceName;
TextView deviceAddress;
}
I'm facing an issue regarding cameraview. Do I have to import something since the error goes "cannot resolve flurgle" for import com.flurgle.camerakit.CameraView;
My permissions in manifest file are already set.
Heres the code using cameraview(taken from "https://blog.mindorks.com/android-tensorflow-machine-learning-example-ff0e9b2654cc.html" )
package com.example.mujtaba.basicai.Machine_Learning;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.mujtaba.basicai.R;
import com.flurgle.camerakit.CameraListener;
import com.flurgle.camerakit.CameraView;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class ImageRecognition extends AppCompatActivity {
private static final int INPUT_SIZE = 224;
private static final int IMAGE_MEAN = 117;
private static final float IMAGE_STD = 1;
private static final String INPUT_NAME = "input";
private static final String OUTPUT_NAME = "output";
private static final String MODEL_FILE = "file:///android_asset/tensorflow_inception_graph.pb";
private static final String LABEL_FILE =
"file:///android_asset/imagenet_comp_graph_label_strings.txt";
private Classifier classifier;
private Executor executor = Executors.newSingleThreadExecutor();
private TextView textViewResult;
private Button btnDetectObject, btnToggleCamera;
private ImageView imageViewResult;
private cameraView cameraView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_recognition);
cameraView = (CameraView) findViewById(R.id.cameraView);
imageViewResult = (ImageView) findViewById(R.id.imageViewResult);
textViewResult = (TextView) findViewById(R.id.textViewResult);
textViewResult.setMovementMethod(new ScrollingMovementMethod());
btnToggleCamera = (Button) findViewById(R.id.btnToggleCamera);
btnDetectObject = (Button) findViewById(R.id.btnDetectObject);
cameraView.setCameraListener(new CameraListener() {
#Override
public void onPictureTaken(byte[] picture) {
super.onPictureTaken(picture);
Bitmap bitmap = BitmapFactory.decodeByteArray(picture, 0, picture.length);
bitmap = Bitmap.createScaledBitmap(bitmap, INPUT_SIZE, INPUT_SIZE, false);
imageViewResult.setImageBitmap(bitmap);
final List<Classifier.Recognition> results = classifier.recognizeImage(bitmap);
textViewResult.setText(results.toString());
}
});
btnToggleCamera.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
cameraView.toggleFacing();
}
});
btnDetectObject.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
cameraView.captureImage();
}
});
initTensorFlowAndLoadModel();
}
#Override
protected void onResume() {
super.onResume();
cameraView.start();
}
#Override
protected void onPause() {
cameraView.stop();
super.onPause();
}
#Override
protected void onDestroy() {
super.onDestroy();
executor.execute(new Runnable() {
#Override
public void run() {
classifier.close();
}
});
}
private void initTensorFlowAndLoadModel() {
executor.execute(new Runnable() {
#Override
public void run() {
try {
classifier = TensorFlowImageClassifier.create(
getAssets(),
MODEL_FILE,
LABEL_FILE,
INPUT_SIZE,
IMAGE_MEAN,
IMAGE_STD,
INPUT_NAME,
OUTPUT_NAME);
makeButtonVisible();
} catch (final Exception e) {
throw new RuntimeException("Error initializing TensorFlow!", e);
}
}
});
}
private void makeButtonVisible() {
runOnUiThread(new Runnable() {
#Override
public void run() {
btnDetectObject.setVisibility(View.VISIBLE);
}
});
}
}
this is the view that crashes when i click start but i dont know why
i think it has something to do with the threding in the bruteForce function
package com.my.app;
import android.app.*;
import android.os.*;
import android.view.*;
import android.view.View.*;
import android.widget.*;
import android.content.*;
import android.graphics.*;
import android.media.*;
import android.net.*;
import android.text.*;
import android.util.*;
import java.util.*;
import java.text.*;
import android.test.*;
import android.view.animation.*;
import android.widget.Button;
public class TestActivity extends Activity {
private LinearLayout linear1;
private TextView original;
private TextView match;
private TextView text;
private Button bstart;
String attempt = new String();
boolean running;
int counter;
private String hash = "";
public String username = new String();
public static String password = "ZZZZZ";
public static char[] charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
private static char[] currentGuess = new char[1];
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
initialize();
initializeLogic();
}
private void initialize() {
linear1 = (LinearLayout) findViewById(R.id.linear1);
original = (TextView) findViewById(R.id.original);
match = (TextView) findViewById(R.id.match);
text = (TextView) findViewById(R.id.text);
bstart = (Button) findViewById(R.id.bstart);
bstart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View _v) {
bruteForce();
}
});
}
private void initializeLogic() {
Intent test = getIntent();
hash = test.getStringExtra("hash");
original.setText(password);
}
public void increment()
{
int index = currentGuess.length - 1;
while (index >= 0)
{
if (currentGuess[index] == charset[charset.length - 1])
{
if (index == 0)
{
currentGuess = new char[currentGuess.length + 1];
Arrays.fill(currentGuess, charset[0]);
break;
}
else
{
currentGuess[index] = charset[0];
index--;
}
}
else
{
currentGuess[index] = charset[Arrays.binarySearch(charset, currentGuess[index]) + 1];
break;
}
}
}
public String toString()
{
return String.valueOf(currentGuess);
}
public void bruteForce()
{
Animation animation = new Animation(){
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
text.setText(attempt);
}
};
animation.setRepeatCount(Animation.INFINITE);
text.startAnimation(animation);
new Thread(){
#Override
public void run() {
running = true;
while(running)
if (attempt.equals(password))
{
text.setText(attempt);
this.stop();
}
attempt = toString();
text.setText(attempt);
increment();
}
}.start();
}
#Override
protected void onStop() {
super.onStop();
running = false;
}
public void BruteForceTest()
{
Arrays.fill(currentGuess, charset[0]);
}
// created automatically
private void ShowMessage(String _s) {
Toast.makeText(getApplicationContext(), _s, Toast.LENGTH_SHORT).show();
}
private int GetRandom(int _minValue ,int _maxValue){
Random random = new Random();
return random.nextInt(_maxValue - _minValue + 1) + _minValue;
}
public ArrayList<Integer> getCheckedItemPositionsToArray(ListView _list) {
ArrayList<Integer> _result = new ArrayList<Integer>();
SparseBooleanArray _arr = _list.getCheckedItemPositions();
for (int _iIdx = 0; _iIdx < _arr.size(); _iIdx++) {
if (_arr.valueAt(_iIdx))
_result.add(_arr.keyAt(_iIdx));
}
return _result;
}
}
Your problem lies here
new Thread(){
#Override
public void run() {
running = true;
while(running)
if (attempt.equals(password))
{
text.setText(attempt);
this.stop();
}
attempt = toString();
text.setText(attempt);
increment();
}
}.start();
You can only update an UI element from the main thread. If you want to do it likes this you need to wrap text.setText(attempt) with a handler that posts it to the main thread. e.g.
new Handler(Looper.getMainLooper()).post(new Runnable(){
void run() {
text.setText(attempt);
}
});
Looper.getMainLooper() (docs) will get the main application thread which the handler then posts the Runnable to .
if (attempt.equals(password))
{
getActivity().runOnUiThread(new Runnable()
{
#Override
public void run()
{
text.setText(attempt);
}
});
this.stop();
}
Similarly wherever you are changing ui elements like setting text or changin colors do it in runOnUithread as I did above.
I've been trying to test the performance of Realm with large datasets. It seems to perform really well inserting and with queries that result in moderate resultset sizes but as soon as there is a result set that is large it starts impacting the main thread even though the actual writes are running async. I've written a test activity that shows the issue below. A few notes:
Attendee is a model with a primary key of "name" and an indexed integer field of "age".
The view has a text field showing the count, a button that calls reloadData on click, and an element that is interactive (I used a slider) to see skipped frames when using it.
If you change the query for the list so that it's causes a smaller result set (instead of less then change to equalsTo) then the issue goes away.
Is this a bug or am I doing something wrong?
package com.test.ui;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.test.R;
import com.test.model.Attendee;
import io.realm.OrderedRealmCollection;
import io.realm.Realm;
import io.realm.RealmAsyncTask;
import io.realm.RealmRecyclerViewAdapter;
import io.realm.RealmResults;
public class LoginActivity extends AppCompatActivity {
private static final int RECORD_COUNT = 200000;
private static final int TRANSACTION_SIZE = 1000;
private RealmAsyncTask mTask = null;
private RecyclerView mAttendeeList;
private TextView mCountText;
private Button mButton;
private Handler handler;
private Realm mRealm;
private RealmResults<Attendee> mActualList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mRealm = Realm.getDefaultInstance();
mCountText = (TextView) findViewById(R.id.count_text);
mButton = (Button) findViewById(R.id.button);
handler = new Handler(Looper.getMainLooper());
setCountText((int) mRealm.where(Attendee.class).count());
mActualList = mRealm.where(Attendee.class).lessThan("age", 20).findAllAsync();
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.deleteAll();
setCountText(0);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
mButton.setEnabled(true);
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
String text = "Error deleting";
Log.e("ANRTEST", text, error);
Toast.makeText(LoginActivity.this, text, Toast.LENGTH_LONG).show();
}
});
}
private void setCountText(int size) {
mCountText.setText(String.format("Count: %s", size));
}
#Override
protected void onDestroy() {
mActualList = null;
if(mTask != null && !mTask.isCancelled()) {
mTask.cancel();
mTask = null;
}
if(mRealm != null && !mRealm.isClosed()) {
mRealm.close();
}
super.onDestroy();
}
public void loadData(final TimerUtil t) {
Realm.Transaction.OnError onError = new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
mTask = null;
Toast.makeText(LoginActivity.this, "Finished should show now", Toast.LENGTH_LONG).show();
}
};
Realm.Transaction.OnSuccess onSuccess = new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
mTask = null;
int count = (int) mRealm.where(Attendee.class).count();
if (count >= RECORD_COUNT) {
Log.v("ANRTEST", String.format("Finished in %s seconds", t.currentElapsed() / 1000));
mButton.setEnabled(false);
} else {
handler.postDelayed(new Runnable() {
#Override
public void run() {
loadData(t);
}
}, 300);
}
setCountText(count);
}
};
mTask = mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
int innerStart = (int) realm.where(Attendee.class).count();
int i = 0;
while (i < TRANSACTION_SIZE && i + innerStart < RECORD_COUNT) {
Attendee attendee = new Attendee();
int innerValue = innerStart + i;
attendee.name = "name " + (innerValue + 1);
attendee.age = innerValue % 50;
realm.insert(attendee);
i++;
}
Log.v("ANRTEST", String.format("Checkpoint %s (%s seconds)", Math.min(innerStart + i, RECORD_COUNT), t.currentElapsed() / 1000));
}
}, onSuccess, onError);
}
public void reloadData(View view) {
//Setup start of process
mButton.setEnabled(false);
final TimerUtil t = TimerUtil.start();
loadData(t);
}
public static class TimerUtil {
private final long mStartTime;
public TimerUtil(long startTime) {
mStartTime = startTime;
}
public static TimerUtil start() {
return new TimerUtil(System.currentTimeMillis());
}
public long currentElapsed() {
return System.currentTimeMillis() - mStartTime;
}
}
}
I am trying to understand how this stuff works a little better.
So I learned about Runnables and Threads and ASyncTasks but apparently they have some serious drawbacks when it comes to configuration changes like rotating the screen.
Is it better to instead use IntentService for anything that should run in the background like SQL database commands, file-system procedures, Internet input/output processes, etc -- and then use LocalBroadcastReceiver to pass results back to the Activity?
Is it better to instead use IntentService for anything that should run in the background like SQL database commands, file-system procedures, Internet input/output processes, etc -- and then use LocalBroadcastReceiver to pass results back to the Activity?
A service is needed if your UI might move to the background while the work is going on, and you are concerned that your process might be terminated while the work is going on. I tend to only worry about this if the work might exceed a second or so. Otherwise, a plain thread suffices.
Using an event bus, like LocalBroadcastManager, is a reasonable approach to let other components know when your service/thread is done with its work. This sample app demonstrates this. Personally, I tend to use greenrobot's EventBus — this sample app is a clone of the first one, but using EventBus instead of LocalBroadcastManager.
Follow an example of a chat using one activity (chat activity) that run a class in service (realtime class). I use a mvc webapi to controll chat between chatters. When realtime receive a message "onConnected" or "ReceivedMessageServer" automatically send by LocalBroadcastManager to chat activity. This way in "onReceive" from BroadcastReceiver receives the message and runs other tasks.
a) ChatActivity.class
package br.com.yourapp;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.res.Configuration;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import java.util.Calendar;
import br.com.yourapp.model.MessageReceived;
import util.RealTime;
public class ChatActivity extends AppCompatActivity implements View.OnClickListener,
View.OnKeyListener , TextWatcher {
AppController obj;
private ProgressDialog pDialog;
MediaPlayer mp;
private ListView lstChatLog;
private ArrayAdapter<String> listAdapter;
private EditText txtChatMessage;
private TextView lblTyping;
private Button btnSendChat;
private boolean resultRequest = true;
private String errorMessage = "Internet is not working";
private AlertDialog alertDialog;
private AlertDialog.Builder alertBuilder;
String userType = "V";
String spaces = "\u0020\u0020\u0020\u0020";
Calendar time;
MessageReceived msg;
private RealTime realTime;
private final Context mContext = this;
private boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
pDialog = new ProgressDialog(this);
pDialog.setMessage("Loading...");
pDialog.setCancelable(false);
showProgressDialog();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
obj = (AppController) getApplicationContext();
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
lblTyping = (TextView) findViewById(R.id.lblTyping);
txtChatMessage = (EditText) findViewById(R.id.txtChatMessage);
txtChatMessage.setOnKeyListener(this);
txtChatMessage.addTextChangedListener(this);
btnSendChat = (Button) findViewById(R.id.btnSendChat);
btnSendChat.setOnClickListener(this);
alertDialog = new AlertDialog.Builder(this).create();
alertDialog.setTitle("Alert");
alertBuilder = new AlertDialog.Builder(this);
lstChatLog = (ListView) findViewById(R.id.lstChatLog);
listAdapter = new ArrayAdapter<String>(ChatActivity.this, android.R.layout.simple_list_item_1, android.R.id.text1) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
TextView tv = (TextView) view.findViewById(android.R.id.text1);
tv.setHeight(20);
tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12);
return view;
}
};
Intent intent = new Intent();
intent.setClass(mContext, RealTime.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
IntentFilter filter = new IntentFilter("Connect");
filter.addAction("RecMsg");
LocalBroadcastManager.getInstance(ChatActivity.this).registerReceiver(mMessageReceiver, filter);
}
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().toString().equals("Connect")) {
msg = (MessageReceived) intent.getExtras().getParcelable("msg");
if (msg == null || msg.Message == null)
return;
listAdapter.add(msg.Destiny + "" + msg.CurrentTime + " : " + msg.Message);
lstChatLog.setAdapter(listAdapter);
realTime.SendToSpecific(msg.Sender, "Hi !", userType, obj.getRiderId(), obj.getDriverId());
hideProgressDialog();
}
else
if (intent.getAction().toString().equals("RecMsg")) {
msg = (MessageReceived) intent.getExtras().getParcelable("msg");
if (msg == null || msg.Message == null)
return;
if (msg.Message.equals("*0.+9=&!*#_&1|8%digi")) {
lblTyping.setVisibility(View.VISIBLE);
}
else
if (msg.Message.equals("*0.+9=&!*#_&1|8%"))
{
lblTyping.setVisibility(View.INVISIBLE);
}
else
{
lblTyping.setVisibility(View.INVISIBLE);
listAdapter.add(msg.Sender + "" + msg.CurrentTime + " : " + msg.Message);
lstChatLog.setAdapter(listAdapter);
mp = MediaPlayer.create(ChatActivity.this, R.raw.notify);
mp.setLooping(false);
mp.setVolume(100, 100);
mp.start();
}
}
}
};
private final ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
RealTime.LocalBinder binder = (RealTime.LocalBinder) service;
realTime = binder.getService();
mBound = true;
realTime.Connect(obj.getRiderName(), userType, obj.getRiderId(), obj.getLatitudeRider(), obj.getLongitudeRider(), obj.getDriverId(), obj.getVehicieId());
hideProgressDialog();
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
#Override
protected void onStop() {
if (mBound) {
unbindService(mConnection);
mBound = false;
}
super.onStop();
}
public void SendToSpecific(String sender, String message, String userType, long userId, long operatorId) {
realTime.SendToSpecific(sender, message, userType, userId,operatorId);
if (!message.equals("*0.+9=&!*#_&1|8%digi") && !message.equals("*0.+9=&!*#_&1|8%"))
{
time = Calendar.getInstance();
listAdapter.add(spaces + sender + " " + "(" + time.get(Calendar.HOUR) + ":" + time.get(Calendar.MINUTE) + ")" + " : " + message);
lstChatLog.setAdapter(listAdapter);
txtChatMessage.setText("");
}
}
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode==KeyEvent.KEYCODE_ENTER) {
if (txtChatMessage.getText().length() > 0)
{
InputMethodManager imm = (InputMethodManager) this.getSystemService(Context.
INPUT_METHOD_SERVICE);
if (this.getCurrentFocus() != null)
imm.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0);
SendToSpecific(obj.getRiderName(), txtChatMessage.getText().toString(), userType, obj.getRiderId(), obj.getDriverId());
}
}
return false;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (txtChatMessage.getText().toString().length() > 0) {
if (txtChatMessage.getText().length() == 1) {
SendToSpecific(obj.getRiderName(), "*0.+9=&!*#_&1|8%digi", userType, obj.getRiderId(), obj.getDriverId());
}
} else {
SendToSpecific(obj.getRiderName(), "*0.+9=&!*#_&1|8%", userType, obj.getRiderId(), obj.getDriverId());
}
}
#Override
public void afterTextChanged(Editable s) {
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnSendChat:
if (txtChatMessage != null && txtChatMessage.getText().length() > 0) {
InputMethodManager imm = (InputMethodManager) this.getSystemService(Context.
INPUT_METHOD_SERVICE);
if (this.getCurrentFocus() != null)
imm.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0);
SendToSpecific(obj.getRiderName(), txtChatMessage.getText().toString(), userType, obj.getRiderId(), obj.getDriverId());
}
break;
}
}
private void showProgressDialog() {
if (!pDialog.isShowing())
pDialog.show();
}
private void hideProgressDialog() {
if (pDialog.isShowing())
pDialog.hide();
}
#Override
protected void onDestroy() {
try {
if (pDialog != null && pDialog.isShowing())
pDialog.dismiss();
} catch (Exception e) {
e.printStackTrace();
}
super.onDestroy();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.
INPUT_METHOD_SERVICE);
if (getCurrentFocus() != null)
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
mMessageReceiver.clearAbortBroadcast();
Intent i = new Intent(this, MenuPageActivity.class);
obj.setLastActivity("Chat");
startActivity(i);
return true;
}
public void ShowAlert() {
hideProgressDialog();
if (resultRequest)
errorMessage = "Internet is not working";
alertBuilder.setTitle("Alert");
alertBuilder.setMessage(errorMessage);
alertBuilder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
arg0.dismiss();
}
});
alertDialog = alertBuilder.create();
alertDialog.show();
errorMessage = "";
resultRequest = true;
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
}
b) Realtime.class
package util;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import java.util.concurrent.ExecutionException;
import br.com.yourapp.model.MessageReceived;
import microsoft.aspnet.signalr.client.Platform;
import microsoft.aspnet.signalr.client.SignalRFuture;
import microsoft.aspnet.signalr.client.http.android.AndroidPlatformComponent;
import microsoft.aspnet.signalr.client.hubs.HubConnection;
import microsoft.aspnet.signalr.client.hubs.HubProxy;
import microsoft.aspnet.signalr.client.hubs.SubscriptionHandler1;
import microsoft.aspnet.signalr.client.transport.ClientTransport;
import microsoft.aspnet.signalr.client.transport.ServerSentEventsTransport;
public class RealTime extends Service {
private HubConnection mHubConnection;
private HubProxy mHubProxy;
private Handler mHandler;
private final IBinder mBinder = new LocalBinder();
public RealTime() { }
#Override
public void onCreate() {
super.onCreate();
mHandler = new Handler(Looper.getMainLooper());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
int result = super.onStartCommand(intent, flags, startId);
startSignalR();
return result;
}
#Override
public void onDestroy() {
mHubConnection.stop();
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
startSignalR();
return mBinder;
}
public class LocalBinder extends Binder {
public RealTime getService() {
return RealTime.this;
}
}
public void Connect(String userName, String userType, long userId, double latitude, double longitude, long driverId, long vehicleId) {
mHubProxy.invoke("Connect", userName, userType, userId, latitude, longitude, driverId, vehicleId);
}
public void SendMessageToGroup(String userName, String userGroup, String userType, long userId, double latitude, double longitude, long vehicleId, short option)
{
mHubProxy.invoke("SendMessageToGroup", userName, userGroup, userType, userId, latitude, longitude, vehicleId, option);
}
public void SendToSpecific(String sender, String message, String userType, long userId, long operatorId)
{
mHubProxy.invoke("SendToSpecific", sender, message, userType, userId, operatorId);
}
private void startSignalR() {
Platform.loadPlatformComponent(new AndroidPlatformComponent());
mHubConnection = new HubConnection("http://webapi.com");
mHubProxy = mHubConnection.createHubProxy("ChatHub");
ClientTransport clientTransport = new ServerSentEventsTransport(mHubConnection.getLogger());
SignalRFuture<Void> signalRFuture = mHubConnection.start(clientTransport);
try {
signalRFuture.get();
} catch (InterruptedException | ExecutionException e) {
Log.e("SimpleSignalR", e.toString());
return;
}
mHubProxy.on("onConnected",
new SubscriptionHandler1<MessageReceived>() {
#Override
public void run(final MessageReceived msg) {
mHandler.post(new Runnable() {
#Override
public void run() {
Intent intent = new Intent("Connect");
intent.putExtra("msg", msg);
LocalBroadcastManager.getInstance(RealTime.this).sendBroadcast(intent);
}
});
}
}
, MessageReceived.class);
}
}
mHubProxy.on("ReceivedMessageServer",
new SubscriptionHandler1<MessageReceived>() {
#Override
public void run(final MessageReceived msg) {
mHandler.post(new Runnable() {
#Override
public void run() {
Intent intent = new Intent("RecMsg");
intent.putExtra("msg", msg);
LocalBroadcastManager.getInstance(RealTime.this).sendBroadcast(intent);
}
});
}
}
, MessageReceived.class);
}