Chrome Custom Tabs pre-load webpages while browsing? - java

I'm testing the Chrome Custom Tabs. It is supposed to preload webpages a user is likely to click in the background. This happens through calling
session.mayLaunchUrl(uri, null, null);
While the MainActivity is active, the webpage is preloaded nicely and when launching the URL, the webpage loads quickly as expected.
However I would like to serve other webpages automatically to the user while he is already browsing (and the activity therefore is in background). Then, preloading the webpages does not seem to speed up the loading process anymore and even though the new served webpages are loading, this happens slowly.
I wrote a small app demonstrating this behavior. Preloading and launching an URL manually works nicely (as the activity is active, I suppose), serving new webpages in a loop automatically is slow (as the activity is not active and the mayLaunchUrl method does not work as expected then).
Is it possible to make use of the pre-loading mechanism while the user is browsing already? If yes, how?
I added the MainActivity code as example below:
public class MainActivity extends AppCompatActivity {
private CustomTabsSession mCustomTabsSession;
private CustomTabsClient mClient;
private CustomTabsServiceConnection mConnection;
private EditText urlET;
private String TAG = "MainActivity";
private ArrayList<String> urlList;
private Thread cycleThread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.v("MainActivity", "onCreate");
urlList = new ArrayList<>();
urlList.add("http://www.google.com");
urlList.add("https://github.com");
urlList.add("http://stackoverflow.com");
urlList.add("http://www.heise.de");
// pre launch the chrome browser, bind services etc
warmup();
urlET = (EditText) findViewById(R.id.urlID);
// pre load a webpage manually
Button prepareBt = (Button) findViewById(R.id.prepareBt);
assert prepareBt != null;
prepareBt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mayLaunch(null);
}
});
//launch webpage manually
Button launchBt = (Button) findViewById(R.id.launchBt);
assert launchBt != null;
launchBt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
launch(null);
}
});
//start a loop that serves webpages every 10 seconds
Button cycleBt = (Button) findViewById(R.id.cycleBt);
assert cycleBt != null;
cycleBt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(cycleThread !=null)
cycleThread.interrupt();
cycleThread = new Thread(cycle);
cycleThread.start();
}
});
}
private Runnable cycle = new Runnable() {
#Override
public void run() {
int i = 0;
mayLaunch(Uri.parse(urlList.get(i)));
try {
Thread.sleep(5000);
while (true){
try {
Log.d(TAG, "launch: "+urlList.get(i));
launch(Uri.parse(urlList.get(i)));
i++;
if(i>=urlList.size())
i=0;
Thread.sleep(5000);
Log.d(TAG, "prepare: "+urlList.get(i));
mayLaunch(Uri.parse(urlList.get(i)));
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
private void mayLaunch(Uri uri){
if(uri ==null)
uri = Uri.parse(urlET.getText().toString());
CustomTabsSession session = getSession();
session.mayLaunchUrl(uri, null, null);
}
private void launch(Uri uri){
if(uri ==null)
uri = Uri.parse(urlET.getText().toString());
CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder(getSession())
.setToolbarColor(ContextCompat.getColor(this, R.color.colorPrimaryDark))
.setShowTitle(true)
.setStartAnimations(this, android.R.anim.slide_in_left, android.R.anim.slide_out_right)
.setExitAnimations(this, android.R.anim.slide_in_left, android.R.anim.slide_out_right)
.build();
customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
customTabsIntent.launchUrl(this,uri);
}
public CustomTabsSession getSession() {
if (mClient == null) {
mCustomTabsSession = null;
} else if (mCustomTabsSession == null) {
mCustomTabsSession = mClient.newSession(null);
Log.d(TAG, "getSession: created new session");
}
return mCustomTabsSession;
}
private void warmup(){
if (mClient != null) return;
String packageName = "com.android.chrome";
if (packageName == null) return;
mConnection = new CustomTabsServiceConnection() {
#Override
public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient customTabsClient) {
mClient = customTabsClient;
mClient.warmup(0L);
}
#Override
public void onServiceDisconnected(ComponentName name) {
mClient = null;
mCustomTabsSession = null;
}
};
CustomTabsClient.bindCustomTabsService(this, packageName, mConnection);
}
private void coolDown(){
if (mConnection == null) return;
unbindService(mConnection);
mClient = null;
mCustomTabsSession = null;
mConnection = null;
}
public void onDestroy() {
Log.v("MainActivity", "onDestroy");
super.onDestroy();
coolDown();
}
#Override
public void onBackPressed(){
}
}
Thanks!

CustomTabs ignores mayLaunchUrl() sent from the background. This is for 2 reasons:
Simple measure to avoid wasted bandwidth (it is easy to check that a custom tab from the same session is not the foreground, just did not do it yet)
Pages loaded through mayLaunchUrl() currently do not preserve Window.sessionStorage(), which is not a problem for App -> CustomTab transition, but for navigating within a single CustomTab this may break assumptions for users with navigation patterns like:
SiteA -> SiteB -> SiteA
(the second visit to SiteA does not see what is in the session). Teaching mayLaunchUrl() to choose the right sessionStorage() is complex, no plans for this yet.

Related

Both local and remote video call display black screen but audio works fine in agora

I am trying out agora sdk sample on android using java. when I run the app on my phone and remote device the audio actually works well but the video shows black screen. How do I solve this issue.
I have tried what was stated on agora docs but non worked for me.
I have checked permissions they are working fine
My front camera is working fine
I enabled the video on my code.
My Code
public class VideoChatViewActivity extends AppCompatActivity {
private static final String TAG = VideoChatViewActivity.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;
private boolean mCallEnd;
private boolean mMuted;
private FrameLayout mLocalContainer;
private RelativeLayout mRemoteContainer;
private SurfaceView mLocalView;
private SurfaceView mRemoteView;
private ImageView mCallBtn;
private ImageView mMuteBtn;
private ImageView mSwitchCameraBtn;
// Customized logger view
private LoggerRecyclerView mLogView;
#Override
public void onJoinChannelSuccess(String channel, final int uid, int elapsed) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mLogView.logI("Join channel success, uid: " + (uid & 0xFFFFFFFFL));
}
});
}
#Override
public void onFirstRemoteVideoDecoded(final int uid, int width, int height, int elapsed) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mLogView.logI("First remote video decoded, uid: " + (uid & 0xFFFFFFFFL));
setupRemoteVideo(uid);
}
});
}
#Override
public void onUserOffline(final int uid, int reason) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mLogView.logI("User offline, uid: " + (uid & 0xFFFFFFFFL));
onRemoteUserLeft();
}
});
}
};
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);
// Initializes the video view of a remote user.
mRtcEngine.setupRemoteVideo(new VideoCanvas(mRemoteView, VideoCanvas.RENDER_MODE_HIDDEN, uid));
mRemoteView.setTag(uid);
}
private void onRemoteUserLeft() {
removeRemoteVideo();
}
private void removeRemoteVideo() {
if (mRemoteView != null) {
mRemoteContainer.removeView(mRemoteView);
}
// Destroys remote view
mRemoteView = null;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_chat_view);
initUI();
// Ask for permissions at runtime.
// This is just an example set of permissions. Other permissions
// may be needed, and please refer to our online documents.
if (checkSelfPermission(REQUESTED_PERMISSIONS[0], PERMISSION_REQ_ID) &&
checkSelfPermission(REQUESTED_PERMISSIONS[1], PERMISSION_REQ_ID) &&
checkSelfPermission(REQUESTED_PERMISSIONS[2], PERMISSION_REQ_ID)) {
initEngineAndJoinChannel();
}
}
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);
mLogView = findViewById(R.id.log_recycler_view);
// Sample logs are optional.
showSampleLogs();
}
private void showSampleLogs() {
mLogView.logI("Welcome to Agora 1v1 video call");
mLogView.logW("You will see custom logs here");
mLogView.logE("You can also use this to show errors");
}
private boolean checkSelfPermission(String permission, int requestCode) {
if (ContextCompat.checkSelfPermission(this, permission) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, REQUESTED_PERMISSIONS, requestCode);
return false;
}
return true;
}
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String[] permissions, #NonNull int[] grantResults) {
if (requestCode == PERMISSION_REQ_ID) {
if (grantResults[0] != PackageManager.PERMISSION_GRANTED ||
grantResults[1] != PackageManager.PERMISSION_GRANTED ||
grantResults[2] != PackageManager.PERMISSION_GRANTED) {
showLongToast("Need permissions " + Manifest.permission.RECORD_AUDIO +
"/" + Manifest.permission.CAMERA + "/" + Manifest.permission.WRITE_EXTERNAL_STORAGE);
finish();
return;
}
// Here we continue only if all permissions are granted.
// The permissions can also be granted in the system settings manually.
initEngineAndJoinChannel();
}
}
private void showLongToast(final String msg) {
this.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
}
});
}
private void initEngineAndJoinChannel() {
// This is our usual steps for joining
// a channel and starting a call.
initializeEngine();
setupVideoConfig();
setupLocalVideo();
joinChannel();
}
private void initializeEngine() {
try {
mRtcEngine = RtcEngine.create(getBaseContext(), getString(R.string.agora_app_id), mRtcEventHandler);
} 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() {
// In simple use cases, we only need to enable video capturing
// and rendering once at the initialization step.
// Note: audio recording and playing is enabled by default.
mRtcEngine.enableVideo();
// Please go to this page for detailed explanation
// https://docs.agora.io/en/Video/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_rtc_engine.html#af5f4de754e2c1f493096641c5c5c1d8f
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.setupLocalVideo(new VideoCanvas(mLocalView, VideoCanvas.RENDER_MODE_HIDDEN, 0));
}
private void joinChannel() {
String token = getString(R.string.agora_access_token);
if (TextUtils.isEmpty(token) || TextUtils.equals(token, "#YOUR ACCESS TOKEN#")) {
token = null; // default, no token
}
mRtcEngine.joinChannel(token, "demoChannel1", "Extra Optional Data", 0);
}
#Override
protected void onDestroy() {
super.onDestroy();
if (!mCallEnd) {
leaveChannel();
}
/*
Destroys the RtcEngine instance and releases all resources used by the Agora SDK.
This method is useful for apps that occasionally make voice or video calls,
to free up resources for other operations when not making calls.
*/
RtcEngine.destroy();
}
private void leaveChannel() {
mRtcEngine.leaveChannel();
}
public void onLocalAudioMuteClicked(View view) {
mMuted = !mMuted;
// Stops/Resumes sending the local audio stream.
mRtcEngine.muteLocalAudioStream(mMuted);
int res = mMuted ? R.drawable.btn_mute : R.drawable.btn_unmute;
mMuteBtn.setImageResource(res);
}
public void onSwitchCameraClicked(View view) {
// Switches between front and rear cameras.
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();
removeRemoteVideo();
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);
}
}
I dont really know what is the issue. Anyone have idea on how I can solve this issue?
I found the solution for this
you have to add delay of 10 seconds before running below code
mRtcEngine!!.setClientRole(Constants.CLIENT_ROLE_BROADCASTER)
mRtcEngine!!.joinChannel(
stringToken,
channelName,
"Extra Optional Data",
currentUid
)
so the surface view of current user is loaded properly first and then it tries to join the call.
Simply put, dont join channel before surface view and camera is ready.

Android MediaPlayer return Prepare failed.: status=0x1 on device but runs in emulator

I'm trying to play an audio streaming from a remote http uri trough MediaPlayer.
The code seems to work fine in the emulator audio stream is played (but there's some noise over it) if I try to play on the device the .prepare() call fails throwing an IO exception. The message reports: status=0x1 . The only articles related with this error I found where talking about reading/writing permissions over files and I think this's not the case, some other where related to wrong sequence of calls (.setDataSource, .prepare(), .start()) neither this should be my problem.
This's the code I'm using:
public class MainActivity extends AppCompatActivity {
public static final String KEY_ADDRESS = "address";
private static final int REQUEST_INTERNET = 21;
private ImageButton exit_button;
private ImageButton stop_button;
private ImageButton play_button;
private MediaPlayer media_player;
private String uri;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
boolean _skip_init = false;
int check_internet = ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.INTERNET);
if (check_internet != PackageManager.PERMISSION_GRANTED) {
Log.i(getClass().getName(), "asking for internet access permission");
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.INTERNET},
REQUEST_INTERNET);
_skip_init = true;
}
getSettings(this); // set uri
exit_button = findViewById(R.id.exit_button);
play_button = findViewById(R.id.play_button);
stop_button = findViewById(R.id.stop_button);
exit_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});
play_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//TODO: start play
if(media_player!=null) {
media_player.start();
disablePlay();
}
}
});
stop_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//TODO: stop play
if(media_player!=null){
media_player.stop();
media_player.reset();
initStreaming(uri);
enablePlay();
}
}
});
play_button.setEnabled(false);
stop_button.setEnabled(false);
play_button.setAlpha(0.5f);
stop_button.setAlpha(0.5f);
if(!_skip_init) {
initMediaPlayer(this);
initStreaming(uri);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if ((requestCode == REQUEST_INTERNET) && (grantResults.length > 0)) {
for (int i = 0; i < permissions.length; i++)
if ((grantResults[i] == PackageManager.PERMISSION_GRANTED)) {
Log.i(getClass().getName(), permissions[i] + " permission granted");
if (Manifest.permission.INTERNET.equals(permissions[i])) {
initMediaPlayer(this);
initStreaming(uri);
}
}
}
}
private void initMediaPlayer(#NonNull final Context context) {
media_player = new MediaPlayer();
media_player.setAudioStreamType(AudioManager.STREAM_MUSIC);
media_player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
Log.d(getClass().getName(), "onPreparedListener");
play_button.setEnabled(true);
play_button.setAlpha(1f);
}
});
media_player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
Log.d(getClass().getName(), "onCompletionListener");
Toast.makeText(context, getString(R.string.streaming_completed), Toast.LENGTH_LONG).show();
}
});
}
private void initStreaming(#NonNull final String uri) {
new Thread(
new Runnable() {
#Override
public void run() {
try {
media_player.setDataSource(uri);
media_player.prepareAsync();
} catch (Throwable e) {
Log.e(getClass().getName(), "Exception: " + e.getMessage());
}
}
}
).start();
}
#Override
protected void onDestroy() {
//TODO: stop and free resources?
Log.d(getClass().getName(), "onDestroy");
if(media_player!=null && media_player.isPlaying())
media_player.stop();
super.onDestroy();
}
private void enablePlay() {
play_button.setEnabled(true);
stop_button.setEnabled(false);
play_button.setAlpha(1f);
stop_button.setAlpha(0.5f);
}
private void disablePlay() {
play_button.setEnabled(false);
stop_button.setEnabled(true);
play_button.setAlpha(0.5f);
stop_button.setAlpha(1f);
}
}
Where's the error? Is possible to fix it?
Why the emulator is able to run it without errors?
--- UPDATE ---
same error using .prepareAsync()
--- UPDATE ---
On the very same hardware configuration (Samsung Galaxy S9 stock), where the application worked with the simple fix when uploaded trough adb over usb link once the app is signed in release variant it stopped working and I got again the same error code reported above.
On different hardware like Huawei P30 it works (installed as a signed release .apk).
Can this depend on a firewall rule?
Apparently the problem is fixed by this entry:
android:usesCleartextTraffic="true"
In AndroidManifest.xml file (application section).
-- UPDATE --
Actually the problem was given by some abnormal firewall rules that rejects connections from the same host after a give number of consecutive access.

Queue_Flush Not flushing Text and speaking updated

I read many blogs but still facing issue with speaking of updated text string after calling shutdown. Next time clicking on play, will speak old string and new one after other. Here is the code:
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.batful);
try {
editor = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit();
// editor.putBoolean("b", true).apply();
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
value = sharedPreferences.getString("personalmessage", null);
Log.e("Voice Message create: ", value);
value = sharedPreferences.getString("personalmessage", null);
textToSpeech = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {
#Override
public void onInit(int i) {
if (i != TextToSpeech.ERROR) {
textToSpeech.setLanguage(Locale.ENGLISH);
/* textToSpeech.setLanguage(Locale.getDefault());*/
final Handler h = new Handler();
h.postDelayed(new Runnable() {
public void run() {
if (Build.VERSION.SDK_INT >= 21) {
textToSpeech.speak(value, TextToSpeech.QUEUE_FLUSH, null, null);
} else {
textToSpeech.speak(value, TextToSpeech.QUEUE_FLUSH, null);
}
h.postDelayed(this, delay);
}
}, delay);
}
}
});
stopvol = (TextView)
findViewById(R.id.stop);
stopvol.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view) {
turnoffbeep(getApplicationContext());
finish();
}
});
} catch (
Exception e)
{
e.printStackTrace();
}
}
static void turnoffbeep(Context contextforstat) {
Log.i(TAG, "Turn Off beep");
if ((textToSpeech != null)) {
// textToSpeech.stop();
textToSpeech.shutdown();
}
}
If u see QUEUE_FLUSH is added but results are not achieved. Looking at Android documentation, I believe I m following correct. Still If someone help me in looking for correct reason for not dropping old text, pls
Just got other fact if I change input from shared preference to edit text , it is speaking latest text not old.
after R&D got issue, it was handler which are creating mess and picking old text. As shutdown do not cancel handler and replay was adding another handler to existing.

Stopping Ongoing Runnable Android

I am creating a BLE app that needs to continuously start and stop scanning for predefined intervals. The way I have implemented it is with two runnables that call each other as follows:
private Runnable scan = new Runnable() {
#Override
public void run() {
scanHandler.postDelayed(stopScan, SCAN_PERIOD);
mLEScanner.startScan(filters, settings, mScanCallback);
Log.e("BLE_Scanner", "Start Scan");
}
};
private Runnable stopScan = new Runnable() {
#Override
public void run() {
mLEScanner.stopScan(mScanCallback);
scanHandler.postDelayed(scan, STOP_PERIOD);
Log.e("BLE_Scanner", "Stop Scan");
}
};
I am attempting to start the continuous scan and pause on a button click. The start button starts the process fine but I am having trouble with stopping the scanning.
//scan button functionality
scanButton=(Button)findViewById(R.id.scan_button);
scanButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
spinner.setVisibility(View.VISIBLE);
scan.run();
}
});
//stop scan button functionality
stopButton=(Button)findViewById(R.id.stop_button);
stopButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
spinner.setVisibility(View.INVISIBLE);
scanHandler.removeCallbacks(scan);
scanHandler.removeCallbacks(stopScan);
}
});
If I press the stop button during the stop interval the scan will stop. However, if I press the stop button while the scan runnable is running it appears to remove the callbacks for the stopScan runnable while leaving the scan runnable continuously running. What I need is for both of the runnables to stop on the button press. To provide more detail, my entire code is provided below. Thanks for the help.
public class MainActivity extends Activity {
private BluetoothAdapter mBluetoothAdapter;
private int REQUEST_ENABLE_BT = 1;
private static final long SCAN_PERIOD = 5000;
private static final long STOP_PERIOD = 1000;
private BluetoothLeScanner mLEScanner;
private ScanSettings settings;
private List<ScanFilter> filters;
private BluetoothGatt mGatt;
private Button scanButton;
private Button stopButton;
//private String proximityUUID = "0000180f-0000-1000-8000-00805f9b34fb";
private ProgressBar spinner;
private Handler scanHandler;
private String[] filterList = {
"D9:ED:5F:FA:0E:02",
"FF:37:3A:25:56:C7",
"F4:57:89:69:93:91"
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
scanHandler = new Handler();
//determine if device supports BLE
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE Not Supported",
Toast.LENGTH_SHORT).show();
finish();
}
//set up bluetooth manager
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
//scan progress bar
spinner=(ProgressBar)findViewById(R.id.progressBar);
spinner.setVisibility(View.GONE);
//scan button functionality
scanButton=(Button)findViewById(R.id.scan_button);
scanButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
spinner.setVisibility(View.VISIBLE);
scan.run();
}
});
//stop scan button functionality
stopButton=(Button)findViewById(R.id.stop_button);
stopButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
spinner.setVisibility(View.INVISIBLE);
scanHandler.removeCallbacks(scan);
scanHandler.removeCallbacks(stopScan);
}
});
}
#Override
protected void onResume() {
super.onResume();
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} else {
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
//scan settings
settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
//scan filter
//populate the filter list
filters = new ArrayList<ScanFilter>();
for (int i=0; i< filterList.length ; i++) {
ScanFilter filter = new ScanFilter.Builder().setDeviceAddress(filterList[i]).build();
filters.add(filter);
}
}
}
#Override
protected void onPause() {
super.onPause();
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
}
}
#Override
protected void onDestroy() {
if (mGatt == null) {
return;
}
mGatt.close();
mGatt = null;
super.onDestroy();
}
//start scan
private Runnable scan = new Runnable() {
#Override
public void run() {
scanHandler.postDelayed(stopScan, SCAN_PERIOD);
mLEScanner.startScan(filters, settings, mScanCallback);
Log.e("BLE_Scanner", "Start Scan");
}
};
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.i("callbackType", String.valueOf(callbackType));
Log.i("result", result.toString());
BluetoothDevice device = result.getDevice();
int mRSSI = result.getRssi();
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
Log.i("ScanResult - Results", sr.toString());
}
}
#Override
public void onScanFailed(int errorCode) {
Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
//stop scan
private Runnable stopScan = new Runnable() {
#Override
public void run() {
mLEScanner.stopScan(mScanCallback);
scanHandler.postDelayed(scan, STOP_PERIOD);
Log.e("BLE_Scanner", "Stop Scan");
}
};
private static double calculateAccuracy(int txPower, double rssi) {
if (rssi == 0) {
return -1.0; // if we cannot determine accuracy, return -1.
}
double ratio = -rssi*1.0/txPower;
if (ratio < 1.0) {
return Math.pow(ratio,10);
}
else {
double accuracy = (0.89976)*Math.pow(ratio,7.7095) + 0.111;
return accuracy;
}
}
}
I imagine you just want to call startScan immediately on start button press (not in a Runnable, not scheduled through a Handler). The call is asynchronous, so nothing will block, and Android will do all the scanning in another thread. If you then want to schedule a call to stop in the future, then you use the Handler to post a Runnable that calls stopScan at the delay you need.
The button for stopping the scan can also just directly call stopScan() if know that a scan was previously in progress. You might want to use a boolean to gate the call to stopScan only if there was a previous call to startScan().
So, I ended up finding a way to get it to work as intended. I don't know if the way I'm doing things is best practice as I'm new to Android and Java, but this is what worked for me. All I did was call the stopScan method in the stop button after removing the handler callbacks.
//stop scan button functionality
stopButton=(Button)findViewById(R.id.stop_button);
stopButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
spinner.setVisibility(View.INVISIBLE);
scanHandler.removeCallbacksAndMessages(null);
mLEScanner.stopScan(mScanCallback);
}
});

BitmapFactory.decodeStream() is working without INTERNET PERMISSION

I am supposed to fetch images from internet and display it on my activity.For this i am using asynctask class.I forgot to mention INTERNET permission in my manifest file and the application is running succesfully without giving any exception and is able to fetch all the images on Emulator.
On device it is showing Network error.
My question is why emulator is behaving like this.I am using genymotion emulator (Google Nexus 4)
Code is like:-
public class MainActivity extends ActionBarActivity implements View.OnClickListener {
Button btnClick = null;
Button btnClick2 = null;
Context context = MainActivity.this;
ImageView imageview = null;
ProgressBar progressBar;
String URL = "http://images.visitcanberra.com.au/images/canberra_hero_image.jpg";
String URLTiger = "http://www.hdwallpapersimages.com/wp-content/uploads/2014/01/Winter-Tiger-Wild-Cat-Images.jpg";
String URLTree = "http://blog.jimdo.com/wp-content/uploads/2014/01/tree-247122.jpg";
String URLSky = "http://www.ucl.ac.uk/news/news-articles/1213/muscle-fibres-heart.jpg";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnClick = (Button) findViewById(R.id.btnClick);
btnClick2 = (Button)findViewById(R.id.btnClick2);
imageview = (ImageView)findViewById(R.id.imageview);
progressBar = (ProgressBar)findViewById(R.id.progressbar);
progressBar.setVisibility(ProgressBar.GONE);
}
#Override
public void onClick(View v) {
switch(v.getId())
{
case R.id.btnClick:
new ImageDownloader(this).execute("http://stylonica.com /wp-content/uploads/2014/02/nature.jpg");
new ImageDownloader(this).execute(URLTiger);
new ImageDownloader(this).execute(URLTree);
new ImageDownloader(this).execute(URLSky);
break;
case R.id.btnClick2:
new Thread(new Runnable() {
#Override
public void run() {
btnClick2.post(new Runnable() {
#Override
public void run() {
btnClick2.setText("Thread B");
}
});
}
}).start();
break;
}
}
public class ImageDownloader extends AsyncTask<String,String,Bitmap> {
ProgressDialog progressDialog;
Context context;
Bitmap bitmap;
public ImageDownloader(Context context) {
this.context = context;
}
#Override
protected void onPreExecute() {
// progressDialog = ProgressDialog.show(context,"Loading Image ","Please wait....");
progressBar.setVisibility(ProgressBar.VISIBLE);
}
#Override
protected Bitmap doInBackground(String... args) {
try {
Log.d("new URL(args[0]..............", new URL(args[0]) + "");
bitmap = BitmapFactory.decodeStream((InputStream) new URL(args[0]).getContent());
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
#Override
protected void onPostExecute(Bitmap image) {
if(image != null){
imageview.setImageBitmap(image);
// progressDialog.dismiss();
progressBar.setVisibility(ProgressBar.INVISIBLE);
}else{
// progressDialog.dismiss();
progressBar.setVisibility(ProgressBar.INVISIBLE);
Toast.makeText(context, "Image Does Not exist or Network Error", Toast.LENGTH_SHORT).show();
}
}
}
}
And I have not mentioned INTERNET permission in my manifest file.
Disclaimer: I work on Genymotion.
The answer is that it is a Genymotion bug. :(
Android API compliance is absolutely fundamental for Genymotion.
Please, accept our apologies for the time you lost on this.
We investigated this bug immediately and fixed it.
You will have the fix in our next release.
The problem only happen on version 2.3.7, 4.1.1 et 4.2.2.

Categories

Resources