I have an advertisment in google adsense for my android application and i know that when user clicks on ads, google create the link with info about user for tracking. So i want to get that link and gclid parameter inside it. How can i do that?
I already tried to do it with intent and google play install referrer, but intent returns null, and google play install referrer returns empty string or sometimes FEATURE_NOT_SUPPORTED
This is my intent getting code:
val intent = this.intent
val uri = intent?.data
urlFromIntent = uri.toString()
Manifest:
<receiver android:name="com.google.android.gms.analytics.CampaignTrackingReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>
<service android:name="com.google.android.gms.analytics.CampaignTrackingService"
android:enabled="true"
android:exported="false" />
And my google play install referrer code:
mRefferClient = InstallReferrerClient.newBuilder(this).build()
mRefferClient.startConnection(object : InstallReferrerStateListener {
#SuppressLint("SwitchIntDef")
override fun onInstallReferrerSetupFinished(responseCode: Int) {
when (responseCode) {
InstallReferrerClient.InstallReferrerResponse.OK -> {
try {
if (BuildConfig.DEBUG) Log.d("InstallReferrerState", "OK")
var response = mRefferClient.installReferrer
urlFromReferClient = response.installReferrer
urlFromReferClient += ";" + response.referrerClickTimestampSeconds
urlFromReferClient += ";" + response.installBeginTimestampSeconds
mRefferClient.endConnection()
} catch (e: RemoteException) {
urlFromReferClient = e.toString()
}
}
InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED -> {
urlFromReferClient = "FEATURE_NOT_SUPPORTED"
}
InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE -> {
urlFromReferClient = "SERVICE_UNAVAILABLE"
}
}
}
override fun onInstallReferrerServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
})
Related
I am creating an app that will have permission to read user notifications.
So I have implemented NotificationListenerService as shown below
class NotifListener : NotificationListenerService() {
override fun onNotificationPosted(sbn: StatusBarNotification?) {
var context = this
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
var packageName = sbn?.packageName ?: ""
var notifId=sbn?.id?:0; //doesn't seem to be unique
val notification: Notification = sbn!!.notification
var extras: Bundle? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
extras = notification.extras
for (key in extras!!.keySet()) {
var title: String = extras.get("android.title").toString()
var text: String = extras.get("android.text").toString()
CoroutineScope(Dispatchers.IO).launch {
NotificationDBBuilder.getInstance(context).notificationDao()
.insertNotification(
NotificationEntity(notifId, title, text,packageName,Date().time)
)
}
}
}
extras?.get("android.title")
// Log.d(TAG, "onNotificationPosted: " + extras?.get("android.title")+" "+extras?.get("android.text")+" "+extras?.get("android.infoText"))
}
}
}
and I have added it in manifest
<service android:name=".services.NotifListener"
android:label="Notif"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
Now when I logged the notifications that I received, it seems that the onNotificaitionPosted is being called multiple times for the same notification for some applications or some situations. I want to list only Unique notifications. Which parameter should I Use from the notifications as a Unique Key?
Or is there some way to prevent multiple calls for the same notification?
or is it possible that the listener is getting registered multiple times?
I am trying to develop a small app that can send WhatsApp messages without user interaction,
I know that this is the kind of problem that may a lot of people are trying to solve.
I am using accessibility services to do such things.
This is a sample of my code.
Accessibility Service Class
class WhatsappAccessibilityService : AccessibilityService() {
override fun onAccessibilityEvent(event: AccessibilityEvent) {
if (rootInActiveWindow == null) {
return
}
val rootInActiveWindow = AccessibilityNodeInfoCompat.wrap(rootInActiveWindow)
val messageNodeList = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.whatsapp:id/entry")
if (messageNodeList == null || messageNodeList.isEmpty()) {
return
}
val messageField = messageNodeList[0]
if (messageField.text == null || messageField.text.length == 0 || !messageField.text.toString()
.endsWith("mysuffix")
) {
return
}
val sendMessageNodeInfoList = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.whatsapp:id/send")
if (sendMessageNodeInfoList == null || sendMessageNodeInfoList.isEmpty()) {
return
}
val sendMessageButton = sendMessageNodeInfoList[0]
if (!sendMessageButton.isVisibleToUser) {
return
}
sendMessageButton.performAction(AccessibilityNodeInfo.ACTION_CLICK)
try {
Thread.sleep(500)
performGlobalAction(GLOBAL_ACTION_BACK)
Thread.sleep(500)
} catch (ignored: InterruptedException) {
}
performGlobalAction(GLOBAL_ACTION_BACK)
}
override fun onInterrupt() {
TODO("Not yet implemented")
}
And this is my whatsapp_service.xml file
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowContentChanged"
android:packageNames="com.whatsapp"
android:accessibilityFeedbackType="feedbackSpoken"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"/>
This is my AndroidManifest.xml file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.messagewithwhatsapp">
<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.MessageWithWhatsapp">
<service
android:name=".WhatsappAccessibilityService"
android:exported="true"
android:label="My Accessibility Service" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<meta-data
android:name="android.accessibilityservice"
android:resource="#xml/whatsapp_service"/>
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
</service>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
And finally, this is how I implemented the send login.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnSend.setOnClickListener {
if (!isAccessibilityOn(this#MainActivity, WhatsappAccessibilityService::class.java)) {
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
this#MainActivity.startActivity(intent)
} else {
val smsNumber = binding.txfPhone.editText?.text.toString()
val sendIntent = Intent(Intent.ACTION_SEND)
sendIntent.type = "text/plain"
sendIntent.putExtra(Intent.EXTRA_TEXT, "${binding.txtfMessage.editText?.text} mysuffix")
sendIntent.putExtra("jid", "$smsNumber#s.whatsapp.net")
sendIntent.setPackage("com.whatsapp")
startActivity(sendIntent)
}
}
}
private fun isAccessibilityOn(context: Context, clazz: Class<out AccessibilityService?>): Boolean {
var accessibilityEnabled = 0
val service: String = context.packageName + "/" + clazz.canonicalName
try {
accessibilityEnabled = Settings.Secure.getInt(
context.applicationContext.contentResolver,
Settings.Secure.ACCESSIBILITY_ENABLED
)
} catch (ignored: Settings.SettingNotFoundException) {
}
val colonSplitter = SimpleStringSplitter(':')
if (accessibilityEnabled == 1) {
val settingValue: String = Settings.Secure.getString(
context.applicationContext.contentResolver,
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
)
colonSplitter.setString(settingValue)
while (colonSplitter.hasNext()) {
val accessibilityService = colonSplitter.next()
if (accessibilityService.equals(service, ignoreCase = true)) {
return true
}
}
}
return false
}
}
What happen are:
I press the send button in my app and firstly my app checks for Accessibility Service.
If the Accessibility service is off then my app will launch system settings to grand the Service.
But if the Accessibility service is on for my app service then it should open WhatsApp and start the service to allow clicking on the WhatsApp send button.
My service not working I tried to use the Log class but nothing show in the run for android studio.
Please anyone can help me and tell me how to do that.
This seems like a package visibility issue as mentioned by Android
https://developer.android.com/about/versions/11/privacy/package-visibility
fun Activity.callPlayStoreIntent() {
val appPackageName = this.packageName
try {
val uri = Uri.parse("market://details?id=$appPackageName")
val intent = Intent(ACTION_VIEW, uri)
startActivity(intent)
} catch (exception: ActivityNotFoundException) {
try {
val uri = Uri.parse("https://play.google.com/store/apps/details?id=$appPackageName")
val intent = Intent(ACTION_VIEW, uri)
startActivity(intent)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
fun Context.callFromDialer(number: String) {
try {
val callIntent = Intent(Intent.ACTION_DIAL)
callIntent.data = Uri.parse("tel:$number")
if (callIntent.resolveActivity(packageManager) != null) {
startActivity(callIntent)
}
} catch (e: Exception) {
e.printStackTrace()
Toast.makeText(this, "No SIM Found", Toast.LENGTH_LONG).show()
}
}
fun Context.intentOpenMap(
latitude: Double,
longitude: Double,
label: String,
) {
try {
val uriBegin = "geo:$latitude,$longitude"
val query = "$latitude,$longitude($label)"
val encodedQuery = Uri.encode(query)
val uriString = "$uriBegin?q=$encodedQuery&z=20"
val uri = Uri.parse(uriString)
val intent = Intent(Intent.ACTION_VIEW, uri)
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
}
} catch (ex: Exception) {
ex.printStackTrace()
Toast.makeText(this, "Unable to open Map", Toast.LENGTH_LONG).show()
}
}
Your problem lies in the line of code intent.resolveActivity(getPackageManager()). When you call resolveActivity, you will get a warning like this:
Consider adding a declaration to your manifest when calling this method; see https://g.co/dev/packagevisibility for details
Check the document under PackageManager, you will see this note:
Note: If your app targets Android 11 (API level 30) or higher, the methods in this class each return a filtered list of apps. Learn more about how to manage package visibility.
So what does that mean?
In android 11, Google added package visibility policy. Apps now have tighter control over viewing other apps. Your application will not be able to view or access applications outside of your application.
What do you need to do?
All you need to do is add below line of code to AndroidManifest.xml:
<manifest>
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="geo" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="market" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<intent>
<action android:name="android.intent.action.DIAL" />
</intent>
</queries>
</manifest>
More information:
Package visibility in Android 11
Package visibility filtering on Android
I have successfully implemented SMS retriever in my application using the guideline here . My Code is working fine and otp is auto populated in many devices but some of the devices like vivo v15 pro, redmi note 4 it is not working (BroadcastReceiver's onReceive() not get triggered) . i have attached my code here. check and let me know if you have any solution for this. Thanks
Manifest.xml
<receiver
android:name=".sms.SMSRetrieverBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED" />
</intent-filter>
</receiver>
SMSRetrieverBroadcastReceiver.kt
class SMSRetrieverBroadcastReceiver : BroadcastReceiver() {
companion object{
private var otpReceiver: OtpReceiver? = null
fun initOTPListener(receiver: OtpReceiver) {
this.otpReceiver = receiver
}
}
override fun onReceive(context: Context, intent: Intent) {
if (SmsRetriever.SMS_RETRIEVED_ACTION == intent.action) {
val extras = intent.extras
val status = extras!!.get(SmsRetriever.EXTRA_STATUS) as Status
when (status.statusCode) {
CommonStatusCodes.SUCCESS -> {
// Get SMS message contents
var otp: String = extras.get(SmsRetriever.EXTRA_SMS_MESSAGE) as String
Timber.d("OTP_Message "+otp)
if(otpReceiver != null) {
otpReceiver!!.onOTPReceived(otp)
}
}
CommonStatusCodes.TIMEOUT -> {
// Waiting for SMS timed out (5 minutes)
// Handle the error ...
if(otpReceiver != null) {
otpReceiver!!.onOTPTimeOut()
}
}
}
}}
MainActivity.kt
fun startListeningForSMS() {
SMSRetrieverBroadcastReceiver.initOTPListener(this)
startSmsListener()
}
private fun startSmsListener() {
val client = SmsRetriever.getClient(mContext)
val task = client.startSmsRetriever()
task.addOnSuccessListener {
Timber.d("Success")
}
task.addOnFailureListener {
Timber.d("Failed")
}
}
override fun onOTPReceived(otp: String) {
setReceivedOtp(otp)
}
override fun onOTPTimeOut() {
setReceivedOtp(null)
}
Go to Messeges-click on (:) on top left ->Go to setting->Disable prevention from Otp verification.It worked fine in my case.
Can you check with this intent-filter action:
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
I followed everything in the android authentication guide and the quick start guide. I did as the guide told me to generate a SHA1 and adding it to Spotify app dashboard, and I got the app_client and added it to my app as well. Both scenarios still return the same thing. I even tried implementing the Login thru Browser feature, yet it still returns type EMPTY.
Here's my Login class
class SignInActivity : AppCompatActivity(), ConnectionStateCallback, Player.NotificationCallback {
private var mPlayer : SpotifyPlayer? = null
private val CLIENT_ID = //replace this
private val REDIRECT_URI = //replace this
private val REQUEST_CODE = 1337
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
btnSignin.setOnClickListener {
val builder = AuthenticationRequest.Builder(CLIENT_ID, AuthenticationResponse.Type.TOKEN, REDIRECT_URI)
builder.setScopes(arrayOf("user-read-private", "streaming"))
val request = builder.build()
AuthenticationClient.openLoginActivity(this, REQUEST_CODE, request)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, intent)
// Check if result comes from the correct activity
if (requestCode == REQUEST_CODE) {
val response = AuthenticationClient.getResponse(resultCode, intent)
Log.i("LoginActivity", Gson().toJson(response))
when (response.type) {
// Response was successful and contains auth token
AuthenticationResponse.Type.TOKEN -> {
Log.i("LoginActivity", "is equals to TOKEN")
val playerConfig = Config(this, response.accessToken, CLIENT_ID)
Spotify.getPlayer(playerConfig, this, object : SpotifyPlayer.InitializationObserver {
override fun onInitialized(spotifyPlayer: SpotifyPlayer) {
mPlayer = spotifyPlayer
mPlayer!!.addConnectionStateCallback(this#SignInActivity)
mPlayer!!.addNotificationCallback(this#SignInActivity)
}
override fun onError(throwable: Throwable) {
Log.e("LoginActivity", "Could not initialize player: " + throwable.message)
}
})
}
// Auth flow returned an error
AuthenticationResponse.Type.ERROR -> {
Log.i("LoginActivity", "ERROR!: $response.error")
}
AuthenticationResponse.Type.EMPTY -> {
Log.i("LoginActivity", "EMPTY!")
}
}
}
}
override fun onLoggedIn() {
Log.d("LoginActivity", "User logged in")
// This is the line that plays a song.
mPlayer!!.playUri(null, "spotify:track:2TpxZ7JUBn3uw46aR7qd6V", 0, 0)
}
}
and here's my AndroidManifest file
`
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".SignInActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<data
android:host="callback"
android:scheme="backdrop.io"/>
</intent-filter>
</activity>
<activity
android:name="com.spotify.sdk.android.authentication.LoginActivity"
android:theme="#android:style/Theme.Translucent.NoTitleBar"/>
</application>
I'm trying to get a Type.TOKEN from this
Had this exact same issue myself, the problem is with this line in onActivityResult():
val response = AuthenticationClient.getResponse(resultCode, intent)
data should be passed as the second argument instead of intent.
The reason this isn't a compiler error is due to Kotlin turning getters and setters into properties, intent is a call to the Activity method getIntent().