I am developing a call recorder application, which is working fine. I want to give the user an option to decide whether to record a call or not. I want to add a button on top of the ongoing call screen, but I am not sure if that is possible. There are some threads which give hint or code to add on top of incoming or outing screen, but I want to add it on top of the ongoing screen.Any help would be appreciated.
//Custom dialog showing after the call has been disconnected, I want to show as soon as the call has been started.
if (extraState.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
Intent intentDialog = new Intent(context, CustomDialog.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(intentDialog);
Intent myIntent = new Intent(context,
RecordService.class);
myIntent.putExtra("commandType",
Constants.STATE_CALL_START);
context.startService(myIntent);
}
I'm using the following intent with a notification and the issue is that if the current activity is the same as the intent nothing happens. How do I open the same activity with the new data?
intent = new Intent(context, PackViewActivity.class);
intent.putExtra("pid", pack_id);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
Here is the details fromt he Manaifest:
<activity
android:name=".PackViewActivity"
android:configChanges="orientation|keyboardHidden|screenLayout|screenSize"
android:launchMode="singleTask"/> //I've also tried singleInstance with no success.
Currently everything works fine unless the current activity is PackViewActivity. In that case nothing happens.
Like #Vivek mentioned, use Intent.FLAG_ACTIVITY_CLEAR_TOP and remove Intent.FLAG_ACTIVITY_CLEAR_TASK. Now, if your activity is already running the new intent will be delivered in onNewIntent(). That is where you should put your intent reading code. Also, get rid of android:launchMode in your activity manifest description since it brings a lot of problems with it.
If the required activity is already in foreground then you need not to push a notification. Alternatively you can register the activity as a listener to the service or activity class which pushes the notification through NotificationManager. Then handle the notification appropriately.
Need to show the Activity (Theme dialog) over the dialer with some information populated in activity like true caller. In unlocked phone, it is working well.
But in locked phone, it didnt work as expected. Did some googling and found few flags needs to be added to make dialog appear in lock screen.
CallActivity.java
LayoutParams layoutParams = new LayoutParams(
LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT, LayoutParams.TYPE_SYSTEM_DIALOG | LayoutParams.TYPE_SYSTEM_ALERT |
LayoutParams.TYPE_SYSTEM_OVERLAY,
LayoutParams.FLAG_NOT_TOUCH_MODAL |
LayoutParams.FLAG_NOT_FOCUSABLE |
LayoutParams.FLAG_TURN_SCREEN_ON |
LayoutParams.FLAG_SHOW_WHEN_LOCKED |
LayoutParams.FLAG_KEEP_SCREEN_ON |
LayoutParams.FLAG_DISMISS_KEYGUARD,
PixelFormat.TRANSLUCENT);
getWindow().setAttributes(layoutParams);
In the above code, i added changes related to make activity appear as dialog and few flags (FLAG_TURN_SCREEN_ON, FLAG_SHOW_WHEN_LOCKED, FLAG_KEEP_SCREEN_ON, FLAG_DISMISS_KEYGUARD) related to make dialog work well in lock screen (but it didnt).
I also starting the activity with (from broadcast receiver)
Intent intent = new Intent(context, MyActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
context.startActivity(intent);
add view on window manager with System Alert permission
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
mParams = new WindowManager.LayoutParams();
mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
mParams.format = PixelFormat.TRANSLUCENT;
mParams.flags = 262184;
mWindowManager.addView(view, mParams);
Note: If the app targets API level 23 or higher, the app user must explicitly grant this permission to the app through a permission management screen. The app requests the user's approval by sending an intent with action ACTION_MANAGE_OVERLAY_PERMISSION. The app can check whether it has this authorization by calling Settings.canDrawOverlays().
I have an app with a splash screen Activity, followed by a main Activity. The splash screen loads stuff (database, etc.) before starting the main Activity. From this main Activity the user can navigate to multiple other child Activities and back. Some of the child Activities are started using startActivityForResult(), others just startActivity().
The Activity hierarchy are as depicted below.
| Child A (startActivityForResult)
| /
|--> Splash --> Main -- Child B (startActivityForResult)
| ^ \
| | Child C (startActivity)
| \
| This Activity is currently skipped if a Notification is started
| while the app is not running or in the background.
I need to achieve the following behavior when clicking a Notification:
The state in the Activity must be maintained, since the user has selected some recipes to create a shopping list. If a new Activity is started, I believe the state will be lost.
If the app is in the Main Activity, bring that to the front and let me know in code that I arrived from a Notification.
If the app is in a child Activity started with startActivityForResult(), I need to add data to an Intent before going back to the Main Activity so that it can catch the result properly.
If the app is in a child Activity started with startActivity() I just need to go back since there is nothing else to do (this currently works).
If the app is not in the background, nor the foreground (i.e. it is not running) I must start the Main Activity and also know that I arrived from a Notification, so that I can set up things that are not set up yet, since the Splash Activity is skipped in this case in my current setup.
I have tried lots of various suggestions here on SO and elsewhere, but I have not been able to successfully get the behavior described above. I have also tried reading the documentation without becoming a lot wiser, just a little. My current situation for the cases above when clicking my Notification is:
I arrive in the Main Activity in onNewIntent(). I do not arrive here if the app is not running (or in the background). This seems to be expected and desired behavior.
I am not able to catch that I am coming from a Notification in any child Activities, thus I am not able to properly call setResult() in those Activities. How should I do this?
This currently works, since the Notification just closes the child Activity, which is ok.
I am able to get the Notification Intent in onCreate() by using getIntent() and Intent.getBooleanExtra() with a boolean set in the Notification. I should thus be able to make it work, but I am not sure that this is the best way. What is the preferred way of doing this?
Current code
Creating Notification:
The Notification is created when an HTTP request inside a Service returns some data.
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setSmallIcon(getNotificationIcon())
.setAutoCancel(true)
.setColor(ContextCompat.getColor(context, R.color.my_brown))
.setContentTitle(getNotificationTitle(newRecipeNames))
.setContentText(getContentText(newRecipeNames))
.setStyle(new NotificationCompat.BigTextStyle().bigText("foo"));
Intent notifyIntent = new Intent(context, MainActivity.class);
notifyIntent.setAction(Intent.ACTION_MAIN);
notifyIntent.addCategory(Intent.CATEGORY_LAUNCHER);
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
/* Add a thing to let MainActivity know that we came from a Notification. */
notifyIntent.putExtra("intent_bool", true);
PendingIntent notifyPendingIntent = PendingIntent.getActivity(context, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(notifyPendingIntent);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(111, builder.build());
MainActivity.java:
#Override
protected void onCreate(Bundle savedInstanceState)
{
Intent intent = getIntent();
if (intent.getBooleanExtra("intent_bool", false))
{
// We arrive here if the app was not running, as described in point 4 above.
}
...
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
switch (requestCode)
{
case CHILD_A:
// Intent data is null here when starting from Notification. We will thus crash and burn if using it. Normally data has values when closing CHILD_A properly.
// This is bullet point 2 above.
break;
case CHILD_B:
// Same as CHILD_A
break;
}
...
}
#Override
protected void onNewIntent(Intent intent)
{
super.onNewIntent(intent);
boolean arrivedFromNotification = intent.getBooleanExtra("intent_bool", false);
// arrivedFromNotification is true, but onNewIntent is only called if the app is already running.
// This is bullet point 1 above.
// Do stuff with Intent.
...
}
Inside a child Activity started with startActivityForResult():
#Override
protected void onNewIntent(Intent intent)
{
// This point is never reached when opening a Notification while in the child Activity.
super.onNewIntent(intent);
}
#Override
public void onBackPressed()
{
// This point is never reached when opening a Notification while in the child Activity.
Intent resultIntent = getResultIntent();
setResult(Activity.RESULT_OK, resultIntent);
// NOTE! super.onBackPressed() *must* be called after setResult().
super.onBackPressed();
this.finish();
}
private Intent getResultIntent()
{
int recipeCount = getRecipeCount();
Recipe recipe = getRecipe();
Intent recipeIntent = new Intent();
recipeIntent.putExtra(INTENT_RECIPE_COUNT, recipeCount);
recipeIntent.putExtra(INTENT_RECIPE, recipe);
return recipeIntent;
}
AndroidManifest.xml:
<application
android:allowBackup="true"
android:icon="#mipmap/my_launcher_icon"
android:label="#string/my_app_name"
android:theme="#style/MyTheme"
android:name="com.mycompany.myapp.MyApplication" >
<activity
android:name="com.mycompany.myapp.activities.SplashActivity"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.mycompany.myapp.activities.MainActivity"
android:label="#string/my_app_name"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustPan" >
</activity>
<activity
android:name="com.mycompany.myapp.activities.ChildActivityA"
android:label="#string/foo"
android:parentActivityName="com.mycompany.myapp.activities.MainActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustPan" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.mycompany.myapp.activities.MainActivity" >
</meta-data>
</activity>
<activity
android:name="com.mycompany.myapp.activities.ChildActivityB"
android:label="#string/foo"
android:parentActivityName="com.mycompany.myapp.activities.MainActivity"
android:screenOrientation="portrait" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.mycompany.myapp.activities.MainActivity" >
</meta-data>
</activity>
...
</manifest>
Such a complicated Question :D
Here is how you should treat this problem :
Use an IntentService in your notification instead of
Intent notifyIntent = new Intent(context, MainActivity.class);
by now, whenever user click on the notification, an intentservice would be called.
in the intent service,Broadcast something.
in OnResume of all your desired activity register the broadcast listener (for the broadcast you create in 2nd phase) and in OnPause unregister it
by now whenever you are in any activity and the user click on notification, you would be informed without any problem and without any recreation of activity
in your Application class define a public Boolean. lets called it APP_IS_RUNNING=false; in your MainActivity, in OnPause make it false and in OnResume make it true;
By doing this you can understand your app is running or not or is in background.
NOTE : if you want to handle more states, like isInBackground,Running,Destroyed,etc... you can use an enum or whatever you like
You want to do different things when the app is running, am i right ? so in the intent service which you declared in 1st phase check the parameter you define in your Application Class. (i mean APP_IS_RUNNING in our example) if it was true use broadcast and otherwise call an intent which open your desired Activity.
You are going on a wrong way buddy.
onActivityResult is not the solution.
Just A simple Answer to this would be to use Broadcast Receiver
Declare an action In your manifest file:
<receiver android:name="com.myapp.receiver.AudioPlayerBroadcastReceiver" >
<intent-filter>
<action android:name="com.myapp.receiver.ACTION_PLAY" />
<!-- add as many actions as you want here -->
</intent-filter>
</receiver>
Create Broadcast receiver's class:
public class AudioPlayerBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equalsIgnoreCase("com.myapp.receiver.ACTION_PLAY")){
Myactivity.doSomething(); //access static method of your activity
// do whatever you want to do for this specific action
//do things when the button is clicked inside notification.
}
}
}
In your setNotification() Method
Notification notification = new Notification.Builder(this).
setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.no_art).build();
RemoteView remoteview = new RemoteViews(getPackageName(), R.layout.my_notification);
notification.contentView = remoteview;
Intent playIntent = new Intent("com.myapp.receiver.ACTION_PLAY");
PendingIntent playSwitch = PendingIntent.getBroadcast(this, 100, playIntent, 0);
remoteview.setOnClickPendingIntent(R.id.play_button_my_notification, playSwitch);
//this handle view click for the specific action for this specific ID used in broadcast receiver
Now when user will click on the button in Notification and broacast receiver will catch that event and perform the action.
Here is what I ended up doing. It is a working solution and every situation of app state, child Activity, etc. is tested. Further comments are highly appreciated.
Creating the Notification
The Notification is still created as in the original question. I tried using an IntentService with a broadcast as suggested by #Smartiz. This works fine while the app is running; the registered child Activities receives the broadcast and we can do what we like from that point on, like taking care of the state. The problem, however, is when the app is not running in the foreground. Then we must use the flag Intent.FLAG_ACTIVITY_NEW_TASK in the Intent to broadcast from the IntentService (Android requires this), thus we will create a new stack and things starts to get messy. This can probably be worked around, but I think it easier to save the state using SharedPreferences or similar things as others pointed out. This is also a more useful way to store persistent state.
Thus the Notification is simply created as before:
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setSmallIcon(getNotificationIcon())
.setAutoCancel(true)
.setColor(ContextCompat.getColor(context, R.color.my_brown))
.setContentTitle(getNotificationTitle(newRecipeNames))
.setContentText(getContentText(newRecipeNames))
.setStyle(new NotificationCompat.BigTextStyle().bigText("foo"));
Intent notifyIntent = new Intent(context, MainActivity.class);
notifyIntent.setAction(Intent.ACTION_MAIN);
notifyIntent.addCategory(Intent.CATEGORY_LAUNCHER);
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
/* Add a thing to let MainActivity know that we came from a Notification.
Here we can add other data we desire as well. */
notifyIntent.putExtra("intent_bool", true);
PendingIntent notifyPendingIntent = PendingIntent.getActivity(context, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(notifyPendingIntent);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(111, builder.build());
Saving state
In the child Activities that need to save state I simply save the I need to SharedPreferences in onPause(). Thus that state can be reused wherever needed at a later point. This is also a highly useful way of storing state in a more general way. I had not though of it since I thought the SharedPreferences were reserved for preferences, but it can be used for anything. I wish I had realized this sooner.
Opening the Notification
Now, when opening a Notification the following things occur, depending on the state of the app and which child Activity is open/paused. Remember that the flags used are Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP.
A. Child Activity
Running in the front: The child Activity is closed, applicable state is saved using SharedPreferences in onPause and can be fetched in onCreate or wherever in the main Activity.
App is in the background: same behavior.
App is in the background, but killed by the OS (tested using adb shell: There is no stack at this point, thus MainActivity is opened. The app is in a dirty state, however, so I revert that intent back to the splash screen with the incoming data and back to the main Activity. The state is again saved in onPause in the child Activity when the user closed it and it can be fetched in the main Activity.
B. Main Activity
Running in the front: The Intent is caught in onNewIntent and everything is golden. Do what we want.
App is in the background: same behavior.
App is in the background, but killed by the OS (tested using adb shell: The app is in a dirty state, so we revert the Intent to the splash screen/loading screen and back to the main Activity.
C. App is not running at all
This is really the same as if Android killed the app in the background to free resources. Just open the main Activity, revert to the splash screen for loading and back to the main Activity.
D. Splash Activity
It is not very likely that a user can be in the splash Activity/loading Activity while a Notification is pressed, but it is possible in theory. If a user does this the StrictMode complains about having 2 main Activities when closing the app, but I am not certain that it is entirely correct. Anyway, this is highly hypothetical, so I am not going to spend much time on it at this point.
I do not think this is a perfect solution since it requires a little bit of coding here and little bit of coding there and reverting Intents back and forth if the app is in a dirty state, but it works. Comments are highly appreciated.
I was wondering if anyone can tell if how to pop a dialog screen up over a native Android screen?
I currently have an application that traps an outgoing call and stops it, I then want to pop up a dialog that would take over from the dialler screen and alert the user that there attempt to call has been blocked and allow them have some new options from the dialog.
I know that some people will say that I should use notifications instead but I'm aware of that and its not the way that it should work, I need to be able to pop up a dialog when the call gets trapped.
This is my dialog code so far
AlertDialog LDialog = new AlertDialog.Builder(context)
.setTitle("Call Blocked")
.setMessage("Call Blocked, reroute call?")
.setPositiveButton("ok", null).create();
LDialog.show();
I presume I have to somehow get the context to be that of the dialler screen?
Can anyone offer any help and assistance or links to tutorials?
Thanks in advance
For my application I used an activity with the Dialog theme.
You can declare the theme in the manifest file :
<activity android:name="PopupActivity"
android:launchMode="singleInstance" android:excludeFromRecents="true"
android:taskAffinity="" android:theme="#android:style/Theme.Dialog" />
use launcheMode="singleInstance" and taskAffinity="" if your popup is detached from your main application. Otherwise user may click the back button and return to the previous activity of your application.
excludeFromRecents="true" to avoid your popup to appear in recent tasks (long press home)
theme="#android:style/Theme.Dialog" to set the Dialog theme.
How to get the equivalent of launchMode = singleTask in code
I have not seen a clear explanation of how to set these flags programmatically, so I will include my results here. tldr: you have to set FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_MULTIPLE_TASK.
If you launch this directly from your app, your dialog will appear on top of your app's last Activity. But if you use a PendingIntent broadcast by AlarmManager to launch your "dialog", you have time to switch to a different app so you can see that your "dialog" will appear over that other app, if the style is set appropriately to show what is behind it.
Obviously one should be responsible about when it is appropriate to display a dialog on top of other apps.
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// you have to set these flags here where you receive the broadcast
// NOT in the code where you created your pendingIntent
Intent scheduledIntent = new Intent(context, AlertAlarmActivity.class);
scheduledIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
scheduledIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
context.startActivity(scheduledIntent);