Adding Marshmallow permissions using "Permissions Dispatcher" - java

I'm successfully using Permission Dispatcher to handle Marshmallow permissions.
Is there a way to use it on a static class, where I cannot access to "this" in ${className}PermissionDispatcher.${method}WitchCheck(this) ?
Working example:
#RuntimePermissions
public class ContactsMapFragment extends mFragment implements OnMapReadyCallback {
#Override
public void onMapReady(#NonNull GoogleMap googleMap) {
if (DeviceUtils.isMarshmallow())
ContactsMapFragmentPermissionsDispatcher.setMapLocationWithCheck(this, googleMap);
else
googleMap.setMyLocationEnabled(true);
}
#NeedsPermission(Manifest.permission.ACCESS_FINE_LOCATION)
protected void setMapLocation(#NonNull GoogleMap gmap) {
gmap.setMyLocationEnabled(true);
}
}
But in this other snippet I have a static class, so this doesn't exist:
#RuntimePermissions
public class PhoneUtils
{
#NeedsPermission(Manifest.permission.CALL_PHONE)
public static void makeCall(String szNumber)
{
Intent intent = new Intent(
Intent.ACTION_CALL, // place a phone call directly
Uri.parse("tel:" + szNumber)
);
mActivity activity = Muffin.getInstance().getActivity();
if (activity == null)
return;
if (DeviceUtils.isMarshmallow())
PhoneHelperPermissionDispatcher.startCallWithCheck(this, activity, intent);
else
activity.startActivity(intent);
}
protected void startCall(mActivity activity, Intent intent)
{
activity.startActivity(intent);
}
}

I have not used Permission Dispatcher library but I think you can pass the Activity or Context to methods of static class and can use it.
or
You can use Application class to keep context global.
for example:
public class MyDemoApplication extends Application {
private static Context context;
public void onCreate() {
super.onCreate();
MyDemoApplication .context = getApplicationContext();
}
public static Context getAppContext() {
return MyDemoApplication.context;
}
}
You have to call MyDemoApplication.getAppContext() to get your application context statically from any class file.
Note: If you are extending Application class in your project then don't forget to specify its name in application tag of manifest file.
<?xml version="1.0" encoding="utf-8"?>
<activity android:name=".MainActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
See below links for more details:
https://developer.android.com/reference/android/app/Application.html
http://www.devahead.com/blog/2011/06/extending-the-android-application-class-and-dealing-with-singleton/

Static class can't be annotated because only Activity and Fragment are allowed, as Android Framework just provides requestPermission method on Activity and Fragment

Related

Android 8.0 - Job Intent Service does not run application on bootup

Initially I had Android 7.0 and didn't have any issues using a BroadcastReceiver and service. However with changes to Android 8.0. I needed to switch to a JobIntentService so my application can run on bootup.
I have tried migrating my code to match the JobIntentService but nothing is happening on bootup.
I am unsure whether the reason is because of my service class or my BroadcastReceiver class.
AndroidManifest.xml
<service android:name=".backgroundService"
android:permission="android.permission.BIND_JOB_SERVICE"/>
backgroundService.java
public class backgroundService extends JobIntentService {
public static final int JOB_ID = 0x01;
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, backgroundService.class, JOB_ID, work);
}
#Override
protected void onHandleWork(#NonNull Intent intent) {
Toast.makeText(this, "Application and Service Started", Toast.LENGTH_LONG).show();
Intent dialogIntent = new Intent(this, Home.class);
dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(dialogIntent);
}
}
startOnBoot.java
public class startOnBoot extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
Log.i("In" , "getAction() - Boot");
backgroundService.enqueueWork(context, intent);
}
else
Log.i("No" , "Boot");
}
}
So I am trying to essentially start the Home.class on bootup.
I tried it and it could run normally. You could check three tips below.
1.Check whether you have declared RECEIVE_BOOT_COMPLETED permission or not.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
2.Check whether you have declared the receiver with BOOT_COMPLETED action.
<receiver android:name=".startOnBoot">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
3.Remove Toast.makeText(this, "Application and Service Started", Toast.LENGTH_LONG).show(); in your service or toast it in main thread. Otherwise it gives you the error java.lang.RuntimeException: Can't toast on a thread that has not called Looper.prepare().

Android - Unable to receive local broadcast in my activity from service

I have my main activity that start a service (Location service) and I want that service to broadcast the new location each time a new location is found.
Thanks to the log I know the service is working and I have new locations every seconds or so, but I never get the broadcast.
MainActivity.java
public class MainActivity extends Activity {
private static final String TAG = "mainActivity";
private CMBroadcastReceiver mMessageReceiver = new CMBroadcastReceiver();
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
// Start Service
startService(new Intent(this, LocationService.class));
super.onCreate(savedInstanceState);
}
#Override
public void onResume()
{
LocalBroadcastManager.getInstance(this).registerReceiver(
mMessageReceiver, new IntentFilter(CMBroadcastReceiver.RECEIVE_LOCATION_UPDATE));
super.onResume();
}
#Override
public void onPause()
{
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
super.onPause();
}
}
CMBroadcastReceiver.java
public class CMBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "CMBroadcastReceiver";
public static final String RECEIVE_LOCATION_UPDATE = "LOCATION_UPDATES";
#Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Received broadcast");
String action = intent.getAction();
if (action.equals(RECEIVE_LOCATION_UPDATE))
{
Log.i(TAG, "Received location update from service!");
}
}
}
LocationService.java
/**
* Callback that fires when the location changes.
*/
#Override
public void onLocationChanged(Location location) {
mCurrentLocation = location;
mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
Log.i(TAG, "onLocationChanged " + location);
Intent intent = new Intent(CMBroadcastReceiver.RECEIVE_LOCATION_UPDATE);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
Log.i(TAG, "Broadcast sent");
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cyclemapapp.gpstracker">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:label="#string/title_activity_main"
android:theme="#style/AppTheme.NoActionBar">
android:configChanges="orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".LocationService" android:process=":location_service" />
</application>
I the log I can see that "Broadcast Sent" But I never get the "Broadcast Received"
Any help will would be greatly appreciated.
EDIT:
Edited how the intent was created in the location service as Shaishav suggested.
Still doesn't work.
LocalBroadcastManager does not work across processes. Your Service is running in a separate process.
You can either run your Service in the same process as the Activity - by removing the process attribute from the <service> element - or use some sort of IPC instead - e.g., by sending and receiving the broadcasts on a Context instead of LocalBroadcastManager.
In your LocationService, send local broadcast using:
Intent intent = new Intent(CMBroadcastReceiver.RECEIVE_LOCATION_UPDATE);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
<service android:name=".LocationService" android:process=":location_service" />
Your service is in a separate process from the activity. LocalBroadcastManager is only for use in one process. Either remove android:process from the <service>, or use some IPC mechanism (e.g., system broadcasts, properly secured).

Activity not found while using a service

I am trying to start a Service that implements SensorEventListener. I am getting an error in my logcat claiming:
android.content.ActivityNotFoundException:Unable to find explicit activity class
{com.devicemoved/com.devicemoved.ShakeWakeupService};
have you declared this activity in your AndroidManifest.xml?
My Service is declared in my manifest as shown:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.devicemoved"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.devicemoved.launcherGo"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="com.devicemoved.ShakeWakeupService" />
</application>
The Activity
public class ShakeWakeupService extends Service implements SensorEventListener {
private Context mContext;
SensorManager mSensorEventManager;
Sensor mSensor;
// BroadcastReceiver for handling ACTION_SCREEN_OFF.
public BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Check action just to be on the safe side.
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Log.v("shake mediator screen off", "trying re-registration");
// Unregisters the listener and registers it again.
mSensorEventManager.unregisterListener(ShakeWakeupService.this);
mSensorEventManager.registerListener(ShakeWakeupService.this,
mSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
}
};
#Override
public void onCreate() {
super.onCreate();
Log.v("shake service startup", "registering for shake");
mContext = getApplicationContext();
// Obtain a reference to system-wide sensor event manager.
mSensorEventManager = (SensorManager) mContext
.getSystemService(Context.SENSOR_SERVICE);
// Get the default sensor for accel
mSensor = mSensorEventManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// Register for events.
mSensorEventManager.registerListener(this, mSensor,
SensorManager.SENSOR_DELAY_NORMAL);
// Register our receiver for the ACTION_SCREEN_OFF action. This will
// make our receiver
// code be called whenever the phone enters standby mode.
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
registerReceiver(mReceiver, filter);
}
#Override
public void onDestroy() {
// Unregister our receiver.
unregisterReceiver(mReceiver);
// Unregister from SensorManager.
mSensorEventManager.unregisterListener(this);
}
#Override
public IBinder onBind(Intent intent) {
// We don't need a IBinder interface.
return null;
}
public void onShake() {
// Poke a user activity to cause wake?
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// not used right now
}
// Used to decide if it is a shake
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
return;
Log.v("sensor", "sensor change is verifying");
}
}
The only thing I could possibly think of is that I am suppose to declare a broadcast receiver since I am using it in my class but I am not sure how to do that in this instance because I am not extending Broadcastreceiver.
I am calling this class from an activity with a button.
Any help will be great Thank You
You can already declared the package in the manifest tag, you should only need the .ShakeWakeupService part. Also make sure there is an empty constructor available for the system to construct your service

Singleton object is recreating

I am facing a problem, that I created a class Controller it is singleton but its object is recreating when I access in different activity of same application,
Main_Activity is my launching activity
public class Main_Activity extends Activity{
private Controller simpleController;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
simpleController = Controller.getInstance(this);
}
}
This is my Controller it is singleton, in it I am setting alarm which is of 10sec from now and my MyMainLocalReciever receives that alarm and notify using notification.
public class Controller {
private MediaPlayer mp;
public Context context;
private static Controller instance;
public static Controller getInstance(Context context) {
if (instance == null) {
instance = new Controller(context);
}
return instance;
}
private Controller(Context context) {
Log.d("TAG", "Creating Controller object");
mp = null;
this.context = context;
setAlarm(10);
}
public void setAlarm(int position) {
Intent intent = new Intent(context, MyMainLocalReciever.class);
intent.putExtra("alarm_id", "" + position);
PendingIntent sender = PendingIntent.getBroadcast(context,
position, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Get the AlarmManager service
AlarmManager am = (AlarmManager) context
.getSystemService(Activity.ALARM_SERVICE);
am.cancel(sender);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
+ (position*1000), sender);
}
}
This is my receiver MyMainLocalReciever it notify and I am binding an intent which starts an activity called NotificationDialog
public class MyMainLocalReciever extends BroadcastReceiver {
private NotificationManager notificationManager;
private int alarmId = 0;
#Override
public void onReceive(Context context, Intent intent) {
if (notificationManager == null) {
notificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
}
Bundle bundle = intent.getExtras();
String alarm_Id = bundle.getString("alarm_id");
try {
alarmId = Integer.parseInt(alarm_Id);
} catch (Exception e) {
Log.d("Exception", "exception in converting");
}
Controller myC = Controller.getInstance(context);
if ((myC.getMp() != null)) {
myC.getMp().stop();
myC.setMp(null);
}
if (myC.getMp() == null) {
myC.setMp(MediaPlayer.create(context , R.id.mpFile));
myC.getMp().start();
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setTicker("Its Ticker")
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("Its Title")
.setContentText("Its Context")
.setAutoCancel(true)
.setContentIntent(
PendingIntent.getActivity(context, 0, new Intent(context,
NotificationDialog.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK), 0));
notificationManager.notify("interstitial_tag", alarmId,
builder.getNotification());
}
}
Till now(before NotificationDialog) code is working perfect MediaPlayer object which is in Controller class is working fine too, but when I access my singleton Controller here in NotificationDialog, it is creating new object of Controller, it should not do that, it should retain that Controller object which is singleton.
public class NotificationDialog extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.notification_dialog);
}
public void onViewContent(View v) { //this method is invoked when I click on a button binded in xml file
Controller myC = Controller.getInstance(getApplicationContext());
if (myC.getMp() != null) {
myC.getMp().stop();
myC.setMp(null);
}
finish();
}
}
Kindly help me regarding this, I will appreciate your help.
Regards
EDIT:
Here is my Manifest
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".Main_Activity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="test.SettingsActivity"
android:label="#string/app_name" />
<activity
android:name="test.NotificationDialog"
android:label="#string/app_name" />
<service android:name="test.MyService" >
</service>
<receiver
android:name="test.MyMainLocalReciever"
android:process=":remote" />
</application>
Your process is getting killed by Android when it is idle in the background. Android will kill off your process if there are no active components (Activities, Services, etc.) or when it needs the memory (even if you have active components).
When the user uses your notification, Android creates a new process for you. That is why the Singleton is gone and needs to get recreated.
EDIT:
After you posted your manifest I immediately saw the problem. This is it:
<receiver
android:name="test.MyMainLocalReciever"
android:process=":remote" />
Your process isn't getting killed. Your BroadcastReceiver is running in another separate process. In that process, the singleton hasn't been set up yet.
Remove android:process=":remote" from your <receiver> tag in the manifest.
Please read about the Initialization-on-demand holder idiom. It's very clear and simple article about right Singleton in the Java programming language.
As the Singleton will be a static object used by many Activities, you don't have to pass the Context to the constructor. Passing it to the methods which will need it, is a better option.
public class Controller {
private static volatile Controller instance = null;
private Controller () { }
public static Controller getInstance() {
if (instance == null) {
synchronized (Controller .class)
if (instance == null) {
instance = new Controller();
}
}
return instance;
}
public void setAlarm(Context context, int position) {
// do stuff
}
}

Sending data from service to activity

I am having issue when sending data from Service to Activity through Notification , I click a Notification an Activity get invoked but when i try to add some parameters through bundle i am not able to get the parameters in that called intent , I have gone through the link
How to send parameters from a notification-click to an activity?
But still no luck.
Has the same issue occurred with somebody else ?
Thanks in advance.
You have to modify the Manifest file as well.
Here is the example that works:
These variables and methods are members of Service class:
public static final String MOVEMENT_UPDATE = "com.client.gaitlink.AccelerationService.action.MOVEMENT_UPDATE";
public static final String ACCELERATION_X = "com.client.gaitlink.AccelerationService.ACCELERATION_X";
public static final String ACCELERATION_Y = "com.client.gaitlink.AccelerationService.ACCELERATION_Y";
public static final String ACCELERATION_Z = "com.client.gaitlink.AccelerationService.ACCELERATION_Z";
private void announceAccelerationChanges()//this method sends broadcast messages
{
Intent intent = new Intent(MOVEMENT_UPDATE);
intent.putExtra(ACCELERATION_X, accelerationX);
intent.putExtra(ACCELERATION_Y, accelerationY);
intent.putExtra(ACCELERATION_Z, accelerationZ);
sendBroadcast(intent);
}
And this are the methods from Main activity:
You have to register receiver in the onResume method:
#Override
public void onResume()
{
IntentFilter movementFilter;
movementFilter = new IntentFilter(AccelerationService.MOVEMENT_UPDATE);
accelerationReceiver = new AccelerationServiceReceiver();
registerReceiver(accelerationReceiver, movementFilter);
startAccelerationService();
super.onResume();
}
private void startAccelerationService()
{
startService(new Intent(this, AccelerationService.class));
}
public class AccelerationServiceReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)//this method receives broadcast messages. Be sure to modify AndroidManifest.xml file in order to enable message receiving
{
accelerationX = intent.getDoubleExtra(AccelerationService.ACCELERATION_X, 0);
accelerationY = intent.getDoubleExtra(AccelerationService.ACCELERATION_Y, 0);
accelerationZ = intent.getDoubleExtra(AccelerationService.ACCELERATION_Z, 0);
announceSession();
updateGUI();
}
}
This is the part of AndroidManifest.xml file that has to be set in order to receive broadcast messages:
<activity android:name=".GaitLink"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="com.client.gaitlink.CommunicationService.action.ACTIVITY_STATUS_UPDATE" />
</intent-filter>
</activity>

Categories