I am making a VOIP call app. When a call comes in, it should open the app, even if the app is closed or never opened. We also need the call notifications to come in from a server.
I know apps like WhatsApp and Facebook Messenger do this, but how?
We are using a design similar to follows: Build a Calling App
We have tried using Firebase Cloud Messaging as recommended by Android documentation, and while there is moderate success, it does not work when the app is closed.
We are considering using a Sync Adapter or WorkManager next, but it takes quite a bit of time to prototype, and I'd prefer to ask if anyone has any success or if there are existing plugins for this.
As I'm aware, there are also going to be restrictions on Android 10. It says to use time-sensitive notifications, but these will still need to be triggered from a server somehow.
In your calling activity add this code. It will turn your screen on.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
this.setTurnScreenOn(true);
} else {
final Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
}
and in your activity manifest, define intent-filter as CALL
<activity
android:name=".activities.CallActivity"
android:label="#string/app_name"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustPan|stateHidden">
<intent-filter>
<action android:name="android.intent.action.CALL" />
</intent-filter>
</activity>
When you receive notification from Firebase in onMessageReceived(RemoteMessage remoteMessage), open your activity
openActivity(CallActivity.class);
public void openActivity(Class<?> tClass) {
Intent i = new Intent(this, tClass);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
}
Related
With Crosswalk I had a very convenient javascript-to-app interface so I could call a java function from javascript and share data from my webapp to my android app.
How can I achieve this with Custom Tabs (or Trusted Web Activity) ?
There seems to be no way at all. There should be, especially when my app and my game/webapp are from the same author.
For example, I do not trust LocalStorage, especially now with Custom Tabs, it may get cleaned, or the user may uninstall the browser and install another one, so the saved data will be lost and the user will be angry at the app for the loss of the saved data, not even understanding that the data were in the browser, not in the app. So I used to have my webapps call the app to save datas.
Another example, when the Custom Tab uses Firefox instead of Chrome, then speech synthesis won't be available. I can detect it easily in my webapp. But I want my webapp to call the app and send it the words to pronounce. That is what I was doing with Crosswalk since it didn't support speech neither.
I understand that webviews are more appropriate for my use than Custom Tabs, but when the webview can't be used on a device (especially Android <5) then my app doesn't have a lot of other options than opening a Custom Tab instead (or Trusted Web Activity if available). I can't use Crosswalk anymore, it is discontinued and still full of serious bugs. And other solutions such as GeckoView or Alibaba Gcanvas are not ready.
edit:
In this article about Trusted Web Activity https://developers.google.com/web/updates/2017/10/using-twa I read
Nevertheless, you can coordinate with the web content by passing data
to and from the page in URLs (e.g. through query parameters, custom
HTTP headers, and intent URIs.)
edit:
I've been reading many pages, Intents and deep-linking are still obscure to me though, but here is what I tried.
I added an intent filter for a custom action :
<receiver android:name=".OutgoingReceiver" android:enabled="true">
<intent-filter>
<action android:name="custom_tabs_js_interface" />
</intent-filter>
</receiver>
I created a class for that receiver :
public class OutgoingReceiver extends BroadcastReceiver {
public static final String CUSTOM_INTENT = "custom_tabs_js_interface";
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received" , Toast.LENGTH_SHORT).show();
}
and I call it in javascript with
location.href="intent:#Intent;action=custom_tabs_js_interface;end";
I don't even pass data for now, I just try to call it.
but nothing happens...
Yes broadcast receiver doesn't work for some reason, probably security. But you can use an Activity in place of Broadcast Receiver to do this as below.
Use custom Android Intent Uri with custom host, query parameters, package, scheme and intent action. Invoke this intent uri from your javascript/html code. Example
"intent://myhost?key=value#Intent;scheme=myscheme;package=my.app.package;action=someaction;end"
Also declare an activity in the manifest file with intent filter to handle the specific host, scheme & action. Example
<activity android:name="my.app.package.ReceiverActivity">
<intent-filter>
<action android:name="someaction" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="myhost"
android:scheme="myscheme" />
</intent-filter>
</activity>
Now in the activity, handle the intent and extract the data from query parameters. Do whatever you want with the data and probably finish the activity in case you want to go back to the same screen. Example
public class ReceiverActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String value = getIntent().getData().getQueryParameter("key");
if (value != null) {
// value is basically your data
}
// in case you want to go back to same screen
finish();
}
}
And that is it. You have data for your disposal at an Android Activity. This ReceiverActivity could (preferrably) belong to same TWA app. Now from this Receiver Activity, you can easily send/share the data to any other apps. Hope this helps.
You can use a hybrid solution:
Use custom tabs as you want for game.
Your server can call your app when is needed using socket programming or push notifications to get the data you need to save in your app.
Also if sending data to your app is unsuccessful your game can warns user in browser at game.
Support for older android versions is encouraged as far as reasonably possible. Platform versions distribution (https://developer.android.com/about/dashboards/) at the moment this post was written states that you would lose around 12.7% of devices if you drop support for version 4.
Consider raising your minimum version to Lollipop where WebView was moved to an APK and use it. You will gain time and implementation simplicity since you'll be able to call #JavascriptInterface annotated methods while keeping your users engaged inside your app (https://developer.android.com/guide/webapps/webview_.
Here's the use case: I want to create a statistics tool that lists all incoming push messages on a given phone, their message text, when they arrived and their source. clarification: I need 100% access to the data as I'm creating my own presentation tool for private use. It's not to be a consumer app.
I'm thinking I can achieve this by creating an app that somehow listens to all incoming intents of type com.google.android.c2dm.intent.RECEIVE and then analyzing the contents of those. Now I believe that is not possible with the security and intent model of stock Android. But is there anyway around this either with a stock rooted phone, or do I even have to go further and fork and modify AOSP?
Other options I can think of is to monitor the visual notification center itself, if that has an API (which I doubt).
Any suggestions are welcome. I am not intimidated by difficult tasks. Yet my knowledge is limited to Java Android coding - I know little of AOSP or the NDK, but I'd be willing to learn if this is the path I must take.
(Sorry if this is a very broad question, I'll accept the answer that gives me the easiest path to a solution)
So it turns out it wasn't all that difficult. You need an app with minSdkVersion=18 and then you can implement a NotificationListenerService which listens for created or deleted notifications, or you can poll for the currently visible.
Caveat: You need explicit persmission from the user to read notifications other than your own.
Basically what you do is 1. Extend the NotificationListenerInterface and override the onNotificationPosted like this:
#Override
public void onNotificationPosted(StatusBarNotification notif) {
Bundle extras = notif.getNotification().extras;
String packageName = notif.getPackageName();
if (extras != null) {
String title = "" + extras.getCharSequence(Notification.EXTRA_TITLE);
String description = "" + extras.getCharSequence(Notification.EXTRA_TEXT);
// Do whatever you want to do with the data
}
}
In addition you need to register the service in the manifest:
<application>
<!-- (...) -->
<service android:name=".YourListenerClassName"
android:label="#string/app_name" android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
</application>
I'm trying to implement Facebook's Deep Linking feature on my app and encountered the following scenario:
I have an activity called MainActivity which is declared like so:
<activity
android:name="com.mypackage.android.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
This activity + my package name are also declared in my app's settings on facebook developer website.
Once a link gets clicked on Facebook's app, I'm supposed to handle this event via the onCreate method of my activity.
The following code handle the event:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri target = getIntent().getData();
if (target != null){
// got here via Facebook deep link
// once I'm done parsing the URI and deciding
// which part of my app I should point the client to
// I fire an intent for a new activity and
// call finish() the current activity (MainActivity)
}else{
// activity was created in a normal fashion
}
}
All goes according to plan except for the following scenario:
User launched my app
MainActivity created
SecondaryActivity created
MainActivity finished
App goes to background via the device home button
Deep link gets clicked on Facebook's app
In this case my app goes to foreground again, but MainActivity's onCreate / onNewIntent
don't get called, instead SecondaryActivity's onResume() gets called and restored to it's
last state.
Note: I've tested this issue on a Samsung Nexus with Android 4.2.1 and got to this result, though when tested on Galaxy S1 with Android 2.3.5 it worked as I initially expected.
Any help would be greatly appreciated,
Thank you.
Facebook is starting your app from their own app by explicitly start your "MainActivity" (the one your provided them in the developer page).
by that - Android's default behavior is: if the application already runs, then calling again to startActivity() won't start new task from scratch, but only restore to foreground the already running task.
but the good news are that you can change this default behavior by adding to your MainActivity the android:launchMode="singleTask". it definition is:
the system creates a new task and instantiates the activity at the root of the new task. However, if an instance of the activity already exists in a separate task, the system routes the intent to the existing instance through a call to its onNewIntent() method, rather than creating a new instance. Only one instance of the activity can exist at a time.
from this point you could always respond to the starting intent, and from that point you can always navigate back to the task that already was in background(if exists) by restarting activity with both flags Intent.FLAG_ACTIVITY_SINGLE_TOP && Intent.FLAG_ACTIVITY_CLEAR_TOP combination
See http://developer.android.com/guide/topics/manifest/activity-element.html
You can play with:
android:clearTaskOnLaunch
android:noHistory
android:launchMode
You need to have more information in your intent filter:
<intent-filter>
<action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<category android:name="android.intent.category.BROWSABLE"></category>
<data android:host="www.yoursite.com" android:scheme="http"></data>
</intent-filter>
This will capture links going to your site (make sure to change the URL), and direct them to whatever Activity you define this intent filter under.
I need my android app to be in background mode after a phone restart/power on.
Currently I am using the following code, so that my app successfully gets launched after a phone restart/power on.
AndroidManifest.xml:
<receiver android:enabled="true" android:name="my_package.BootUpReceiver" android:permission="android.permission.RECEIVE_BOOT_COMPLETED" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
BootUpReceiver.java:
public class BootUpReceiver extends BroadcastReceiver
{
private static SharedPreferences aSharedSettings;
#Override
public void onReceive(Context context, Intent intent)
{
aSharedSettings = context.getSharedPreferences("MyPreferences", Context.MODE_PRIVATE);
boolean isUserLoggedIn = aSharedSettings.getBoolean(Key.AUTHENTICATED, false);
if(isUserLoggedIn)
{
Intent aServiceIntent = new Intent(context, MyHomeView.class);
aServiceIntent.addCategory(Intent.CATEGORY_HOME);
aServiceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(aServiceIntent);
}
}
}
As I said above, my app successfully gets launched after a phone restart/power on.
However, after the phone restart/power on, my app was in foreground mode. But I need my app to be in background mode.
Can anyone please say, how to make an app to be in background mode after a phone restart or power on.
I even tried by changing the intent category to
<category android:name="android.intent.category.HOME" />
But no use in it. Can anyone please help me?
Thanks.
I need my app to be just running in background after the phone restart, so that users can select from the minimized app
I think your approach is wrong. All you are trying to do now is to add icon of your app to recent apps list. Your app won't run in background and I think you don't really want it. Am I right?
Recent apps list managed by android and IMHO forcing your app to be in recent apps list is not a very good idea. User will start you app when he wants from launcher or icon on his desktop.
If your broadcast receiver is working fine and app is starting successfully then you can use the below code in your MyHomeView activity's onCreate method to go to the home screen.
Trick is to click HOME button programmatically when app starts.
Intent startMain = new Intent(Intent.ACTION_MAIN);
startMain.addCategory(Intent.CATEGORY_HOME);
startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startMain);
you can pass some variable from the BroadcastReceiver to differentiate a normal request and BroadcastReceiver's request to make the above code conditional.
But if you want to execute it always in background then it would be better to use Service.
It is recommended to change your code to the service to run it in background.
The suggestion which Leonidos replied is correct.
However, Just a workaround for this:
In my BootUpReceiver, I had a seperate boolean flag for this! (Its a bad way. but just a workaround)
SharedPreferences.Editor aPrefEditor = aSharedSettings.edit();
aPrefEditor.putBoolean(Key.IS_DEVICE_RESTARTED, true);
aPrefEditor.commit();
In Oncreate method of MyHomeView:
boolean isDeviceRestarted = aSharedSettings.getBoolean(Key.IS_DEVICE_RESTARTED, false);
if(isDeviceRestarted)
{
SharedPreferences.Editor aPrefEditor = aSharedSettings.edit();
aPrefEditor.putBoolean(MamaBearKey.IS_DEVICE_RESTARTED, false);
aPrefEditor.commit();
moveTaskToBack(true);
}
Thanks
I am having a strange problem in an Android application that I am building, the application is basically a Homescreen replacement app which will be put as a default homescreen in a device. To do some initialization work I have extended Android Application class and in the onCreate() method I am basically registering some observer and starting a service, here's the code:
public class MyApplication extends Application implements ExternalStorageListener {
private ExternalStorageObserver externalStorageObserver;
public void onCreate() {
Log.i("MyApplication", "Starting application");
super.onCreate();
externalStorageObserver = new ExternalStorageObserver(this);
if(externalStorageObserver.isExternalStorageAvailable()) {
// this builds a list of files present in the SD card
// which is being used through the application to do
// some work
buildData();
File externalFileDir = getApplicationContext().getExternalFilesDir(null);
if(externalFileDir != null && externalFileDir.isDirectory()) {
// do something...
}
}
//Register listener to observe external storage state
registerExternalStorageObserver();
Log.i("SyncService", "Starting sync service...");
ComponentName cmp = startService(new Intent(getApplicationContext(), SyncService.class));
Log.i("SyncService", cmp.toString());
}
private void registerExternalStorageObserver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
filter.addAction(Intent.ACTION_MEDIA_REMOVED);
registerReceiver(externalStorageObserver, filter);
}
private void buildData() {
// builds a list
}
}
Content of Manifest file:
<application android:persistent="true" android:icon="#drawable/icon"
android:label="#string/app_name" android:name="com.webgyani.android.MyApplication"
android:debuggable="true">
<activity android:name=".HomeTabActivity" android:launchMode="singleInstance"
android:stateNotNeeded="true" android:theme="#style/LightTabsTheme"
android:screenOrientation="landscape" android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
This works fine when I install the app either using Eclipse or manually installing the apk onto the device. Once the app is installed things work fine, I mean the above onCreate() method gets called and service is also started normally, but if I reboot the device this time the onCreate() method does not get called(none of the log statements appear and also the services are not started). After some debugging I noticed that this only happens if I set my app as the default launcher/homescreen app and thereafter reboots the device, because once you set the app as the default launcher, Android should automatically launch your app as the homescreen after reboot. In my case the app is launched but that code is not executed.
I tried to use debugger but that didn't work because when I reboot the device the debugger gets disconnected and by the time USB debugging gets enabled my app is already started.
I even double checked the Logcat but didn't see any error. I thought of having a BOOT_COMPLETED intent to initialize that part, but that will require some code refactoring which I am not willing to do at this point of time unless there is a solution.
So I am curious to know that whether this is a standard behavior, or is there a known bug which causes this, because my assumption is the onCreate method of the Application will always get called whenever the app is started. I have tried whatever I could since morning, but nothing worked, couldn't pinpoint the issue, if any of you could shed some light into this then that would be highly appreciated.
Thanks
Well, finally I figured out the problem, and it was in my code itself. I initially suspected that the MyApplication's onCreate() method was not getting called, I had that assumption because I was not able to see any logs. To know whether the method is getting called or not, instead of using Log.i() I was also appending some additional log messages in an ArrayList and printing them later, this revealed that the methods were indeed getting called and even the Service was instantiating properly but the data or filelist is not being populated because the SDCard was not ready by that time. I am also pretty sure that the logs were not available on Logcat due to the fact that the USB debugger becomes ready after my app is started(as it's a homescreen app).
The actual problem becomes obvious when you see that my overridden MyApplication class implements a listener called ExternalStorageListener which basically extends BroadcastReceiver, I have created that class to receive SDCard related Intents for example ACTION_MEDIA_MOUNTED, ACTION_MEDIA_REMOVED to rebuild the data(file list). In my case the ExternalStorageListener class was not receiving the Intents because I forgot to add this filter.addDataScheme("file") in the registerExternalStorageObserver method above in the code sample.
I do agree that my question was based on a false assumption and the code example I posted it's kind of hard to figure out the actual issue. I am not sure what to do with the question whether to mark this as answer or leave it as it is.