If the user drags a widget normally to the home screen, then the configuration activity is started (if set). If the activity returns a result code of RESULT_CANCELED, then the widget creation is cancelled.
This is documented here.
Alternativly, to add a widget from an activity, one can use AppWidgetManager.requestPinAppWidget().
But then the configuration activity is not started, and the program should explicitly start it, as documented here.
This is my code that does it:
object StartWidgetConfigureReceiver : BroadcastReceiver() {
const val ACTION_NEW_WIDGET = "ACTION_NEW_WIDGET"
override fun onReceive(context: Context?, intent: Intent?) {
Intent(context, MyWidgetConfigureActivity::class.java).apply {
action = ACTION_APPWIDGET_CONFIGURE
putExtras(intent)
context?.startActivity(this)
}
}
}
val appWidgetManager = AppWidgetManager.getInstance(context)
val myProvider = ComponentName(applicationContext, MyWidgetProvider::class.java)
val successIntent = PendingIntent.getBroadcast(applicationContext, 0,
Intent(StartWidgetConfigureReceiver.ACTION_NEW_WIDGET),
PendingIntent.FLAG_MUTABLE)
appWidgetManager.requestPinAppWidget(myProvider,null, successIntent)
But how to cancel the widget if needed?
Adding resultCode = Activity.RESULT_CANCELED in onReceive does not seem to do it.
(I would need to wait for the result from the configuration activity, but that is not the question. Unless the way of starting the activity is relevant.)
Related
I have a notification in my app and I want to perform a custom task on clicking of the action in the notification. I have done this to add a action:
timerNotificationBuilder.addAction(0, "STOP", null /*What to add here ?*/ );
But, I want to stop a handler from running on click of this action. I have only seen to open activities using this. But, how to stop a handler or any custom task?
Thanks 😀
#Sambhav.K , You need to pass Pending Intent in action button like below code
notificationBuilder.addAction(mCallAction)
val mCallAction = NotificationCompat.Action.Builder(
R.drawable.ic_reject_call,
"Stop",
mPendingIntent
)
val mCallIntent = Intent(context, CustomActionReceiver::class.java)
val mPendingIntent = PendingIntent.getBroadcast(
context, 999,
mCallIntent, PendingIntent.FLAG_UPDATE_CURRENT
)
and create new CustomActionReceiver class and do your stuff what you want like below
class CustomActionReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// Do your Stuff
}}
I'm trying to open the MainActivity when the user clicks a button in my notification, while the app is only running in the background with a service. When the button is clicked, these lines are triggered in the Service class:
Intent openApp = new Intent(this, MainActivity.class);
openApp.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openApp);
I've checked it, and the lines are triggered, so there's no problem in reacting to the button's click, the Activity won't open though.
Any suggestions? Why isn't this working for me and how can I make it work?
Edit
I was asked for some more code, so in my onStartCommand() inside my Service, if it starts with a stop-action within its intent, I call the killService() method, which kills the Service, starts the MainActivity and do some other stuff:
if (action != null && action.equals(ACTION_STOP_SERVICE)) {
killService();
}
To set the Notifications button, I use this code:
Intent stopActionIntent = new Intent(this, TimerService.class);
stopActionIntent.setAction(ACTION_STOP_SERVICE);
PendingIntent stopActionPendingIntent = PendingIntent.getService(this, 1, stopActionIntent, PendingIntent.FLAG_IMMUTABLE);
timerNotificationBuilder.addAction(R.drawable.stop, "Stop", stopActionPendingIntent);
And as I said, the button already reacts to the user clicking on it, so that's not the problem.
You can try to receive the click in a BroadcastReceiver and then open activity from there.
Try this to add a action button o your notification:
timerNotificationBuilder.addAction(createNotificationActionButton("STOP");
Where the createNotificationActionButton method is this:
public NotificationCompat.Action createNotificationActionButton(String text){
Intent intent = new Intent(this, StopwatchNotificationActionReceiver.class);
#SuppressLint("InlinedApi") PendingIntent pendingIntent = PendingIntent.getBroadcast(this, new Random().nextInt(100), intent, PendingIntent.FLAG_IMMUTABLE);
return new NotificationCompat.Action(0, text, pendingIntent);
}
Create a class named StopwatchNotificationActionReceiver and make it extent a BroadcastReceiver`. This is the code for that class:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class StopwatchNotificationActionReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
PrefUtil.setIsRunningInBackground(context, false);
PrefUtil.setTimerSecondsPassed(context, 0);
PrefUtil.setWasTimerRunning(context, false);
context.stopService(MainActivity.serviceIntent);
Intent activityIntent = new Intent(context, MainActivity.class);
activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActvity(activityIntent);
}
}
Also you need to register that receiver in your manifest like this:
<receiver android:name="StopwatchNotificationActionReceiver"/>
Where the MainActivity.serviceIntent is a public static variable which looks like this:
public static Intent serviceIntent;
And this intent is only used to start the service like this:
//In onCreate
serviceIntent = new Intent(this, TimerService.class);
//In onPause
PrefUtil.setTimerSecondsPassed(this,seconds);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent);
}
Or you can try the simple method:
if (action != null && action.equals(ACTION_STOP_SERVICE)) {
Context context = this;
Intent activityIntent = new Intent(context, MainActivity.class);
activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActvity(activityIntent);
killService();
}
Edit
Another solution is here. Again. You need to refer to my repo as I have made changes to the files in order to complete your task. In the service class, refer to this method. There, I start the activity if the action is reset(r). Or else, it opens the broadcast receiver. Then, in the activity, I receive that extra in the onResume() method. If the reset button is not clicked, it opens the Receiver class.
And as always, you can view the result of the app from here.
I hope that code will do your work.
I found it! See this answer.
This answer suggests enabling ScreeanOverlay settings because as of Android 10 and later you can no longer open an activity from the background just by calling the lines I've used.
To make it work, you'd have to add this permission through your Manifest.xml:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
And then the user would have to enable the Display over other apps setting.
I searched for an option to get the user to this setting more easily and found this answer.
This answer gives a code that redirects the user to the Display over other apps setting
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 0);
}
and then I guide the user with the notification's content (text) on how to enable the setting.
Once the setting is enabled, The lines I've used before work.\
So, problem solved?
Not Completely Solved
this whole configuration described above works, but only if the app is not killed.
If the app was killed and I try the method listed above, the app joins the recent apps list, but won't open and show up.
A solution that solves this issue as well will be accepted as an answer instead of this one.
When app is open then its working fine but when app is in background then BroadcastReceiver is called but activity intent not working
class FakeCallReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
LogUtils.d("===onReceive 1")
setCustomSetting(context)
LogUtils.d("===onReceive 2")
val incomingCallActivity = Intent(context.applicationContext, FakeCallActivity::class.java)
incomingCallActivity.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(incomingCallActivity)
LogUtils.d("===onReceive 3")
}
fun setCustomSetting(context: Context){
val wakeLock: PowerManager.WakeLock =
(context.getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp::MyWakelockTag").apply {
acquire()
}
}
val mKeyguard = (context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager).apply {
newKeyguardLock("MyApp::Keyguard").disableKeyguard()
}
}
}
All logs are print there is no exception is occur but still FakeCallActivity is not called
MinSdkVersion = 24
TargetSdkVersion = 29
1. Have i made any mistake ?
2. Is there other way to open activity when app is in background ?
Starting Android 10 (API level 29), there are restrictions on opening an activity from the background. Check this - https://developer.android.com/guide/components/activities/background-starts.
You should try to avoid this or you can check these exceptions - https://developer.android.com/guide/components/activities/background-starts#exceptions. If any of the exceptions works for you then you can try doing that.
I've the below code that launch the Dialer intent, and make a call with the given number.
I need to minimize/hide this activity, so I'm making a delay of 15 seconds that call the Press home intent but t is not working.
How can I hide the dialer while it still active (i.e. still making the required call)!
if (isChecked) {
val dial = "tel:12345678"
val phoneIntent = Intent(Intent.ACTION_CALL, Uri.parse(dial))
val startMain = Intent(Intent.ACTION_MAIN).apply {
addCategory(Intent.CATEGORY_HOME)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
val r = java.lang.Runnable {
context.startActivity(startMain)
}
context.startActivity(phoneIntent)
val h = Handler()
h.postDelayed(r, 15000) // will be delayed for 15 seconds
}
I managed it using the below code, but not clear wha is the different between this and the above one in the question:
Thread().run {
context.startActivity(phoneIntent)
Thread.sleep(2000)
context.startActivity(startMain)
}
Notification is giving old values.
I read the stackoverflow link but still not working for me:
Notification passes old Intent Extras
I have a Activity A.
When I am on the activity B and touch the Notification, the Extra parameter is given correctly and shows the Activity A with the correctc values read with getExtras(..);
Then the Activity A is still on the top - showing on the screen:
I click on the second notification with new values of putExtras(newValue) to create a new activity A but with new values.
The problem:
The intent.getExtras()` is reading old values of the first notification clicked instead new values given by the second notification.
I made a lot of combinations of Flags of Pending Intent and the combinations of the link on top, but the aplication is still taking the old values(the values of the first touched notification) for the second Notification. I tried the flag: PendingIntent.FLAG_UPDATE_CURRENT to update the values instead create a new one Activity and some others Flags.
How can I make the second notification give the correct values for the activity A when the Activity A is still shown on the screen?
The snippet of the code creating the notification.
public void notificationCreateGu(String newMessageUserUidOfSender) {
Intent it = new Intent(this,ActivityA.class);
it.putExtra(USER_UID_READER,newMessageUserUidOfSender);
StoreValuesClass.count=StoreValuesClass.count+2;
PendingIntent pi = PendingIntent.getActivity(this, StoreValuesClass.count,it, 0);
Notification notification = new NotificationCompat.Builder(this)
.setTicker(newMessageUserUidOfSender )
.setSmallIcon(android.R.mipmap.sym_def_app_icon)
.setContentTitle("Title Message ")
.setContentText(String.valueOf(newMessageUserUidOfSender))
.setContentIntent(pi)
.setAutoCancel(true)
.build();
int m;
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
m= StoreValuesClass.count=StoreValuesClass.count+2;
notificationManager.notify((m), notification);
}
//StoreValueClass.count is a static values that can be read by the activity to give an unique id for the notification.
the snippet of code reading the values.
userUidReader = getIntent().getExtras().getString(USER_UID_READER)
I tried to reload the values into onResume() but into onResume() the still taking the old values of the first time the getExtras() is read.
i understood that the operational system Android are not creating a new Activity but only giving it to the top.
using the answser of CommonsWare that helped with the override onNewIntetn and the link:
http://www.helloandroid.com/tutorials/communicating-between-running-activities
1 into xmlFile: put th android:launchMode="singleTask"
for the activity will receive the extra parameters with getExtras.
<activity android:name=".ActivityWillReceiveWithGetExtras"
android:launchMode="singleTask"
android:taskAffinity=""
android:excludeFromRecents="true">
</activity>
2.into the activity you that will receive the values with get_extras(...) override a method called onNewIntent:
2.1 Observation: put the line:
setIntent(intent);
to set the identifier of the intent.
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);//must store the new intent unless getIntent() will return the old one
getExtraParameterActual();
}
2.2 get the Extra parameters into a function that will have inside the command getExtras(...
getExtraParameterActual();
Write the function of the top getExtraParameterActual();
private void getExtraParameterActual() {
Intent intent = getIntent();//take back the value set with //setintenT of pass 2.1
user = getIntent().getExtras().getString(USER);//
}
5.
into OnCreate() call the e getExtraParameterActual();
and if necessary reload your views with a method for example reloadMyViews()
into onResume() reload your views again with the same function of the pass 5 reloadMyViews()
7 the notificatio code I used take care with the FLAGS
public void notificationCreateGu(String User) {
Log.d(TAG,nameOfTheService+"BUG createnotification for received CHAT messages useruidOfTheFriendNear="+newMessageUserUidOfSender);
Intent it = new Intent(this,ActivityWillReceiveWithGetExtras.class);
it.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
it.putExtra(USER,user);
StoreValuesClass.count=StoreValuesClass.count+2;
PendingIntent pi = PendingIntent.getActivity(this, StoreValuesClass.count,it, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(this)
.setTicker(newMessageUserUidOfSender )
.setSmallIcon(android.R.mipmap.sym_def_app_icon)
.setContentTitle("Title Message ")
.setContentText(String.valueOf(newMessageUserUidOfSender))
.setContentIntent(pi)
.setAutoCancel(true)
.build();
int m;
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
m= StoreValuesClass.count=StoreValuesClass.count+2;
notificationManager.notify((m), notification);
}
Override onNewIntent() in your activity.
getIntent() returns the Intent that was used to initially create the activity. If an existing activity instance is brought back to the foreground via a startActivity() call, onNewIntent() is called to deliver to you the Intent used for that most recent startActivity() call.