I am using firebase notifications and dynamic links in my application
I am sending deeplinks as a payload in notifications and when the user clicks on the notification i am opening the deeplink like this
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(pushAction.getActionDeeplink())); // contains the deeplink
if (pushNotification.getExtraData() != null) { // not extra Data from push
intent.putExtra(IntentKeys.PUSH_DATA_EXTRA, pushNotification.getExtraData());
}
PendingIntent actionPendingIntent =
PendingIntent.getActivity(context, pushNotification.getNotificationId(), intent,
PendingIntent.FLAG_UPDATE_CURRENT);
final NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(largeIcon)
.setTicker(pushNotification.getTicker())
.setContentTitle(pushNotification.getTitle())
.setContentText(pushNotification.getMessage())
.setAutoCancel(true)
.setContentIntent(actionPendingIntent);
notificationManager.notify(uniqueId, mBuilder.build());
I have declared an activity in my application
<activity
android:name=".ui.activities.SaveActivity"
android:label="#string/title_activity_save"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustPan">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="example.com"
android:pathPattern="/save"
android:scheme="http" />
</intent-filter>
</activity>
and the deeplink url, that is sent through notification is http://example.com/save
And in the activity I have setup to receive deeplinks like this
public class SaveActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_save);
setupGoogleApiClient();
}
private void setupGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this, this)
.addApi(AppInvite.API)
.build();
boolean autoLaunchDeepLink = false;
AppInvite.AppInviteApi.getInvitation(mGoogleApiClient, this, autoLaunchDeepLink)
.setResultCallback(
new ResultCallback<AppInviteInvitationResult>() {
#Override
public void onResult(#NonNull AppInviteInvitationResult result) {
if (result.getStatus().isSuccess()) {
Intent intent = result.getInvitationIntent();
String deepLink = AppInviteReferral.getDeepLink(intent);
Log.d("save getInvitation: found deeplink." + deepLink);
/**************************** ISSUE ***********************/
if(intent.hasExtra(IntentKeys.PUSH_DATA_EXTRA)) {
// there is no extra data :(
}
/**************************** ISSUE ***********************/
} else {
Log.d("save getInvitation: no deep link found.");
}
}
});
}
}
Everything is working as expected. The SaveActivity is getting called and it is opening the SaveActivity. But, NO extra data is received in the activity.
I am setting the string extra using IntentKeys.PUSH_DATA_EXTRA as the key in the intent.
Related
I am trying to create a simple oauth2 flow using AppAuth. I am following this tutorial of AppAuth. It is doing good up to making an oauth request but after authorization when it comes to main activity then it never calls onNewIntent, I also checked the question discussed here.
Edit: When I use onResume method then it comes to onResume method after authorization but with "android.intent.action.MAIN" action. Where it should come with "com.google.codelabs.appauth.HANDLE_AUTHORIZATION_RESPONSE" action on onResume.
Any suggestion why it is happening?
following is the MainActivity class
public class MainActivity extends AppCompatActivity {
private static final String USED_INTENT = "USED_INTENT";
public static final String LOG_TAG = "AppAuthSample";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void setupAuthorization(View view) {
AuthorizationServiceConfiguration serviceConfiguration = new AuthorizationServiceConfiguration(
Uri.parse("https://accounts.google.com/o/oauth2/v2/auth"), //* auth endpoint *//*,
Uri.parse("https://oauth2.googleapis.com/token") //* token endpoint *//*
);
String clientId = "MY_ID.apps.googleusercontent.com";
Uri redirectUri = Uri.parse("com.demo.testdriveapi:/oauth2callback");
AuthorizationRequest.Builder builder = new AuthorizationRequest.Builder(
serviceConfiguration,
clientId,
"code",
redirectUri
);
builder.setScopes("https://www.googleapis.com/auth/drive.appdata");
AuthorizationRequest request = builder.build();
AuthorizationService authorizationService = new AuthorizationService(this);
String action = "com.google.codelabs.appauth.HANDLE_AUTHORIZATION_RESPONSE";
Intent postAuthorizationIntent = new Intent(action);
PendingIntent pendingIntent = PendingIntent.getActivity(this, request.hashCode(), postAuthorizationIntent, 0);
authorizationService.performAuthorizationRequest(request, pendingIntent);
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
checkIntent(intent);
}
private void checkIntent(#Nullable Intent intent) {
if (intent != null) {
String action = intent.getAction();
switch (action) {
case "com.google.codelabs.appauth.HANDLE_AUTHORIZATION_RESPONSE":
if (!intent.hasExtra(USED_INTENT)) {
handleAuthorizationResponse(intent);
intent.putExtra(USED_INTENT, true);
}
break;
default:
// do nothing
}
}
}
#Override
protected void onStart() {
super.onStart();
checkIntent(getIntent());
}
private void handleAuthorizationResponse(#NonNull Intent intent) {
AuthorizationResponse response = AuthorizationResponse.fromIntent(intent);
AuthorizationException error = AuthorizationException.fromIntent(intent);
final AuthState authState = new AuthState(response, error);
if (response != null) {
Log.i(LOG_TAG, String.format("Handled Authorization Response %s ", authState.jsonSerializeString()));
AuthorizationService service = new AuthorizationService(this);
service.performTokenRequest(response.createTokenExchangeRequest(), new AuthorizationService.TokenResponseCallback() {
#Override
public void onTokenRequestCompleted(#Nullable TokenResponse tokenResponse, #Nullable AuthorizationException exception) {
if (exception != null) {
Log.w(LOG_TAG, "Token Exchange failed", exception);
} else {
if (tokenResponse != null) {
authState.update(tokenResponse, exception);
Log.i(LOG_TAG, String.format("Token Response [ Access Token: %s, ID Token: %s ]", tokenResponse.accessToken, tokenResponse.idToken));
}
}
}
});
}
}
}
Here is the snippet from AndroidMenifest.xml
<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/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="com.google.codelabs.appauth.HANDLE_AUTHORIZATION_RESPONSE"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity android:name="net.openid.appauth.RedirectUriReceiverActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="com.demo.testdriveapi"/>
</intent-filter>
</activity>
</application>
and here is the snippet of activity_main.xml, I only have one button
<Button
android:id="#+id/buttonAuthorize"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="153dp"
android:layout_marginTop="288dp"
android:layout_marginEnd="170dp"
android:onClick="setupAuthorization"
android:text="Authorize"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Edit: Also tried using "android:launchMode="singleTop" but it also doesn't work.
Edit2: Adding screenshot of Google developer console
check the google drive API Dashboard for oAuth authentication and your callback name shall match and it should be registered there as well. And it is with this callback that authorization response is sent back to your activity.
It appears that net.openid:appauth:0.7.1 is buggy. I stumbled upon this question then I changed my version to 0.2.0 and it worked.
If I understand you correctly, you have successfully configured AppAuth and on launching the authentication mechanism, the user is able to enter their username and password to login into openId or whatever it is. But on doing so you cannot intercept the result of that intent (chrome custom tabs).
If so try this override method
onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
you also need to startActivityForResult(authIntent, REQUEST_CODE)
I’m new to android.I programmed an app that uses Firebase to get push notifications. If I send a notification from the Firebase interface, the device receives the notification only when the app is running. Is there a way to get the notifications when the app is not running or at least to get all the unseen notifications when the app starts up?
Thank you in advance!
I add my code...
This is the class extending the FirebaseMessagingService:
public class MyFirebaseMessagingService extends FirebaseMessagingService {
public MyFirebaseMessagingService() {
}
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
sendNotification(remoteMessage.getNotification().getBody());
}
private void sendNotification(String messageBody)
{
//Intent intent = new Intent (this, MainActivity.class);
//intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
notificationBuilder.setSmallIcon(R.mipmap.ic_launcher_round);
notificationBuilder.setContentTitle("My app");
notificationBuilder.setContentText(messageBody);
notificationBuilder.setAutoCancel(true);
notificationBuilder.setSound(defaultSoundUri);
//notificationBuilder.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0,notificationBuilder.build());
}
}
and this is the class extending the FirebaseInstanceIdService:
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService{
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// Instance ID token to your app server.
sendRegistrationToServer(refreshedToken);
}
private void sendRegistrationToServer(String token)
{
}
}
Finally, this is the manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="user.pushnotificationexample">
<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_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<service
android:name=".MyFirebaseMessagingService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
</application>
</manifest>
This is Xamarin Android solution, but it should get you close to native.
Try test sending a "data" push notification from an online tester like "pushwatch". Make sure to click the json checkbox and add a json payload that includes the key, "data":
{"data": {"title":"This is a title!","body":"This is the body text!"}}
The OnMessageReceived(RemoteMessage message) method, message parameter, has a dictionary property "Data": i.e. message.Data. You'll find the json payload key value pairs (title & body) in that dictionary.
Note, if the app is STOPPED, the user will OPEN the notification and Android will open the app, targeting the Activity used in the pending intent created and used to build the local notification (see below). You will need to handle the notification intent from the OnCreate method of your target Activity ( otherwise if the app is running, you'll handle the intent from an override of OnNewIntent).
public class MainActivity....
{
protected override void OnCreate(Bundle bundle)
{
...
var pushExtra = Intent.GetStringExtra("your.pushextra.key");
if(pushExtra != null)
{
MyType myType = JsonConvert.Deserialize<MyType>(pushExtra);
//if you have a framework like MVVM or startup process
//and your type
//is used for app navigation, you may need to store
//the string value in shared preferences and deserialize
//and use it later
//after the app has completely finished starting
}
}
}
Save yourself some effort, if you need a type or dictionary here, make sure to serialize the object when putting the extra (when you create the local notification) and then deserialize the pushExtra to your concrete type to use an instance of it above. You may find most examples put a string, or int or dictionary of key value pairs.
void ConfigureLocalNotification(MyType typeInstance)
{
...
var intent = new Intent(context, typeof(MainActivity));
var jsonData = JsonConvert.SerializeObject(typeInstance);
Log.Info(TAG, $"Adding IntentExtra: {jsonData}");
intent.PutExtra("your.pushextra.key", jsonData);
intent.AddFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop |
ActivityFlags.IncludeStoppedPackages); //| ActivityFlags.NewTask);
var pendingIntent = PendingIntent.GetActivity(context, 0, intent,
PendingIntentFlags.OneShot);
....
NotificationCompat.Builder builder = null;
builder = new NotificationCompat.Builder(context ...);
builder.SetContentIntent(pendingIntent) ....
}
Hope this helps
I am trying to implement a custom notification for my mediaplayer with notification bar that has just one button that will act as stop and play control.
So far have implemented the notification successfully and the button is making the function call, but the problem is that onReceive when I create an intent and call the activity, the activity gets recreated on top of the old one and I get bad double echoing media player playing in the background.
Have tried to make the launchMode= Single, but when I make it single the button click makes no difference, it means that the function call is not getting made if I turn the launch mode to SINGLE instead of STANDARD.
MAIN_ACTIVITY CODE SNIPPET
//NOTIFICATION RELATED CLASSES
private NotificationCompat.Builder builder;
private NotificationManager notificationManager;
//private int notification_id;
private RemoteViews remoteViews;
Context context;
Intent notification_intent;
final int notification_id =545816666;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
boolean attachMedia = getIntent().getBooleanExtra("attachMedia",false);
if (attachMedia) {
attachMediaActivity();
}
//CODE DE NOTIFICATION
context =this;
notification_intent=new Intent(context,MainActivity.class);
notificationManager=(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
remoteViews=new RemoteViews(getPackageName(),R.layout.custom_notification);
remoteViews.setImageViewResource(R.id.notif_icon,R.mipmap.ic_launcher);
remoteViews.setTextViewText(R.id.notif_title,"BOOM");
remoteViews.setTextViewText(R.id.button,"Button");
Intent button_intent= new Intent("player_control_clicked");
button_intent.putExtra("id",notification_id);
PendingIntent p_button_intent=PendingIntent.getBroadcast(context,123,button_intent,0);
remoteViews.setOnClickPendingIntent(R.id.button,p_button_intent);
tview=(TextView) findViewById(R.id.playerText);
btn=(Button) findViewById(R.id.playerButton);
if(!mediaPlayer.isPlaying())
{
tview.setText("Press play");
btn.setText("Play");
}
else
{
tview.setText("Playing");
btn.setText("Stop");
}
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Intent notification_intent=new Intent(context,MainActivity.class);
//THIS CODE UPDATES OLD NOTIFICATION
PendingIntent pendingIntent=PendingIntent.getActivity(context,0,notification_intent,PendingIntent.FLAG_UPDATE_CURRENT);
builder =new NotificationCompat.Builder(context);
builder.setSmallIcon(R.mipmap.ic_launcher)
.setCustomBigContentView(remoteViews)
.setContentIntent(pendingIntent)
.setOngoing(true);
notificationManager.notify(notification_id,builder.build());
if(!mediaPlayer.isPlaying())
{
//tview.setText("Playing");
btn.setText("Stop");
playStream();
}
else
{
tview.setText("Stopped");
btn.setText("Play");
mediaPlayer.stop();
mediaPlayer.reset();
}
}
});
}
public void attachMediaActivity()
{
//CODE DE NOTIFICATION
context =this;
notification_intent=new Intent(context,MainActivity.class);
notificationManager=(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
remoteViews=new RemoteViews(getPackageName(),R.layout.custom_notification);
remoteViews.setImageViewResource(R.id.notif_icon,R.drawable.stream_icon);
remoteViews.setTextViewText(R.id.notif_title,"Stopped");
remoteViews.setTextViewText(R.id.button,"STOPA");
Intent button_intent= new Intent("player_control_clicked");
button_intent.putExtra("id",notification_id);
/*PendingIntent p_button_intent=PendingIntent.getBroadcast(context,123,button_intent,0);
remoteViews.setOnClickPendingIntent(R.id.button,p_button_intent);*/
PendingIntent pendingIntent=PendingIntent.getActivity(context,0,notification_intent,PendingIntent.FLAG_UPDATE_CURRENT);
builder =new NotificationCompat.Builder(context);
builder.setSmallIcon(R.mipmap.ic_launcher)
.setCustomBigContentView(remoteViews)
.setContentIntent(pendingIntent)
.setOngoing(true);
notificationManager.notify(notification_id,builder.build());
if (mediaPlayer.isPlaying())
{
mediaPlayer.stop();
}
else
{
playStream();
}
}
THE BROADCAST LISTENER FOR THE NOTIFICATION BUTTON CLICK THAT CALLS THE ACTIVTY VIA INTENT
public class Button_listener extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(intent.getExtras().getInt("id"));
Toast.makeText(context, "GENERATED BY NOTIFICATION", Toast.LENGTH_SHORT).show();
intent= new Intent(context, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//intents.addExtra("attachMedia",true); // Extra info
intent.putExtra("attachMedia",true);
context.startActivity(intent);
}
}
MY MANIFEST
<activity android:name=".MainActivity"
android:screenOrientation="portrait"
android:launchMode="singleTop"
android:noHistory="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="Button_listener">
<intent-filter>
<action android:name="player_control_clicked"/>
</intent-filter>
</receiver>
Thanks in advance
PS: TRIED EVERY POSSIBLE ANSWER ON STACK
The problem lies within your intent Flag
you have added the flag intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);,
change that to intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
For more reference read this https://developer.android.com/reference/android/content/Intent.html
I have made an forum application in android and used phpmyadmin as my database. But when a question gets a new answer the application should show a notification to all users so how can i do it is there a need to use firebase or by just using a webservice!
Firstly, you need to go to the firebase console and create an app. (For this you will need to login into your google account) and follow the steps provided here.
https://firebase.google.com/docs/
Once that is done you will need to add these services to your Manifest.xml file
<service
android:name=".firebase.FirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<service
android:name=".firebase.FirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
FirebaseInstanceIdService.class
public class FirebaseInstanceIDService extends FirebaseInstanceIdService {
private static final String TAG = "FirebaseInstanceIDService";
#Override
public void onTokenRefresh() {
String token = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "OnTokenRefresh callback. Token received : " + token);
}
}
FirebaseMessagingService.class
public class FirebaseMessagingService extends com.google.firebase.messaging.FirebaseMessagingService {
private static final String TAG = "FirebaseMessagingService";
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG,"onMessageReceived.");
showNotification(remoteMessage.getData().get("message"));
}
private void showNotification(String message) {
Intent i = new Intent(this, HomeActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,i,PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setAutoCancel(true)
.setContentTitle("Slapr Notification Demo")
.setContentText(message)
.setSmallIcon(R.drawable.common_google_signin_btn_icon_dark)
.setContentIntent(pendingIntent);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(0,builder.build());
}
}
Now you can get the tokenId in your activity by doing the following
Log.d(TAG, "Recieved token : " + FirebaseInstanceId.getInstance().getToken());
This are the most helpful tutorials that i had found when i started. I hope it helps you.
https://www.youtube.com/watch?v=LiKCEa5_Cs8
https://www.youtube.com/watch?v=MYZVhs6T_W8
I have two activities and service. I'm trying to make direct sharing, but it doesn't work(
First activity that sending data
public class MainShareActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_share_main);
}
public void share(View view){
startService(new Intent(this, DirectSharePicker.class));
EditText etShare = (EditText) findViewById(R.id.sharedText);
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, etShare.getText().toString());
startActivity(Intent.createChooser(intent, "Direct share demo"));
}
Activity, that catches SEND
public class CatchSendActivity extends AppCompatActivity {
public static final String EXTRA_USER_ID = "userId";
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shared);
TextView textView = (TextView) findViewById(R.id.result);
Intent intent = getIntent();
int userID = intent.getIntExtra(EXTRA_USER_ID, -1);
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
textView.setText(userID + ": " + sharedText);
}
Target service
public class DirectSharePicker extends ChooserTargetService {
#Override
public List<ChooserTarget> onGetChooserTargets(ComponentName componentName, IntentFilter intentFilter) {
List<ChooserTarget> response = new LinkedList<>();
Icon icon = Icon.createWithResource(this, R.id.icon);
for (int i = 0; i < 5; i++){
String title = "Target" + i;
Intent intent = new Intent(this, CatchSendActivity.class);
intent.putExtra(CatchSendActivity.EXTRA_USER_ID, i);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
ChooserTarget chooserTarget = new ChooserTarget(title, icon, i, pendingIntent);
response.add(chooserTarget);
}
return response;
}
And this is my manifest
<activity
android:name=".MainShareActivity"
android:label="#string/app_name"/>
<service
android:name=".DirectSharePicker"
android:label="SHARING!"
android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
<intent-filter>
<action android:name="android.service.chooser.ChooserTargetService" />
</intent-filter>
</service>
<activity
android:name=".CatchSendActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain" />
</intent-filter>
<meta-data
android:name="android.service.chooser.chooser_target_service"
android:value=".DirectSharePicker" />
</activity>
But onGetChooserTargets never called.
From the documentation on Android Developer Pages:
The creator of a target may supply a ranking score. This score is assumed to be relative to the other targets supplied by the same query. Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match). Scores for a set of targets do not need to sum to 1.
Try putting some valid score instead 'i' since i can go upto 4 as per your code.
Also, put full name of classes and services in manifest
e.g
<service
android:name="com.your.package.DirectSharePicker"
...
Instead
<service
android:name=".DirectSharePicker"
...