When attempting to poll a HTTP endpoint, notifications are noticeably slower (2 - 3 seconds active, 10 seconds on average but occasionally as long as a minute and a half when locked) to generate when the phone is active in comparison to when it is locked.
The application requests for it not to be optimised on Android M+. This does not reduce response time.
EDIT:
Attempted to use WakeLock within the service. No change in behaviour.
The issue appears to be that the connection is consistently timing out when the phone is in idle mode.
EDIT 2:
The interval period is every 2 seconds between requests.
Main Activity
package uk.co.xxx;
import android.app.AlarmManager;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.SystemClock;
import android.provider.Settings;
import android.support.annotation.RequiresApi;
import org.qtproject.qt5.android.bindings.QtActivity;
import dagger.android.AndroidInjection;
public class NotificationActivity extends QtActivity {
public static final String NURSE_CALL_CHANNEL_ID = "NURSE_CALL_CHANNEL";
public static final String HIGH_PRIORITY_CHANNEL_ID = "HIGH_PRIORITY_CHANNEL";
#Override
public void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel nurseCallChannel = createNotificationChannel();
NotificationChannel highPriorityChannel = createHighPriorityChannel();
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
NotificationManager notificationManager = getSystemService(NotificationManager.class);
if (notificationManager != null) {
notificationManager.createNotificationChannel(nurseCallChannel);
notificationManager.createNotificationChannel(highPriorityChannel);
}
}
//Ask permission to ignore battery optimisations, but only on API 23+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PowerManager manager = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (!manager.isIgnoringBatteryOptimizations(getPackageName())) {
Intent intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
startActivity(intent);
}
}
}
#RequiresApi(api = Build.VERSION_CODES.O)
private NotificationChannel createNotificationChannel() {
CharSequence name = getString(R.string.channel_name);
String description = getString(R.string.channel_description);
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel = new NotificationChannel(NURSE_CALL_CHANNEL_ID, name, importance);
channel.setDescription(description);
String audioPath = ContentResolver.SCHEME_ANDROID_RESOURCE
+ "://" + getPackageName() + "/" + R.raw.call;
AudioAttributes attributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
.setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
.build();
channel.setSound(Uri.parse(audioPath), attributes);
channel.enableLights(true);
channel.setLightColor(Color.WHITE);
channel.enableVibration(true);
channel.setVibrationPattern(new long[] {0, 200, 200, 200});
channel.setShowBadge(true);
return channel;
}
#RequiresApi(api = Build.VERSION_CODES.O)
private NotificationChannel createHighPriorityChannel() {
CharSequence name = getString(R.string.high_priority_channel_name);
String description = getString(R.string.high_priority_channel_description);
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel = new NotificationChannel(HIGH_PRIORITY_CHANNEL_ID, name, importance);
channel.setDescription(description);
String audioPath = ContentResolver.SCHEME_ANDROID_RESOURCE
+ "://" + getPackageName() + "/" + R.raw.emergency;
AudioAttributes attributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
.setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
.build();
channel.setSound(Uri.parse(audioPath), attributes);
channel.enableLights(true);
channel.setLightColor(Color.RED);
channel.enableVibration(true);
channel.setVibrationPattern(new long[] {0, 300});
channel.setShowBadge(true);
return channel;
}
public void startLiveCallService() {
Intent serviceIntent = new Intent(this, LiveCallService.class);
startService(serviceIntent);
}
}
Service Class
package uk.co.xxx;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.util.Log;
import android.util.SparseArray;
import java.io.IOException;
import java.text.DateFormat;
import javax.inject.Inject;
import dagger.android.AndroidInjection;
import retrofit2.Call;
import retrofit2.Response;
import uk.co.xxx.api.CallItem;
import uk.co.xxx.api.HostSelectionInterceptor;
import uk.co.xxx.api.LiveCallResponse;
import uk.co.xxx.api.NurseCallApiInterface;
import uk.co.xxx.db.dao.ConfigurationDao;
import uk.co.xxx.db.dao.LiveCallDao;
import uk.co.xxx.db.entities.Configuration;
import uk.co.xxx.db.entities.LiveCall;
public class LiveCallService extends Service {
#Inject
public NurseCallApiInterface nurseCallApiInterface;
#Inject
public HostSelectionInterceptor hostSelectionInterceptor;
#Inject
public LiveCallDao liveCallDao;
#Inject
public ConfigurationDao configurationDao;
private Looper looper;
private Handler handler;
private final class NurseCallRunnable implements Runnable {
private static final int RUNNABLE_DELAY = 2000;
#Override
public void run() {
Log.i("LIVECALL", "Handling call intent!");
String ipAddress = configurationDao.getIpAddress();
Log.i("LIVECALL", "IP ADDRESS:" + ipAddress);
hostSelectionInterceptor.setHost(ipAddress);
Call<LiveCallResponse> call = nurseCallApiInterface.getLiveCalls();
Response<LiveCallResponse> response = null;
try {
response = call.execute();
} catch (IOException e) {
Log.e("LIVECALL", "IOException occurred when attempting to retrieve live calls", e);
}
boolean retrievalSuccess = response != null;
Configuration activeNetworkConfiguration = new Configuration(
"IS_ACTIVE_NETWORK",
retrievalSuccess ? "1" : "0"
);
configurationDao.update(activeNetworkConfiguration);
if (!retrievalSuccess) {
handler.postDelayed(this, RUNNABLE_DELAY);
return;
}
LiveCallResponse liveCallResponse = response.body();
if (liveCallResponse == null) {
handler.postDelayed(this, RUNNABLE_DELAY);
return;
}
Log.i("LIVECALL", "response:" + liveCallResponse.toString());
boolean isNew;
boolean isUpdate;
SparseArray<LiveCall> pendingRemovalCalls = new SparseArray<>();
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(LiveCallService.this);
Intent activityIntent = new Intent(LiveCallService.this, NotificationActivity.class);
activityIntent.setAction(Intent.ACTION_MAIN);
activityIntent.addCategory(Intent.CATEGORY_LAUNCHER);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(LiveCallService.this, 0, activityIntent, 0);
for (LiveCall existingLiveCall: liveCallDao.getAll()) {
pendingRemovalCalls.put(existingLiveCall.getCallerTagId(), existingLiveCall);
}
if (liveCallResponse.getCallItems() != null) {
for (CallItem callItem : liveCallResponse.getCallItems()) {
isNew = true;
isUpdate = false;
LiveCall existingCall = liveCallDao.getBySender(Integer.toString(callItem.getCallpointId()));
//Without a sender GUID, we are using caller ID as a substitute
LiveCall liveCall = new LiveCall.Builder(Integer.toString(callItem.getCallpointId()))
.callerId(callItem.getCallpointId())
.callTypeId(callItem.getCallTypeId())
.callerName(callItem.getCalledBy())
.zoneId(callItem.getZoneId())
.priority(callItem.getPriority())
.date(callItem.getReceivedTime())
.forwardingBleRuName("")
.build();
pendingRemovalCalls.remove(liveCall.getCallerTagId());
if (existingCall != null) {
isNew = false;
int existingTypeId = existingCall.getCallTypeId();
int newTypeId = callItem.getCallTypeId();
if (existingTypeId != newTypeId || !liveCall.getForwardingBleRuName().equals("")) {
isUpdate = true;
}
}
if (isNew ^ isUpdate) {
Log.i("LIVECALL", "Database to be updated!");
if (isNew) {
liveCallDao.insert(liveCall);
} else {
liveCallDao.update(liveCall);
}
notificationManager.notify(liveCall.getCallerTagId(), createNotification(pendingIntent, callItem));
}
}
}
for (int i = 0, existingLiveCallSize = pendingRemovalCalls.size(); i < existingLiveCallSize; i++) {
LiveCall liveCall = pendingRemovalCalls.valueAt(i);
liveCallDao.delete(liveCall);
notificationManager.cancel(liveCall.getCallerTagId());
}
handler.postDelayed(this, RUNNABLE_DELAY);
}
}
#Override
public void onCreate() {
AndroidInjection.inject(this);
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work doesn't disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
looper = thread.getLooper();
handler = new Handler(looper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Runnable runnable = new LiveCallService.NurseCallRunnable();
handler.post(runnable);
// If we get killed, after returning from here, restart
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
#Override
public void onDestroy() {
super.onDestroy();
looper.quit();
}
private Notification createNotification(PendingIntent intent, #NonNull CallItem callItem) {
String shortContent = callItem.getCalledBy();
String bigContent = callItem.getCallType() + "\n" + DateFormat.getDateTimeInstance().format(callItem.getReceivedTime());
String channelId;
//High priority call
if (callItem.getPriority() == 1) {
channelId = NotificationActivity.NURSE_CALL_CHANNEL_ID;
} else {
channelId = NotificationActivity.HIGH_PRIORITY_CHANNEL_ID;
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle(shortContent)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(bigContent))
.setPriority(NotificationCompat.PRIORITY_MAX)
.setContentIntent(intent);
if (callItem.getColour() != null && !callItem.getColour().isEmpty()) {
builder.setColorized(true);
builder.setColor(callItem.getColourCode());
}
//Sounds, light colour & vibration pattern are handled by Notification Channel on Oreo and up
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
long[] vibrationPattern;
int colour;
//High priority call
if (callItem.getPriority() == 1) {
vibrationPattern = new long[] {0, 300};
colour = Color.RED;
} else {
vibrationPattern = new long[] {0, 200, 200, 200};
colour = Color.WHITE;
}
builder.setVibrate(vibrationPattern)
.setLights(colour, 500, 200);
if (callItem.getSound() != null && !callItem.getSound().isEmpty()) {
builder.setSound(callItem.getSoundUri());
}
}
Notification notification = builder.build();
notification.flags |= Notification.FLAG_ONGOING_EVENT;
if (callItem.getPriority() == 1) {
notification.flags |= Notification.FLAG_INSISTENT;
}
return notification;
}
}
Related
I have written a program to read out nfc Tags and visualize them onto a Toast which works perfectly fine.
Code:
package com.example.myapplication;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NdefMessage;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import android.widget.Toast;
import java.io.UnsupportedEncodingException;
public class MainActivity extends Activity {
NfcAdapter nfcAdapter;
PendingIntent pendingIntent;
IntentFilter writeTagFilters[];
Tag myTag;
Context context;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null) {
// Stop here, we definitely need NFC
Toast.makeText(this, "This device doesn't support NFC.", Toast.LENGTH_LONG).show();
finish();
}
readFromIntent(getIntent());
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
writeTagFilters = new IntentFilter[]{tagDetected};
}
private void readFromIntent(Intent intent) {
String action = intent.getAction();
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage[] msgs = null;
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
}
}
buildTagViews(msgs);
}
}
private void buildTagViews(NdefMessage[] msgs) {
if (msgs == null || msgs.length == 0) return;
String text = "";
// String tagId = new String(msgs[0].getRecords()[0].getType());
byte[] payload = msgs[0].getRecords()[0].getPayload();
String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16"; // Get the Text Encoding
int languageCodeLength = payload[0] & 0063; // Get the Language Code, e.g. "en"
// String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
try {
// Get the Text
text = new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
} catch (UnsupportedEncodingException e) {
Log.e("UnsupportedEncoding", e.toString());
}
Toast.makeText(getApplicationContext(), text, Toast.LENGTH_LONG).show();
}
#Override
protected void onNewIntent(Intent intent) {
setIntent(intent);
readFromIntent(intent);
if(NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())){
myTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
}
}
}
But when I want this to be started in another File the app just crashes without giving me an error.
This is my used code:
package com.example.myapplication;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NdefMessage;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import android.widget.Toast;
import java.io.UnsupportedEncodingException;
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Nfc nfc = new Nfc();
nfc.startnfc();
}
}
I didn't change the onCreate since it was imported from my main app, so you have an easier time to understand my code. It obviously wouldn't make a difference..
package com.example.myapplication;
import static java.sql.DriverManager.println;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NdefMessage;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import android.widget.Toast;
import java.io.UnsupportedEncodingException;
public class Nfc extends Activity {
NfcAdapter nfcAdapter;
PendingIntent pendingIntent;
IntentFilter writeTagFilters[];
Tag myTag;
Context context;
public void startnfc() {
setContentView(R.layout.activity_main);
context = this;
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null) {
// Stop here, we definitely need NFC
Toast.makeText(this, "This device doesn't support NFC.", Toast.LENGTH_LONG).show();
finish();
}
readFromIntent(getIntent());
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
writeTagFilters = new IntentFilter[]{tagDetected};
}
private void readFromIntent(Intent intent) {
String action = intent.getAction();
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage[] msgs = null;
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
}
}
buildTagViews(msgs);
}
}
private void buildTagViews(NdefMessage[] msgs) {
if (msgs == null || msgs.length == 0) return;
String text = "";
// String tagId = new String(msgs[0].getRecords()[0].getType());
byte[] payload = msgs[0].getRecords()[0].getPayload();
String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16"; // Get the Text Encoding
int languageCodeLength = payload[0] & 0063; // Get the Language Code, e.g. "en"
// String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
try {
// Get the Text
text = new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
} catch (UnsupportedEncodingException e) {
Log.e("UnsupportedEncoding", e.toString());
}
Toast.makeText(getApplicationContext(), text, Toast.LENGTH_LONG).show();
}
#Override
protected void onNewIntent(Intent intent) {
setIntent(intent);
readFromIntent(intent);
if(NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())){
myTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
}
}
}
this is my second week Ive been programming with Java. Sorry in advance for dumb mistakes
This is a standard class inheritance problem.
Really what it looks like you were trying to do create a new base class by extending Activity
So in your MainActivity.java change the line
public class MainActivity extends Activity {
to
public class MainActivity extends Nfc {
then you can change
Nfc nfc = new Nfc();
nfc.startnfc();
to
startnfc()
As your Nfc class has already extended the Activity class, so when the Android framework creates an instance of MainActivity your startnfc method is available
The Activity or something extended from that is not something you ever create an instance of yourself
I'm making an application that plays audio files off the device's storage, and I have a seek bar that checks the progress of the audio file (how long it has played) every second, and updates the seekbar accordingly.
The audio is played through a service that runs in the foreground, and also displays a notification that the audio is playing.
When the audio ends, I noticed the handler still goes in it's cycle, and I want to end the handler once the audio is done.
What I'm currently trying to do is to end the handler from inside the runnable that the handler runs, as I'm not sure how else I can end it.
Main Activity, where I handle OnClick from the ListView where you can select an audio file and also handles the seekbar update.
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity {
SeekBar musicProgBar;
Handler progBarHandler = null;
Runnable r = null;
private MP3Service.MyBinder myService = null;
TextView progressText = null;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
musicProgBar = findViewById(R.id.musicProgressBar);
progressText = findViewById(R.id.progressText);
final ListView lv = (ListView) findViewById(R.id.musicList);
Cursor cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
null,
MediaStore.Audio.Media.IS_MUSIC + "!= 0",
null,
null);
progBarHandler = new Handler();
final int progBarDelay = 1000; // delay for the handler, making it repeat itself only every 1000 miliseconds (1 second)
lv.setAdapter(new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_1,
cursor,
new String[] { MediaStore.Audio.Media.DATA},
new int[] { android.R.id.text1 }));
Intent tempIntent = new Intent(this, MP3Service.class);
startService(tempIntent);
bindService(tempIntent, serviceConnection, 0);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> myAdapter,
View myView,
int myItemInt,
long myLong) {
Cursor c = (Cursor) lv.getItemAtPosition(myItemInt);
String uri = c.getString(c.getColumnIndex(MediaStore.Audio.Media.DATA));
Log.d("g53mdp", uri);
if (myService.fetchPlayerState() != MP3Player.MP3PlayerState.STOPPED)
myService.stopMusic();
myService.loadMusic(uri);
myService.playMusic();
musicProgBar.setMax(myService.fetchDuration()); // set the max value of the seekbar to be the duration of the song
r = new Runnable()
{
#Override
public void run()
{
int tempInt = myService.fetchProgress();
Log.d("progress ticking", tempInt + " " + musicProgBar.getProgress());
musicProgBar.setProgress(tempInt); // sets the current progress of the seekbar to be the progress of the song
long minutes = TimeUnit.MILLISECONDS.toMinutes(tempInt);
long seconds = TimeUnit.MILLISECONDS.toSeconds(tempInt);
if (seconds >= 60)
seconds = seconds - 60;
String tempString = minutes + ":" + seconds;
progressText.setText(tempString);
progBarHandler.postDelayed(this, progBarDelay);
if (musicProgBar.getProgress() == myService.fetchDuration())
progBarHandler.removeCallbacks(this);
}
};
progBarHandler.post(r);
}});
}
private ServiceConnection serviceConnection = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName name, IBinder service)
{
myService = (MP3Service.MyBinder) service;
}
#Override
public void onServiceDisconnected(ComponentName name)
{
myService = null;
}
};
public void playButClicked(View view)
{
myService.playMusic();
}
public void pauseButClicked(View view)
{
myService.pauseMusic();
}
public void stopButClicked(View view)
{
myService.stopMusic();
}
#Override
protected void onDestroy()
{
unbindService(serviceConnection);
progBarHandler.removeCallbacks(r);
super.onDestroy();
}
}
What is strange is that in onDestroy(), I do use removeCallbacks to end the handler, and that works nicely. I know that it comes to the point where it calls for removeCallbacks in the Runnable r, confirmed through debugging and logging. Also tried implementing a method that is specifically for removing the handler and calling that, no luck. Also tried using return.
The part I'm struggling with is
r = new Runnable()
{
#Override
public void run()
{
int tempInt = myService.fetchProgress();
Log.d("progress ticking", tempInt + " " + musicProgBar.getProgress());
musicProgBar.setProgress(tempInt); // sets the current progress of the seekbar to be the progress of the song
long minutes = TimeUnit.MILLISECONDS.toMinutes(tempInt);
long seconds = TimeUnit.MILLISECONDS.toSeconds(tempInt);
if (seconds >= 60)
seconds = seconds % 60;
String tempString = minutes + ":" + seconds;
progressText.setText(tempString);
progBarHandler.postDelayed(this, progBarDelay);
if (musicProgBar.getProgress() == myService.fetchDuration())
progBarHandler.removeCallbacks(this);
}
};
progBarHandler.post(r);
}});
The service which handles the music player and handles playing the music
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
public class MP3Service extends Service
{
public static MP3Service instance = null; // implementing singleton by instantiating a reference to the instance
private final String SERVICE_ID = "100"; // id for the service
public static boolean isRunning = false; //
NotificationManager notificationManager = null;
int notificationID = 001;
private final IBinder binder = new MyBinder();
MP3Player mainPlayer;
#Nullable
#Override
public IBinder onBind(Intent intent)
{
return binder;
}
#Override
public void onRebind(Intent intent)
{
// TODO: implement Rebind for screen orientation change
}
#Override
public void onCreate()
{
instance = this;
isRunning = true;
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mainPlayer = new MP3Player();
super.onCreate();
Handler handler = new Handler();
}
#Override
public void onDestroy()
{
isRunning = false;
instance = null;
notificationManager.cancel(notificationID);
}
public void createNotification()
{
CharSequence name = "MP3 Notification";
String description = "Displays a notification when a song is playing";
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(SERVICE_ID, name, importance);
channel.setDescription(description);
notificationManager.createNotificationChannel(channel);
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
// when the user clicks the notification, this will bring them to the activity
PendingIntent navToMainActivity = PendingIntent.getActivity(this, 0, intent, 0);
// sets up the info and details for the notification
final NotificationCompat.Builder mNotification = new NotificationCompat.Builder(this, SERVICE_ID)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("MP3 Player")
.setContentText("Playing music")
.setContentIntent(navToMainActivity)
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
startForeground(notificationID, mNotification.build());
}
public void removeNotification()
{
stopForeground(false);
notificationManager.cancelAll();
}
public class MyBinder extends Binder
{
public void loadMusic(String filePath)
{
mainPlayer.load(filePath);
}
public void playMusic()
{
mainPlayer.play();
createNotification();
}
public void pauseMusic()
{
mainPlayer.pause();
removeNotification();
}
public void stopMusic()
{
mainPlayer.stop();
removeNotification();
}
public MP3Player.MP3PlayerState fetchPlayerState()
{
return mainPlayer.getState();
}
public int fetchDuration()
{
return mainPlayer.getDuration();
}
public int fetchProgress()
{
return mainPlayer.getProgress();
}
}
}
Thanks in advance for any help, and I am happy to provide with more information if required
EDIT: changed the progBarHandler.postDelay() that is outside the runnable to a simple progBarHandler.post()
I think the only reason you failed to stop the handler is constantly invoking progBarHandler.postDelayed(this, progBarDelay);,you need to check why it's still running.
Managed to get it working by introducing a different kind of if statement to the runnable, like so
Runnable r = new Runnable()
{
#Override
public void run()
{
if (musicProgBar.getProgress() == myService.fetchProgress() && myService.fetchProgress() != 0)
Log.d("audio", "over");
else {
int tempInt = myService.fetchProgress();
long minutes = TimeUnit.MILLISECONDS.toMinutes(tempInt);
long seconds = TimeUnit.MILLISECONDS.toSeconds(tempInt);
if (seconds >= 60)
seconds = seconds % 60;
String tempString = minutes + ":" + seconds;
progressText.setText(tempString);
musicProgBar.setProgress(tempInt); // sets the current progress of the seekbar to be the progress of the song
progBarHandler.postDelayed(this, progBarDelay);
}
}
};
It seemed for some reason, using .fetchDuration() and .fetchProgress() will never equalize each other, so changing the if statement worked.
So I have an application which has to monitor and range after beacons and than calculates the position of the user. After calculating this , the value is passed to the Wayfindigoverlayactivity.class where the value should be putt on the map with the blue dot.
I don know how to assign the value to the blue dot but before that my application is working on an endless loop and is opening the activity on ranging about 100x .
RangingActivity:
package com.indooratlas.android.sdk.examples.wayfinding;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
import android.widget.EditText;
import android.content.Context;
import com.google.android.gms.maps.model.LatLng;
import com.indooratlas.android.sdk.IALocationRequest;
import com.indooratlas.android.sdk.examples.R;
import org.altbeacon.beacon.Beacon;
import org.altbeacon.beacon.BeaconConsumer;
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.RangeNotifier;
import org.altbeacon.beacon.Region;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.BlockingQueue;
public class RangingActivity extends Activity implements BeaconConsumer,Runnable{
protected static final String TAG = "RangingActivity";
public LatLng center;
private final BlockingQueue queue;
private BeaconManager beaconManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ranging);
beaconManager = BeaconManager.getInstanceForApplication(this);
beaconManager.bind(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
beaconManager.unbind(this);
}
#Override
protected void onPause() {
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
}
#Override
public void onBeaconServiceConnect() {
RangeNotifier rangeNotifier = new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
int beacon_number = beacons.size();
Beacon[] beacon_array = beacons.toArray(new Beacon[beacons.size()]);
Beacon device1 = null, device2 = null, device3 = null;
Constants constants = new Constants();
float txPow1 = 0;
double RSSI1Unfiltered = 0;
double RSSI2Unfiltered = 0;
float txPow2 = 0;
double RSSI3Unfiltered = 0;
float txPow3 = 0;
if (beacon_number == 4) {
if (beacon_array[0].getIdentifier(0).toString() == constants.DEVICE1_UUID) {
device1 = beacon_array[0];
} else if (beacon_array[1].getIdentifier(0).toString() == constants.DEVICE1_UUID) {
device1 = beacon_array[1];
} else {
device1 = beacon_array[2];
}
if (beacon_array[0].getIdentifier(0).toString() == constants.DEVICE2_UUID) {
device2 = beacon_array[0];
} else if (beacon_array[1].getIdentifier(0).toString() == constants.DEVICE2_UUID) {
device2 = beacon_array[1];
} else {
device2 = beacon_array[2];
}
if (beacon_array[0].getIdentifier(0).toString() == constants.DEVICE3_UUID) {
device3 = beacon_array[0];
} else if (beacon_array[1].getIdentifier(0).toString() == constants.DEVICE3_UUID) {
device3 = beacon_array[1];
} else {
device3 = beacon_array[2];
}
RSSI1Unfiltered = device1.getRssi();
RSSI2Unfiltered = device2.getRssi();
RSSI3Unfiltered = device3.getRssi();
txPow1 = device1.getTxPower();
txPow2 = device2.getTxPower();
txPow3 = device3.getTxPower();
} else if (beacon_number > 0) {
Log.d(TAG, "didRangeBeaconsInRegion called with beacon count: " + beacons.size());
for (int i = 0; i < beacon_number; i++) {
Beacon nextBeacon = beacon_array[i];
Log.d(TAG, "The next beacon " + nextBeacon.getIdentifier(0) + " is about " + nextBeacon.getDistance() + " meters away." + "RSSI is: " + nextBeacon.getRssi());
logToDisplay("The next beacon" + nextBeacon.getIdentifier(0) + " is about " + nextBeacon.getDistance() + " meters away." + "RSSI is: " + nextBeacon.getRssi());
}
}
Log.d(TAG, "FLOAT!!!!!!!!" + txPow1);
LocationFinder locationFinder = new LocationFinder();
//pass location
center = locationFinder.findLocation(RSSI1Unfiltered, txPow1, RSSI2Unfiltered, txPow2, RSSI3Unfiltered, txPow3);
Log.d(TAG, "Current coordinates: asta e asta e !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! " + center.toString());
Bundle args = new Bundle();
args.putParcelable("b", center);
Intent intent00 = new Intent(RangingActivity.this, WayfindingOverlayActivity.class);
intent00.putExtras(args);
startActivity(intent00);
}
private void logToDisplay(final String s) {
runOnUiThread(new Runnable() {
#Override
public void run() {
EditText editText = RangingActivity.this.findViewById(R.id.textView3);
editText.append(s+"\n");
}
});
}
};
try {
beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
beaconManager.addRangeNotifier(rangeNotifier);
} catch (RemoteException e) {
}
}
/* Blockinqueue try---not working
RangingActivity(BlockingQueue q)
{
queue = q;
}
public void run() {
LatLng res;
try
{
res = center;
queue.put(res);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
*/
}
Everything works fine here , until I open the next class where my map is the WayfindingOverlayActivity
package com.indooratlas.android.sdk.examples.wayfinding;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import com.google.android.material.snackbar.Snackbar;
import androidx.fragment.app.FragmentActivity;
import android.util.Log;
import android.view.View;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.GroundOverlay;
import com.google.android.gms.maps.model.GroundOverlayOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.indooratlas.android.sdk.IALocation;
import com.indooratlas.android.sdk.IALocationListener;
import com.indooratlas.android.sdk.IALocationManager;
import com.indooratlas.android.sdk.IALocationRequest;
import com.indooratlas.android.sdk.IAOrientationListener;
import com.indooratlas.android.sdk.IAOrientationRequest;
import com.indooratlas.android.sdk.IAPOI;
import com.indooratlas.android.sdk.IARegion;
import com.indooratlas.android.sdk.IARoute;
import com.indooratlas.android.sdk.IAWayfindingListener;
import com.indooratlas.android.sdk.IAWayfindingRequest;
import com.indooratlas.android.sdk.examples.R;
import com.indooratlas.android.sdk.examples.SdkExample;
import com.indooratlas.android.sdk.resources.IAFloorPlan;
import com.indooratlas.android.sdk.resources.IALatLng;
import com.indooratlas.android.sdk.resources.IALocationListenerSupport;
import com.indooratlas.android.sdk.resources.IAVenue;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.RequestCreator;
import com.squareup.picasso.Target;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
#SdkExample(description = R.string.example_wayfinding_description)
public class WayfindingOverlayActivity extends FragmentActivity
implements GoogleMap.OnMapClickListener, OnMapReadyCallback ,Runnable{
private final BlockingQueue queue;
private static final String TAG = "IndoorAtlasExample";
/* used to decide when bitmap should be downscaled */
private static final int MAX_DIMENSION = 2048;
//kalman filter
private static final double KALMAN_R = 0.125d;
private static final double KALMAN_Q = 0.5d;
private GoogleMap mMap; // Might be null if Google Play services APK is not available.
private Circle mCircle;
private IARegion mOverlayFloorPlan = null;
private GroundOverlay mGroundOverlay = null;
private IALocationManager mIALocationManager;
private Target mLoadTarget;
private boolean mCameraPositionNeedsUpdating = true; // update on first location
private Marker mDestinationMarker;
private Marker mHeadingMarker;
private IAVenue mVenue;
private List<Marker> mPoIMarkers = new ArrayList<>();
private List<Polyline> mPolylines = new ArrayList<>();
private IARoute mCurrentRoute;
private IAWayfindingRequest mWayfindingDestination;
private IAWayfindingListener mWayfindingListener = new IAWayfindingListener() {
#Override
public void onWayfindingUpdate(IARoute route) {
mCurrentRoute = route;
if (hasArrivedToDestination(route)) {
// stop wayfinding
showInfo("You're there!");
mCurrentRoute = null;
mWayfindingDestination = null;
mIALocationManager.removeWayfindingUpdates();
}
updateRouteVisualization();
}
};
private IAOrientationListener mOrientationListener = new IAOrientationListener() {
#Override
public void onHeadingChanged(long timestamp, double heading) {
updateHeading(heading);
}
#Override
public void onOrientationChange(long timestamp, double[] quaternion) {
// we do not need full device orientation in this example, just the heading
}
};
private int mFloor;
// circle
private void showLocationCircle(LatLng center, double accuracyRadius) {
if (mCircle == null) {
// location can received before map is initialized, ignoring those updates
if (mMap != null) {
mCircle = mMap.addCircle(new CircleOptions()
.center(center)
.radius(accuracyRadius)
.fillColor(0x201681FB)
.strokeColor(0x500A78DD)
.zIndex(1.0f)
.visible(true)
.strokeWidth(5.0f));
mHeadingMarker = mMap.addMarker(new MarkerOptions()
.position(center)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.map_blue_dot))
.anchor(0.5f, 0.5f)
.flat(true));
}
} else {
// move existing markers position to received location
mCircle.setCenter(center);
mHeadingMarker.setPosition(center);
mCircle.setRadius(accuracyRadius);
}
}
private void updateHeading(double heading) {
if (mHeadingMarker != null) {
mHeadingMarker.setRotation((float) heading);
}
}
private IALocationListener mListener = new IALocationListenerSupport() {
public void onLocationChanged(IALocation location) {
Log.d(TAG, "NEW" + location.getLatitude() + " " + location.getLongitude());
if (mMap == null) {
return;
}
final LatLng center = new LatLng(location.getLatitude(),location.getLongitude());
final int newFloor = location.getFloorLevel();
if (mFloor != newFloor) {
updateRouteVisualization();
}
mFloor = newFloor;
showLocationCircle(center, location.getAccuracy());
if (mCameraPositionNeedsUpdating) {
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(center, 15.5f));
mCameraPositionNeedsUpdating = false;
}
}
};
/**
* Listener that changes overlay if needed
*/
private IARegion.Listener mRegionListener = new IARegion.Listener() {
#Override
public void onEnterRegion(final IARegion region) {
if (region.getType() == IARegion.TYPE_FLOOR_PLAN) {
Log.d(TAG, "enter floor plan " + region.getId());
mCameraPositionNeedsUpdating = true; // entering new fp, need to move camera
if (mGroundOverlay != null) {
mGroundOverlay.remove();
mGroundOverlay = null;
}
mOverlayFloorPlan = region; // overlay will be this (unless error in loading)
fetchFloorPlanBitmap(region.getFloorPlan());
//setupPoIs(mVenue.getPOIs(), region.getFloorPlan().getFloorLevel());
} else if (region.getType() == IARegion.TYPE_VENUE) {
mVenue = region.getVenue();
}
}
#Override
public void onExitRegion(IARegion region) {
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
// prevent the screen going to sleep while app is on foreground
findViewById(android.R.id.content).setKeepScreenOn(true);
// instantiate IALocationManager
mIALocationManager = IALocationManager.create(this);
// Try to obtain the map from the SupportMapFragment.
((SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map))
.getMapAsync(this);
Intent myIntent = new Intent(this, RangingActivity.class);
this.startActivity(myIntent);
Intent intent00 = getIntent();
LatLng center = intent00.getParcelableExtra("b");
Log.d(TAG,"Location!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + center);
}
#Override
protected void onDestroy() {
super.onDestroy();
// remember to clean up after ourselves
mIALocationManager.destroy();
}
/*Some blockingqueue---does not work
public class BlockinQueueExample
{
public void main(String[] args) throws Exception
{
BlockingQueue q = new ArrayBlockingQueue(1000);
RangingActivity producer = new RangingActivity(q);
WayfindingOverlayActivity consumer = new WayfindingOverlayActivity(q);
new Thread(producer).start();
new Thread(consumer).start();
}
}
WayfindingOverlayActivity(BlockingQueue q)
{
this.queue = q;
}
public void run() {
try{
queue.take();
Log.d(TAG,"BIANCABICA"+queue.take());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
*/
#Override
protected void onResume() {
super.onResume();
// start receiving location updates & monitor region changes
mIALocationManager.requestLocationUpdates(IALocationRequest.create(), mListener);
mIALocationManager.registerRegionListener(mRegionListener);
mIALocationManager.registerOrientationListener(
// update if heading changes by 1 degrees or more
new IAOrientationRequest(1, 0),
mOrientationListener);
if (mWayfindingDestination != null) {
mIALocationManager.requestWayfindingUpdates(mWayfindingDestination, mWayfindingListener);
}
}
EDIT , LAUNCHER ACTIVITY
package com.indooratlas.android.sdk.examples;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.indooratlas.android.sdk.examples.imageview.ImageViewActivity;
import com.indooratlas.android.sdk.examples.wayfinding.MonitoringActivity;
import com.indooratlas.android.sdk.examples.wayfinding.RangingActivity;
import com.indooratlas.android.sdk.examples.wayfinding.WayfindingOverlayActivity;
import org.altbeacon.beacon.Beacon;
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.Region;
import org.altbeacon.beacon.powersave.BackgroundPowerSaver;
import org.altbeacon.beacon.startup.BootstrapNotifier;
import org.altbeacon.beacon.startup.RegionBootstrap;
public class Bianca extends Activity implements BootstrapNotifier {
private static final String TAG = "RANGE";
private RegionBootstrap regionBootstrap;
private Button button;
private BackgroundPowerSaver backgroundPowerSaver;
private boolean haveDetectedBeaconsSinceBoot = false;
private MonitoringActivity monitoringActivity = null;
private String cumulativeLog = "";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_one);
BeaconManager beaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
//--------------------------------meniu -------------------------------
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openAct();
}
});
Button button2 = findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openAct2();
}
});
Button button3 = findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openAct3();
}
});
Button button4 = findViewById(R.id.button4);
button4.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openAct4();
}
});
//-----------------------------meniu----------------------------------
Notification.Builder builder = new Notification.Builder(this);
Intent intent = new Intent(this,WayfindingOverlayActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pendingIntent);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
NotificationChannel channel = new NotificationChannel("My Notification Channel ID",
"My Notification Name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("My Notification Channel Description");
NotificationManager notificationManager = (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
builder.setChannelId(channel.getId());
}
beaconManager.enableForegroundServiceScanning(builder.build(), 456);
Log.d(TAG, "setting up background monitoring for beacons and power saving");
// wake up the app when a beacon is seen
Region region = new Region("backgroundRegion",
null, null, null);
regionBootstrap = new RegionBootstrap((BootstrapNotifier) this, region);
backgroundPowerSaver = new BackgroundPowerSaver(this);
}
public void openAct()
{
Intent intent = new Intent(this, WayfindingOverlayActivity.class);
startActivity(intent);
}
public void openAct2()
{
Intent intent2 = new Intent(this, RangingActivity.class);
startActivity(intent2);
}
public void openAct3()
{
Intent intent4 = new Intent(this, ImageViewActivity.class);
startActivity(intent4);
}
public void openAct4()
{
Intent intent5 = new Intent(this,RegionsActivity.class);
startActivity(intent5);
}
public void disableMonitoring() {
if (regionBootstrap != null) {
regionBootstrap.disable();
regionBootstrap = null;
}
}
public void enableMonitoring() {
Region region = new Region("backgroundRegion",
null, null, null);
regionBootstrap = new RegionBootstrap((BootstrapNotifier) this, region);
}
public void didEnterRegion(Region arg0) {
// In this example, this class sends a notification to the user whenever a Beacon
// matching a Region (defined above) are first seen.
Log.d(TAG, "did enter region.");
if (!haveDetectedBeaconsSinceBoot) {
Log.d(TAG, "auto launching MainActivity");
// The very first time since boot that we detect an beacon, we launch the
// MainActivity
Intent intent = new Intent(this, WayfindingOverlayActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Important: make sure to add android:launchMode="singleInstance" in the manifest
// to keep multiple copies of this activity from getting created if the user has
// already manually launched the app.
this.startActivity(intent);
haveDetectedBeaconsSinceBoot = true;
} else {
if (monitoringActivity != null) {
// If the Monitoring Activity is visible, we log info about the beacons we have
// seen on its display
Log.d(TAG, "I see a beacon again");
} else {
// If we have already seen beacons before, but the monitoring activity is not in
// the foreground, we send a notification to the user on subsequent detections.
Log.d(TAG, "Sending notification.");
}
}
}
public void didExitRegion(Region arg0) {
Log.d(TAG,"I no longer see a beacon.");
}
#Override
public void didDetermineStateForRegion(int i, Region region) {
}
}
The second class is not fully posted , only where I make changes.
The intent in the second class is in the OnCreate part
The location is calculated in the logcat , the only problem is that the application is working in a loop
Please help me , I am stuck. Thanks
I guess, you only need to open RangingActivity from onCreate() of WayFindingOverlayActivity if center is null. This means, we need to open RangingActivity to get the value for center. Doing null check for the center will also ensure that the application doesn't go in loop and proceed when we have the value for center. The code in the onCreate() of WayFindingOverlayActivity may look like this :
// edited here
Bundle extras = getIntent().getExtras();
if(extras == null){
Intent myIntent = new Intent(this, RangingActivity.class);
this.startActivity(myIntent);
} else{
LatLng center = extras.getParcelable("b");
Log.d("Location!!", center);
// call showLocationCircle() to show the blue dot
showLocationCircle(center, yourAccuracyRadius);
}
In the code below, I maintain the "isFound" boolean variable to determine whether a thread should run or not. However, when I set the "isFound" to true. My threads will run exactly one more time. In other words, they will not stop properly which also causes the UI to not update the Textview properly. I guess this is due to some synchronized problems but I am not sure how to deal with it and I have tried using a synchronized block but it still does not work.
import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private ProgressBar progressBar;
private TextView message;
private TextView magicNumDisplay;
private boolean isFound = false;
private NotificationManager notificationManager;
private Notification notifyDetail;
private final Handler handler = new Handler(){
public void handleMessage(Message msg) {
if (isMagic(msg.what)) {
isFound = true;
progressBar.setVisibility(View.INVISIBLE);
magicNumDisplay.setText("The magic number is " + msg.what);
handler.removeMessages(0);
message.setText("A magic number s found");
Intent intent = new Intent("MAGIC_NUMBER");
intent.putExtra("THREAD_NAME", msg.obj.toString());
intent.putExtra("MAGIC_NUMBER", String.valueOf(msg.what));
sendBroadcast(intent);
} else {
message.setText("Finding a magic number...");
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
message = (TextView) findViewById(R.id.message);
progressBar = (ProgressBar) findViewById(R.id.progressbar);
magicNumDisplay = (TextView) findViewById(R.id.magic_num_display);
progressBar.setVisibility(View.VISIBLE);
IntentFilter filter = new IntentFilter("MAGIC_NUMBER");
BroadcastReceiver receiver = new MagicNumberReceiver();
registerReceiver(receiver, filter);
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
public void onStart(){
super.onStart();
Thread firstThread = new Thread(backgroundTask, "First");
Thread secondThread = new Thread(backgroundTask, "Second");
firstThread.start();
secondThread.start();
}
public boolean isMagic(int num) {
return (num % 7 == 0 && isLastDigitEqualTwo(num));
}
public boolean isLastDigitEqualTwo(int num) {
String numString = String.valueOf(num);
return Integer.parseInt(numString.substring(numString.length() - 1))==2;
}
Runnable backgroundTask = new Runnable() {
#Override
public void run() {
try {
while (!isFound) {
Thread.sleep(1000);
int number = (int) (Math.random() * 9999);
Message msg = handler.obtainMessage(number, Thread.currentThread().getName());
handler.sendMessage(msg);
Log.e(Thread.currentThread().getName(), "The number is :" + number);
}
} catch(InterruptedException e){
Log.e("Exception", e.getMessage());
}
}
};
public class MagicNumberReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context localContext, Intent callerIntent) {
String threadName = callerIntent.getStringExtra("THREAD_NAME");
String magicNum = callerIntent.getStringExtra("MAGIC_NUMBER");
Log.e("Magic", "Thread name:"+threadName+" Magic Number:"+magicNum);
int MAGIC_NUMBER_NOTIFICATION_ID=200;
notifyDetail = new Notification.Builder(getApplicationContext())
.setContentTitle("Magic Number")
.setContentText("Thread Name: " + threadName + ". Magic Number: " + magicNum+".")
.setSmallIcon(R.drawable.droid)
.setVibrate(new long[] {1000, 1000, 1000, 1000})
.setLights(Integer.MAX_VALUE, 500, 500)
.build();
notificationManager.notify(MAGIC_NUMBER_NOTIFICATION_ID,notifyDetail);
}
}
}
I am trying to make an app in which there is a lock / password screen before you open selected apps to protect your apps.
There are some on the market such as:
https://play.google.com/store/apps/details?id=com.domobile.applock&hl=en
Here is an open source one on GitHub. https://github.com/twinone/AppLocker
I realized that even if the above apps are killed, or ram the is cleared, or the phone is restarted etc. The lock screen stills shows up for the selected application.
Essentially I want to achieve the same thing that they did, but currently I am not achieving this currently with the service class I have written. I can lock the app, but when the app is killed I cannot. I have spent a great deal of time learning pending intents, alarm managers, broadcast receivers, and even studying example source code
https://github.com/twinone/AppLocker/blob/master/src/com/twinone/locker/lock/AppLockService.java
but I am not successful in my implementation.
Here is my service class:
package com.ibc.android.demo.appslist.app;
import android.app.ActivityManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.IBinder;
import android.util.Log;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
public class HeartBeat extends Service {
private static final String TAG = HeartBeat.class.getSimpleName();
public Timer TIMER;
private static Set<AccessGranted> mAccessGrantedList = new HashSet<AccessGranted>();
private Set<String> mLockedApps = new HashSet<String>();
private long lastModified = 0;
private BroadcastReceiver mScreenStateReceiver;
private BroadcastReceiver mAccessGrantedReceiver;
private File mLockedAppsFile;
ArrayList<String> packagezList;
SharedPreferences sharedPrefs;
Map<String, ?> allEntries;
SharedPreferences sharedPrefsapp;
String prefix;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startService(new Intent(this, HeartBeat.class));
// Log.i("LocalService", "Received start id " + startId + ": " +
// intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
if (TIMER == null) {
TIMER = new Timer(true);
TIMER.scheduleAtFixedRate(new LockAppsTimerTask(), 1000, 250);
mScreenStateReceiver = new BroadcastReceiver() {
private boolean screenOff;
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
screenOff = true;
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
screenOff = false;
}
if (screenOff) {
//Log.i(TAG, "Cancel Timer");
TIMER.cancel();
} else {
// Log.i(TAG, "Restart Timer");
TIMER = new Timer(true);
TIMER.scheduleAtFixedRate(new LockAppsTimerTask(), 1000, 250);
}
}
};
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(mScreenStateReceiver, filter);
mAccessGrantedReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
String packageName = intent.getStringExtra("packageName");
if (action.equals(Constants.ACTION_GRANT_ACCESS) && packageName != null) {
AccessGranted ag = new AccessGranted(packageName);
mAccessGrantedList.remove(ag);
mAccessGrantedList.add(ag);
}
}
};
IntentFilter filter2 = new IntentFilter(Constants.ACTION_GRANT_ACCESS);
registerReceiver(mAccessGrantedReceiver, filter2);
}
// this.stopSelf();
//startforeground goes here
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
startService(new Intent(this, HeartBeat.class));
}
private class LockAppsTimerTask extends TimerTask {
#Override
public void run() {
sharedPrefs = getApplicationContext().getSharedPreferences(getApplicationContext().getPackageName(), Context.MODE_PRIVATE);
sharedPrefsapp = getApplicationContext().getSharedPreferences("appdb", Context.MODE_PRIVATE);
allEntries= null;
allEntries = sharedPrefsapp.getAll();
//prefix = "m";
packagezList= null;
packagezList = new ArrayList<String>();
for (Map.Entry<String, ?> entry : allEntries.entrySet()) {
//Log.e("right key: ", entry.getKey() + "right value: " + entry.getValue().toString() );
packagezList.add(entry.getKey());
}
/* for (Map.Entry<String, ?> entry : allEntries.entrySet())
{
//Check if the package name starts with the prefix.
if (entry.getKey().startsWith(prefix)) {
//Add JUST the package name (trim off the prefix).
packagezList.add(entry.getKey().substring(prefix.length()));
packagezList.add(entry.getKey());
}
}*/
for(Object object: packagezList){
Log.e("YO!", (String) object);
}
ActivityManager activityManager = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
try {
//List<RecentTaskInfo> recentTasks = activityManager.getRecentTasks(1, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> RunningTask = mActivityManager
.getRunningTasks(1);
ActivityManager.RunningTaskInfo ar = RunningTask.get(0);
String activityOnTop = ar.topActivity.getPackageName();
// Log.e("activity on Top", "" + activityOnTop);
// Log.e(" My package name", "" + getApplicationContext().getPackageName());
//for (Object data : newArrayList) {
for(Object object: packagezList){
// Provide the packagename(s) of apps here, you want to show password activity
if ((activityOnTop.contains((CharSequence) object)) &&
(!activityOnTop.contains(getApplicationContext().getPackageName()
))) { // you have to make this check even better
Intent i = new Intent(getApplicationContext(), LockScreenActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
i.putExtra( "", "");
startActivity(i);
}
}
} catch (Exception e) {
// Log.e("Foreground App", e.getMessage(), e);
}
}
}
}
Here is my LockScreen activity class (the screen that is displayed when the selected app is open) :
package com.ibc.android.demo.appslist.app;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import com.spicycurryman.getdisciplined10.app.R;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class LockScreenActivity extends Activity {
private static final String TAG = LockScreenActivity.class.getSimpleName();
Map<String, ?> allEntries;
SharedPreferences sharedPrefsapp;
ArrayList<String> packagezList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_setup);
}
#Override
public void onBackPressed() {
// Grab a list of all running processes and their PIDs.
ActivityManager am = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> pids = am.getRunningAppProcesses();
// Now loop through the list of PIDs and find Instagram's PID.
sharedPrefsapp = getApplicationContext().getSharedPreferences("appdb", Context.MODE_PRIVATE);
allEntries= null;
allEntries = sharedPrefsapp.getAll();
//prefix = "m";
packagezList= null;
packagezList = new ArrayList<String>();
for (Map.Entry<String, ?> entry : allEntries.entrySet()) {
//Log.e("right key: ", entry.getKey() + "right value: " + entry.getValue().toString() );
packagezList.add(entry.getKey());
}
// Killing any process for blocked applications when the back button is pressed while the lock screen is displayed
for(Object object: packagezList){
am.killBackgroundProcesses((String) object);
Log.d("Killed Background Process!: ", (String) object);
}
// Now that we've got the PID, kill the Instagram process.
// Now that we've got the PID, kill the Instagram process.
ActivityManager am1 = (ActivityManager) getApplicationContext().getSystemService(ACTIVITY_SERVICE);
// Display confirmation here, finish() activity.
Intent startMain = new Intent(Intent.ACTION_MAIN);
startMain.addCategory(Intent.CATEGORY_HOME);
startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startMain);
startService(new Intent(this, HeartBeat.class));
/* Intent iHeartBeatService = new Intent(this, HeartBeat.class);
PendingIntent piHeartBeatService = PendingIntent.getService(this, 0, iHeartBeatService, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(piHeartBeatService);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 300000, piHeartBeatService);*/
finish();
super.onBackPressed();
}
}
Right now I am able to lock the apps, but when the app is killed or phone is restarted I cannot. The app lock apps on the Google Play Store can successfully still lock apps with a pin when all apps are force stopped or when the phone is restarted, ram cleared, etc.
How can I achieve this like those have?
I think u should try this ...
package com.ankit.vkapplock;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class StartupServiceReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)){
context.startService(new Intent(context, HeartBeat.class ));
}
Dont forget to add permisson for boot_completion action.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>