On StackOverflow, I noticed a number of people having trouble catching INSTALL_REFERRER over the years, and a number of bug reports on the Android forum (all closed due to "wrong forum" -- but, I'm not seeing any public issue tracker). I'm wondering if anyone has a way to get INSTALL_REFERRER to work in the current 2014 version of Google Play.
Edit: I did find an issue tracker and created an issue:
https://code.google.com/p/play-games-platform/issues/detail?id=202
Edit 2: (replace com.myapp with the app's name)
Here's what I did for a test of the response. This is the same as on Google's Analytics help docs. This works.
adb shell am broadcast -a com.android.vending.INSTALL_REFERRER -n com.myapp/com.flyingsoftgames.googleplayquery.QueryReceiver --es "referrer" "utm_source=testSource&utm_medium=testMedium&utm_term=testTerm&utm_content=testContent&utm_campaign=testCampaign"``
Here's what I did for a test of the response from Google's Play Store. This is also the same as Google's Analytics help docs, from the link generation utility. (Yes, the keys are different, but that's per the docs, and not what I'm testing -- I just want ANY response, and the docs indicate that referrer should work...) I installed from this rank and ran, listening for QueryReceiver with logcat (adb logcat -s QueryReceiver). This doesn't work.
https://play.google.com/store/apps/details?id=com.myapp&referrer=utm_source%3Dgoogle%26utm_medium%3Dcpc%26utm_term%3Dpodcast%252Bapps%26utm_content%3DdisplayAd1%26utm_campaign%3Dpodcast%252Bgeneralkeywords
Here is my (Cordova plugin) code, which works perfectly when triggering a manual broadcast:
AndroidManifest.xml:
<receiver android:exported="true" android:name="com.flyingsoftgames.googleplayquery.QueryReceiver">
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>
GooglePlayQuery.java:
package com.flyingsoftgames.googleplayquery;
import com.flyingsoftgames.googleplayquery.QueryReceiver;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.CordovaPlugin;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.Context;
import android.app.Activity;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import org.json.JSONArray;
import org.json.JSONException;
public class GooglePlayQuery extends CordovaPlugin {
public static CallbackContext queryCallback = null;
public static CordovaInterface cordova = null;
#Override public void initialize (CordovaInterface initCordova, CordovaWebView webView) {
// Create a static cordova reference so that QueryReceiver can access it.
cordova = initCordova;
// Enable the broadcast receiver in case it isn't enabled.
Activity activity = cordova.getActivity ();
ComponentName receiver = new ComponentName (activity, QueryReceiver.class);
PackageManager pm = activity.getPackageManager ();
pm.setComponentEnabledSetting (receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
super.initialize (cordova, webView);
}
public boolean execute (String action, JSONArray inputs, CallbackContext callbackContext) throws JSONException {
if ("getURI".equals(action)) {this.queryCallback = callbackContext;}
return true;
}
}
QueryReceiver.java:
package com.flyingsoftgames.googleplayquery;
import com.flyingsoftgames.googleplayquery.GooglePlayQuery;
import org.apache.cordova.PluginResult;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.app.Activity;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.util.Log;
public class QueryReceiver extends BroadcastReceiver {
#Override public void onReceive (Context context, Intent intent) {
if (GooglePlayQuery.queryCallback != null) {
Log.d ("QueryReceiver", intent.toURI());
GooglePlayQuery.queryCallback.sendPluginResult (new PluginResult (PluginResult.Status.OK, intent.toURI()));
}
// Now disable the broadcast receiver since we don't need it anymore.
Activity activity = GooglePlayQuery.cordova.getActivity ();
ComponentName receiver = new ComponentName (activity, QueryReceiver.class);
PackageManager pm = activity.getPackageManager ();
pm.setComponentEnabledSetting (receiver, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
}
}
I believe I finally found the reason that it does not work. There is a silent error where GooglePlayQuery.cordova is null. QueryReceiver.onReceive runs before GooglePlayQuery.initialize, but only in production mode. Insane.
I still need a few hours to test it (as my game is published) to verify, but here's my new code. It's also available at https://github.com/agamemnus/cordova-plugin-google-play-query-receiver.
AndroidManifest.xml:
<receiver android:exported="true" android:name="com.flyingsoftgames.googleplayquery.QueryReceiver">
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>
GooglePlayQuery.java:
package com.flyingsoftgames.googleplayquery;
import com.flyingsoftgames.googleplayquery.QueryReceiver;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;
import android.content.Intent;
import android.app.Activity;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import org.json.JSONArray;
import org.json.JSONException;
public class GooglePlayQuery extends CordovaPlugin {
public static CallbackContext queryCallback = null;
public static CordovaInterface cordova = null;
public static String referrer_uri = "";
public static Intent QueryReceiverCachedIntent = null;
#Override public void initialize (CordovaInterface initCordova, CordovaWebView webView) {
// Create a static cordova reference so that QueryReceiver can access it.
cordova = initCordova;
// Enable the broadcast receiver in case it isn't enabled.
Activity activity = cordova.getActivity ();
ComponentName receiver = new ComponentName (activity, QueryReceiver.class);
PackageManager pm = activity.getPackageManager ();
pm.setComponentEnabledSetting (receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
// If the QueryReceiver's onReceive already ran, run the cached data.
if (QueryReceiver.cachedIntent != null) {QueryReceiver.runCachedOnReceive (QueryReceiver.cachedIntent);}
super.initialize (cordova, webView);
}
public boolean execute (String action, JSONArray inputs, CallbackContext callbackContext) throws JSONException {
if ("getURI".equals(action)) {
if (referrer_uri != "") {
callbackContext.sendPluginResult (new PluginResult (PluginResult.Status.OK, referrer_uri));
referrer_uri = "";
return true;
}
this.queryCallback = callbackContext;
}
return true;
}
}
QueryReceiver.java:
package com.flyingsoftgames.googleplayquery;
import com.flyingsoftgames.googleplayquery.GooglePlayQuery;
import org.apache.cordova.PluginResult;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.app.Activity;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.util.Log;
public class QueryReceiver extends BroadcastReceiver {
public static Intent cachedIntent = null;
#Override public void onReceive (Context context, Intent intent) {
// If the onReceive occurred before the GooglePlayQuery initialize function ran: cache the intent. Otherwise, run as intended.
if (GooglePlayQuery.cordova == null) {cachedIntent = intent;} else {runCachedOnReceive (intent);}
}
public static void runCachedOnReceive (Intent intent) {
if (cachedIntent != null) cachedIntent = null;
Log.e ("QueryReceiver", intent.toURI());
if (GooglePlayQuery.queryCallback != null) {
GooglePlayQuery.queryCallback.sendPluginResult (new PluginResult (PluginResult.Status.OK, intent.toURI()));
} else {
GooglePlayQuery.referrer_uri = intent.toURI();
}
// Now disable the broadcast receiver since we don't need it anymore.
Activity activity = GooglePlayQuery.cordova.getActivity ();
ComponentName receiver = new ComponentName (activity, QueryReceiver.class);
PackageManager pm = activity.getPackageManager ();
pm.setComponentEnabledSetting (receiver, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
}
}
Related
I've been playing around with some Android recently and have the following setup:
I have app A, which is a service. This has no activity. Here's its manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sandbox.sampleservice">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.SampleService">
<service
android:name=".DummyService"
android:exported="true"
android:enabled="true">
</service>
</application>
</manifest>
and here's the Java code:
package com.sandbox.sampleservice;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;
public class DummyService extends Service {
private static final String TAG = "DummyService";
static class MessageHandler extends Handler {
#Override
public void handleMessage(Message msg) {
// TODO: sort this later
super.handleMessage(msg);
}
}
Messenger mMessenger = null;
#Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "Binding");
mMessenger = new Messenger(new MessageHandler());
return mMessenger.getBinder();
}
#Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "No longer bound");
return false;
}
}
I then have an app B with an empty activity which I want to bind to the service in app A. This is it's MainActivity:
package com.sandbox.sampleclient;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "SampleClient";
private ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "Bound");
}
#Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "Unbound");
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.sandbox.sampleservice", ".DummyService"));
boolean result = this.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
if (result) {
Toast.makeText(this, "success", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "failed", Toast.LENGTH_SHORT).show();
}
}
}
I'm running on the default emulator that comes with Android Studio - API 30/x86.
I've installed the service using the installDebug Gradle task and can see the app in the Settings. But running app B, the call to bindService() returns false (it shows the failure toast). And I have the following message in logcat:
2021-04-03 20:33:59.743 500-1764/system_process W/ActivityManager: Unable to start service Intent { cmp=com.sandbox.sampleservice/.DummyService } U=0: not found
I've tried a few things - using the app context rather than the activity one when binding, using the fully qualified class name, and more. No change in behaviour, and the error in logcat is always the same.
I'm working on an Ionic app that requires geofence to run in the background. I'm using the cordova-plugin-geofence plugin. I understand that JavaScript does not run in the background and that the plugin developer provided native code to listen to geofence transitions.
I followed the instructions but it doesn't seem to work. Transition does not trigger when the app is closed.
Here is my package name: io.ionic.pla.
I placed this code below in my Android Manifest
<receiver android:name="io.ionic.pla.TransitionReceiver">
<intent-filter>
<action android:name="com.cowbell.cordova.geofence.TRANSITION" />
</intent-filter>
</receiver>
Here is my TransitionReceiver.java
package io.ionic.pla;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.cowbell.cordova.geofence.Gson;
import com.cowbell.cordova.geofence.GeoNotification;
public class TransitionReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String error = intent.getStringExtra("error");
if (error != null) {
//handle error
Log.println(Log.ERROR, "YourAppTAG", error);
} else {
String geofencesJson = intent.getStringExtra("transitionData");
Log.println(Log.INFO, "xxxYourAppTAG", geofencesJson);
GeoNotification[] geoNotifications = Gson.get().fromJson(geofencesJson, GeoNotification[].class);
//handle geoNotifications objects
}
}
}
Please help guys, I really need this to work.
I'm trying to listen to all the notifications that the mobile receive, I'm working with nativescript & angular2,
First solution I've tried : -
after installing the tns-platform-declarations package and referencing it
android.service['notification']['NotificationListenerService'].extend({
onNotificationPosted: (sbn) => {
console.log("got it");
console.log(sbn.getNotification().tickerText);
},
onCreate: () => {
console.log("Created");
}
});
put unfortunately nothing happens on notification posted.
second try:-
I've made a java file and place it in
platforms/android/src/main/java/com/tns/MoNotificationListener.java
package com.tns;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.app.Notification;
import java.util.List;
import java.util.Set;
import java.util.Arrays;
import java.util.ArrayList;
import android.widget.RemoteViews;
import java.lang.reflect.Field;
import android.os.Parcelable;
import android.os.Parcel;
import android.text.TextUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.app.PendingIntent;
public class MoNotificationListener extends NotificationListenerService {
static PendingIntent pickCallIntent ;
static PendingIntent rejectCallIntent ;
static PendingIntent hangCallIntent ;
#Override
public void onCreate() {
Log.d("ReactNative", "Created");
super.onCreate();
}
#Override
public void onListenerConnected() {
Log.d("ReactNative", "Got onListenerConnected");
}
#Override
public void onNotificationPosted(StatusBarNotification sbn) {
Log.d("Native", "Got Notification");
if(sbn.getNotification().tickerText !=null) {
Log.d("Native",sbn.getNotification().tickerText.toString());
Log.d("Native",sbn.getPackageName());
}
}
}
tested with
adb logcat *:S Native:V
and got the logs on notifications posted successfully
add to the android manifest in
app/App_Resources/Android/AndroidManifest.xml
<service
android:name="com.tns.WheemoNotificationListener"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
>
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
then I was trying to extend that class from the typescript to get notified on notification posted.
android.service['notification']['NotificationListenerService'].extend("com.tns.notification.MoNotificationListener", {
onNotificationPosted: (sbn) => {
console.log("got it");
console.log(sbn.getNotification().tickerText);
},
onCreate: () => {
console.log("Created");
}
});
but again nothing happens
After searching we found that NotificationListenerService wasn't available through the default definition file
android.d.ts
so we used this
NativeScript/android-dts-generator
to generate a new one with the latest platform level we needed, and it worked.
thanks all
My app listens to BOOT_COMPLETED to start.
<receiver android:name=".BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</receiver>
But if my app crashes, how could I get it to automatically restart?
BOOT_COMPLETED is not a sticky intent.
To get Answer of your question is very simple. In that case you need to use Thread.setDefaultUncaughtExceptionHandler(). It will always enter in uncaughtException() in case your application crashed.For Check Full Tutorial Here
public class YourApplication extends Application {
private static Context mContext;
public static YourApplication instace;
#Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
instace = this;
}
#Override
public Context getApplicationContext() {
return super.getApplicationContext();
}
public static YourApplication getIntance() {
return instace;
}
}
DefaultExceptionHandler.java
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Environment;
import android.util.Log;
/**
* This custom class is used to handle exception.
*
* #author Chintan Rathod (http://www.chintanrathod.com)
*/
public class DefaultExceptionHandler implements UncaughtExceptionHandler {
private UncaughtExceptionHandler defaultUEH;
Activity activity;
public DefaultExceptionHandler(Activity activity) {
this.activity = activity;
}
#Override
public void uncaughtException(Thread thread, Throwable ex) {
try {
Intent intent = new Intent(activity, RelaunchActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TASK
| Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(
YourApplication.getInstance().getBaseContext(), 0, intent, intent.getFlags());
//Following code will restart your application after 2 seconds
AlarmManager mgr = (AlarmManager) YourApplication.getInstance().getBaseContext()
.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000,
pendingIntent);
//This will finish your activity manually
activity.finish();
//This will stop your application and take out from it.
System.exit(2);
} catch (IOException e) {
e.printStackTrace();
}
}
}
I have made small app for Android mobile.
In one situation I am not getting any solution.
Actually my app has small functionality for calling to customer.
So after call ended I need that event of which last number will dialed or which app is runs.
AndroidManifest:
<receiver android:name=".PhoneStateBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE">
</action></intent-filter>
</receiver>
Add following permission:
<uses-permission android:name="android.permission.READ_PHONE_STATE">
</uses-permission>
PhoneStateBroadcastReceiver.java (refactored the code a bit)
package com.mobisys.android.salesbooster;
import com.mobisys.android.salesbooster.database.HelperDatabase;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
public class PhoneStateBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "PhoneStateBroadcastReceiver";
Context mContext;
String incoming_number;
private int prev_state;
#Override
public void onReceive(Context context, Intent intent) {
TelephonyManager telephony = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); //TelephonyManager object
CustomPhoneStateListener customPhoneListener = new CustomPhoneStateListener();
telephony.listen(customPhoneListener, PhoneStateListener.LISTEN_CALL_STATE); //Register our listener with TelephonyManager
Bundle bundle = intent.getExtras();
String phoneNr = bundle.getString("incoming_number");
Log.v(TAG, "phoneNr: "+phoneNr);
mContext = context;
}
/* Custom PhoneStateListener */
public class CustomPhoneStateListener extends PhoneStateListener {
private static final String TAG = "CustomPhoneStateListener";
#Override
public void onCallStateChanged(int state, String incomingNumber){
if( incomingNumber != null && incomingNumber.length() > 0 )
incoming_number = incomingNumber;
switch(state){
case TelephonyManager.CALL_STATE_RINGING:
Log.d(TAG, "CALL_STATE_RINGING");
prev_state=state;
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Log.d(TAG, "CALL_STATE_OFFHOOK");
prev_state=state;
break;
case TelephonyManager.CALL_STATE_IDLE:
Log.d(TAG, "CALL_STATE_IDLE==>"+incoming_number);
if((prev_state == TelephonyManager.CALL_STATE_OFFHOOK)){
prev_state=state;
//Answered Call which is ended
}
if((prev_state == TelephonyManager.CALL_STATE_RINGING)){
prev_state=state;
//Rejected or Missed call
}
break;
}
}
}
}
Read more here, Source : http://mobisys.in/blog/2011/09/is-your-call-ended-on-android-phone/