Append Firebase Token to start URL -Trusted Web Activity - android

I would like to pass FCM token in the start url. My code doesnt work everytime, i think needs a delay but i cant handle it.
Below code doesnt work every time because sometimes the TWA launches before the firebase connection has been made:
public class LauncherActivity
extends com.google.androidbrowserhelper.trusted.LauncherActivity {
public static String x = null;
#override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//RegisterToTopic for FCM
FirebaseMessaging.getInstance().subscribeToTopic("all");
FirebaseMessaging.getInstance().getToken()
.addOnCompleteListener(new OnCompleteListener() {
#override
public void onComplete(#nonnull Task task) {
// Get new FCM registration token
x = task.getResult();
}
});
}
#Override
protected Uri getLaunchingUrl() {
// Get the original launch Url.
Uri uri = super.getLaunchingUrl();
// Append the extra parameter to the launch Url
return uri
.buildUpon()
.appendQueryParameter("z", String.valueOf(x))
.build();
}
}
I have also tried this but the same result:
public class StartActivity extends AppCompatActivity {
final long SPLASH_DELAY = 4000;
public static String x = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
runMainApp();
FirebaseMessaging.getInstance().getToken()
.addOnCompleteListener(new OnCompleteListener<String>() {
#Override
public void onComplete(#NonNull Task<String> task) {
// Get new FCM registration token
x = task.getResult();
Intent intent = new Intent(getBaseContext(), CustomLauncherActivity.class);
intent.putExtra("EXTRA_SESSION_ID", x);
startActivity(intent);
}
});
}
private void runMainApp() {
new Handler().postDelayed(() -> {
startActivity(new Intent(SplashActivity.this, CustomLauncherActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK));
finish();
overridePendingTransition(R.anim.anim_right_in, R.anim.anim_left_out);
}, SPLASH_DELAY);
}
}
I have received an answer from android-browser-helper repo but i cant handel it. If someone could provide more help would be much appreciated.
public class MyLauncherActivity extends LauncherActivity {
private static class DelayedTwaLauncher extends TwaLauncher {
#Override
public void launch(TrustedWebActivityIntentBuilder twaBuilder,
CustomTabsCallback customTabsCallback,
#Nullable SplashScreenStrategy splashScreenStrategy,
#Nullable Runnable completionCallback,
FallbackStrategy fallbackStrategy) {
if (firebase has finished loading) {
super.launch(twaBuilder, customTabsCallback, splashScreenStrategy, fallbackStrategy);
} else {
// Save the parameters to some variables.
// Don't do anything else.
}
}
public void actuallyLaunch() {
if (we didn't call super.launch before) {
super.launch(the parameters you saved before);
}
}
#Override
protected TwaLauncher createTwaLauncher() {
return delayedTwaLauncher;
}
}

Starting with android-browser-helper] v2.2.0, it's possible to run asynchronous code before in the LauncherActivity before launching the Trusted Web Activity.
This is how a custom LauncherActivity for Firebase Analytics looks like:
public class FirebaseAnalyticsLauncherActivity extends LauncherActivity {
private String mAppInstanceId;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FirebaseAnalytics firebaseAnalytics = FirebaseAnalytics.getInstance(this);
// Start the asynchronous task to get the Firebase application instance id.
firebaseAnalytics.getAppInstanceId().addOnCompleteListener(task -> {
// Once the task is complete, save the instance id so it can be used by
// getLaunchingUrl().
mAppInstanceId = task.getResult();
launchTwa();
});
}
#Override
protected boolean shouldLaunchImmediately() {
// launchImmediately() returns `false` so we can wait until Firebase Analytics is ready
// and then launch the Trusted Web Activity with `launch()`.
return false;
}
#Override
protected Uri getLaunchingUrl() {
Uri uri = super.getLaunchingUrl();
// Attach the Firebase instance Id to the launchUrl. This example uses "appInstanceId" as
// the parameter name.
return uri.buildUpon()
.appendQueryParameter("appInstanceId", mAppInstanceId)
.build();
}
}
Check out the full Firebase Analytics demo here.

Related

Android Your app(s) are vulnerable to Intent Redirection

I got an email from google play support saying "Intent Redirection Your app(s) are vulnerable to Intent Redirection. To address this issue, follow the steps in this Google Help Center article."
After reading through the article, I'm guessing the key is my app should not call startActivity, startService, sendBroadcast, or setResult on untrusted Intents (intents used by external apps to invoke my app for example) without validating or sanitizing these Intents.
However, solution 1 in the article doesn't work in my case because my component needs to receive Intents from other apps.
Solution 2 is not applicable to my case because I don't know in advance which app would invoke my app, so I don't know what would getCallingActivity returns.
Solution 3 seems to be the most promising one, I tried to removeFlags of intents, however, when I resubmit my app, Google Play again alerts this vulnerability. I am about to try checking whether an Intent grants a URI permission using methods like getFlags and submit my app again to see the result. Does anyone know how do Google check this vulnerability anyway, and could someone spot the vulnerability in my source code and suggest a way to resolve it?
The exact message from Google Play is
Intent Redirection
Your app(s) are vulnerable to Intent Redirection.
To address this issue, follow the steps in this Google Help Center article.
com.mydomain.somepackage.a->a
And the following is the simplified source code.
// MainActivity.java
public class MainActivity extends CordovaActivity
{
SpecialUtil specialUtil;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
specialUtil = new specialUtil(MainActivity.this);
}
#Override
public void onResume() {
super.onResume();
specialUtil.verifyServerIfNeeded(MainActivity.this);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == this.specialUtil.CERT_INVALID_POPUP_REQUEST_CODE) {
// the user clicked the return button in the alert dialog within WhiteScreen activity
this.specialUtil.declareAsFailure();
}
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
}
// com/mydomain/somepackage/SpecialUtil.java
public class SpecialUtil {
private SharedPreferences mSharedPreferences;
private SharedPreferences.Editor mSharedPreferencesEditor;
private SharedPreferences.OnSharedPreferenceChangeListener listener;
private Activity activity;
private boolean shownCertInvalidPopup = false;
public final int CERT_INVALID_POPUP_REQUEST_CODE = 1000;
public SpecialUtil(Activity activity) {
this.activity = activity;
this.mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
this.mSharedPreferencesEditor = mSharedPreferences.edit();
this.listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals("SOME_RESULT")) {
String result = mSharedPreferences.getString("SOME_RESULT", "");
if (result.equals("RESULT_OK")) {
SpecialUtil.this.declareAsSuccess();
} else if (result.equals("RESULT_CANCELED")) {
SpecialUtil.this.declareAsFailure();
}
}
}
};
this.mSharedPreferences.registerOnSharedPreferenceChangeListener(listener);
}
public void verifyServerIfNeeded(Activity activity) {
Intent intent = activity.getIntent();
if (this.isFlowA(intent)) {
this.removePermission(intent);
String url = intent.getStringExtra("url");
this.verifyServer(url);
} else if (this.isFlowB(intent)) {
this.removePermission(intent);
String payment_request_object_url = intent.getData().getQueryParameter("pay_req_obj");
String callback_url = intent.getData().getQueryParameter("callback");
this.verifyServer(payment_request_object_url);
}
}
public boolean isFlowA(Intent intent) {
if (intent.getAction().equals("someAction")) {
return true;
}
return false;
}
public boolean isFlowB(Intent intent) {
if (intent.getData() != null) {
String path = intent.getData().getPath();
if (path.equals("something")) {
return true;
}
}
return false;
}
public void verifyServer(final String httpsURL) {
new Thread(new Runnable() {
#Override
public void run() {
try {
boolean isCertValid = SpecialUtil.this.verify(httpsURL);
if (isCertValid) {
// do somthing
} else {
// show a white screen with an alert msg
SpecialUtil.this.activity.runOnUiThread(new Runnable() {
public void run() {
if (!shownCertInvalidPopup) {
shownCertInvalidPopup = true;
Intent intent = new Intent(SpecialUtil.this.activity, WhiteScreen.class);
SpecialUtil.this.activity.startActivityForResult(intent, CERT_INVALID_POPUP_REQUEST_CODE);
}
}
});
}
} catch (IOException e) {
SpecialUtil.this.declareAsFailure();
}
}
}).start();
}
private void declareAsSuccess() {
this.activity.setResult(Activity.RESULT_OK, SpecialUtil.this.activity.getIntent());
this.activity.finishAndRemoveTask();
}
public void declareAsFailure() {
this.activity.setResult(Activity.RESULT_CANCELED, this.activity.getIntent());
this.activity.finishAndRemoveTask();
}
private void removePermission(Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.removeFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.removeFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}
}
// com/mydomain/somepackage/WhiteScreen.java
public class WhiteScreen extends Activity {
SpecialUtil specialUtil;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
specialUtil = new SpecialUtil(WhiteScreen.this);
String title = "someTitle";
final AlertDialog.Builder builder = new AlertDialog.Builder(WhiteScreen.this)
.setTitle(title)
.setPositiveButton(btn_text, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Don't start the process, quit App immediately
WhiteScreen.this.setResult(Activity.RESULT_CANCELED, WhiteScreen.this.getIntent());
WhiteScreen.this.finishAndRemoveTask();
}
});
AlertDialog alertDialog = builder.create();
alertDialog.show();
}
}

Truecaller SDK Error : No compatible client available. Please change your scope

I am trying to implement truecaller in my app and i am doing exactly written in Truecaller Docs.
But still it gives me error.
I have tried googling the problem but still couldn't find the solution.
Here is the error:
No compatible client available. Please change your scope
Here is my code:
public class MainActivity extends FragmentActivity implements ITrueCallback {
private ViewPager2 viewPager2;
private List < Integer > imagesList;
private Button btnContinue, btnLoginTruecaller;
private EditText etPhone;
private Preferences preferences;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Constants.removeStatusBar(this);
TruecallerSdkScope trueScope = new TruecallerSdkScope.Builder(this, sdkCallback)
.consentMode(TruecallerSdkScope.CONSENT_MODE_BOTTOMSHEET)
.buttonColor(Color.parseColor("#000000"))
.buttonTextColor(Color.parseColor("#000000"))
.loginTextPrefix(TruecallerSdkScope.LOGIN_TEXT_PREFIX_TO_GET_STARTED)
.loginTextSuffix(TruecallerSdkScope.LOGIN_TEXT_SUFFIX_PLEASE_VERIFY_MOBILE_NO)
.ctaTextPrefix(TruecallerSdkScope.CTA_TEXT_PREFIX_USE)
.buttonShapeOptions(TruecallerSdkScope.BUTTON_SHAPE_ROUNDED)
.privacyPolicyUrl("<<YOUR_PRIVACY_POLICY_LINK>>")
.termsOfServiceUrl("<<YOUR_PRIVACY_POLICY_LINK>>")
.footerType(TruecallerSdkScope.FOOTER_TYPE_NONE)
.consentTitleOption(TruecallerSdkScope.SDK_CONSENT_TITLE_LOG_IN)
.sdkOptions(TruecallerSdkScope.SDK_OPTION_WITHOUT_OTP)
.build();
TruecallerSDK.init(trueScope);
btnContinue = findViewById(R.id.btnContinue);
etPhone = findViewById(R.id.etPhone);
btnLoginTruecaller = findViewById(R.id.btnLoginTruecaller);
TruecallerSDK.getInstance().getUserProfile(this);
btnLoginTruecaller.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {}
});
preferences = new Preferences(this);
if (preferences.isLoggedin()) {
Intent intent = new Intent(this, HomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
imagesList = new ArrayList < > ();
imagesList.add(R.drawable.black);
imagesList.add(R.drawable.pubg);
// ViewPagerAdapter adapter = new ViewPagerAdapter(this,imagesList);
// viewPager2.setAdapter(adapter);
btnContinue.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
preferences.setMobileNumber(etPhone.getText().toString());
Intent intent = new Intent(MainActivity.this, OTPActivity.class);
intent.putExtra("phone", etPhone.getText().toString());
startActivity(intent);
}
});
}
private final ITrueCallback sdkCallback = new ITrueCallback() {
#Override
public void onSuccessProfileShared(#NonNull TrueProfile trueProfile) {
}
#Override
public void onFailureProfileShared(#NonNull TrueError trueError) {
}
#Override
public void onVerificationRequired(TrueError trueError) {
}
};
#Override
public void onSuccessProfileShared(#NonNull TrueProfile trueProfile) {
}
#Override
public void onFailureProfileShared(#NonNull TrueError trueError) {
}
#Override
public void onVerificationRequired(TrueError trueError) {
}
}
Here is the truecaller docs i am following:
https://docs.truecaller.com/truecaller-sdk/android/integrating-with-your-app/setup
Thanks for sharing the above information.
The exception that you are facing :
No compatible client available. Please change your scope
comes only in the case where you are calling a method from TruecallerSDK that is not in the scope which you provide while initialising the SDK.
For instance, in case where Truecaller app is not installed or Truecaller app is installed but not logged and you have mentioned the sdkOptions as TruecallerSdkScope.SDK_OPTION_WIHTOUT_OTP then on calling TruecallerSDK.getInstance().getUserProfile() method you will face this exception.
To refrain from facing this again you can put a check that if TruecallerSDK.getInstance.isUsable turns out to be True, then only call TruecallerSDK.getInstance.getUserProfile or you can change the sdkOptions scope to TruecallerSdkScope.SDK_OPTION_WITH_OTP to verify both Truecaller and Non-Truecaller users
In case if you face any queries in the future, please feel free to reach us via our support channel https://developer.truecaller.com/support for a faster and dedicated response.
Regard,
Parth

Communication between BroadcastReceiver and Activity using interface

Folks!
I'm trying to implement BroadcastReceiver for listening to incoming Message using SMSRetrieverAPI.
I'm getting messages in Broadcast's onReceive method but after that, I have to pass that Message String in activity without restarting it, So I have implemented interface.
I have tried below code but, it throws NullPointerException for mOTPReceiveListener. Something didn't work perfectly so please correct me.
SMSBroadcastReceiver is like below
public class SMSBroadcastReceiver extends BroadcastReceiver {
OTPReceiveListener mOTPReceiveListener = null;
public void InitOTPReceiveListener(OTPReceiveListener otpreceivelistener){
this.mOTPReceiveListener = otpreceivelistener;
}
#Override
public void onReceive(Context context, Intent intent) {
if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
Bundle extras = intent.getExtras();
Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
switch(status.getStatusCode()) {
case CommonStatusCodes.SUCCESS:
// Get SMS message contents
String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
Log.e("VERIFY","Full Message : "+message);
if (mOTPReceiveListener != null) {
String[] temp = message.split("-");
String[] msg = temp[1].split(" ");
message = msg[0];
Log.e("VERIFY","message : "+message);
mOTPReceiveListener.onOTPReceived(message);
}
break;
case CommonStatusCodes.TIMEOUT:
// Waiting for SMS timed out (5 minutes)
mOTPReceiveListener.onOTPTimeOut();
break;
}
}
}
public interface OTPReceiveListener {
void onOTPReceived(String otp);
void onOTPTimeOut();
}
}
and the activity code
public class VerificationActivity extends BaseActivity implements SMSBroadcastReceiver.OTPReceiveListener
{
Context mContext;
Button btn_verify_submit;
TextView text_resend_otp;
private String TAG = "VERIFY";
private String PHONE = null;
private SmsRetrieverClient mSmsRetrieverClient;
private SMSBroadcastReceiver mSMSBroadcastReceiver;
SMSBroadcastReceiver.OTPReceiveListener mOTPReceiveListener;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_verification);
initView();
setClickListener();
PHONE = getIntent().getStringExtra("phone");
Log.e(TAG,"phone : "+PHONE);
mSmsRetrieverClient = SmsRetriever.getClient(this);
startVerify();
mOTPReceiveListener = this;
mSMSBroadcastReceiver = new SMSBroadcastReceiver();
mSMSBroadcastReceiver.InitOTPReceiveListener(this);
}
private void initView()
{
layout_verification_main = findViewById(R.id.layout_verification_main);
overrideFonts(layout_verification_main,VerificationActivity.this);
edt_verify_code = findViewById(R.id.edt_verify_code);
btn_verify_submit = findViewById(R.id.btn_verify_submit);
text_resend_otp = findViewById(R.id.text_resend_otp);
}
private void setClickListener()
{
btn_verify_submit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Call OTP Match API
}
});
text_resend_otp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Call Resend OTP API
}
});
}
private void startVerify() {
Task<Void> task = mSmsRetrieverClient.startSmsRetriever();
task.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.e(TAG, "SmsRetrievalResult start onSuccess.");
}
});
task.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.e(TAG, "SmsRetrievalResult start failed.", e);
}
});
}
#Override
public void onOTPReceived(String otp) {
edt_verify_code.setText("" + otp);
Toast.makeText(mContext, "Message OTP : " + otp, Toast.LENGTH_SHORT).show();
Log.e("VERIFY","otp in activity : "+otp);
}
#Override
public void onOTPTimeOut() {
}
}
and I have registered in Manifest like this
<receiver android:name=".broadcast.SMSBroadcastReceiver" android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED"/>
</intent-filter>
</receiver>
Am I missing something?
The SMSBroadcastReceiver instance created from the definition in your manifest is NOT the same one you created in VerificationActivity.onCreate().
You can register the broadcast receiver from your activity using Context.registerReceiver().
To contact between activity and receiver what you are try is wrong as your listener is dependent on activities onCreate() i.e on lifecycle of Activity.
So to solve this you need to follow below step's:
Solution
Be sure that activity is alive by starting the activity from your receiver (startActitvity() using intent ) again. check this thread if you don't want to restart the activity again and want to be sure that activity is on screen.
Send a broadcast from your receiver to activity read this thread. No need of listener as activity can directly listen to message
you can combine both solution's for you own use case it will work.

Braintree PayPal Sandbox keeps redirecting

I have added a simple non drop-in paypal integration in sandbox mode to my app. Here is a test activity with a single "Pay" button:
public class PaypalPaymentAcivity extends Activity implements PaymentMethodNonceCreatedListener {
private BraintreeFragment mBraintreeFragment;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_paypal);
findViewById(R.id.payButton).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startPayment();
}
});
}
private void startPayment() {
try {
mBraintreeFragment = BraintreeFragment.newInstance(this, "...");
PayPalRequest request = new PayPalRequest("1")
.currencyCode("USD")
.intent(PayPalRequest.INTENT_AUTHORIZE);
PayPal.requestOneTimePayment(mBraintreeFragment, request);
} catch (InvalidArgumentException e) {
e.printStackTrace();
}
}
#Override
public void onPaymentMethodNonceCreated(PaymentMethodNonce paymentMethodNonce) {
}
}
However once the PayPal browser window comes up after clicking the buttons it just keeps popping up over and over, and never returns to my activity.
Anyone had a successful integration like this?

receiving error when building an android project with Neura

I'm working with neura sdk in order to detect current data of a user(where he/she is, where were they 10 min ago etc).
I want to login to their api, and authenticate my user, however - when i call NeuraApiClient.authenticate(...) nothing happens.
I followed neura documentations, but still - nothing happens.
Here's my code :
public class MainActivity extends AppCompatActivity {
private ArrayList<Permission> mPermissions;
private AuthenticationRequest mAuthenticateRequest;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Builder builder = new Builder(this);
NeuraApiClient neuraApiClient = builder.build();
neuraApiClient.setAppUid(getResources().getString(R.string.app_uid));
neuraApiClient.setAppSecret(getResources().getString(R.string.app_secret));
neuraApiClient.connect();
fetchPermissions(neuraApiClient);
neuraApiClient.authenticate(100, mAuthenticateRequest);
}
private void fetchPermissions(final NeuraApiClient client) {
client.getAppPermissions(new GetPermissionsRequestCallbacks() {
#Override
public void onSuccess(final List<Permission> permissions) throws RemoteException {
runOnUiThread(new Runnable() {
#Override
public void run() {
mPermissions = new ArrayList<>(permissions);
mAuthenticateRequest = new AuthenticationRequest();
mAuthenticateRequest.setAppId(client.getAppUid());
mAuthenticateRequest.setAppSecret(client.getAppSecret());
mAuthenticateRequest.setPermissions(mPermissions);
}
});
}
#Override
public void onFailure(Bundle resultData, int errorCode) throws RemoteException {
}
#Override
public IBinder asBinder() {
return null;
}
});
}
}
getAppPermissions is an asynchronous call, and the data is fetched on GetPermissionsRequestCallbacks.
in GetPermissionsRequestCallbacks you're initiating mAuthenticateRequest which is in use of authenticate method.
Which means you have to wait untill onSuccess of GetPermissionsRequestCallbacks is called, and only then you can call
neuraApiClient.authenticate(100, mAuthenticateRequest);
Basically, if you don't wait for mAuthenticateRequest to be fetched, you authenticate with mAuthenticateRequest = null, and neuraApiClient.authenticate(..) fails.
You can do something like this : call authenticate when the results are received -
private void fetchPermissions(final NeuraApiClient client) {
client.getAppPermissions(new GetPermissionsRequestCallbacks() {
#Override
public void onSuccess(final List<Permission> permissions) throws RemoteException {
runOnUiThread(new Runnable() {
#Override
public void run() {
mPermissions = new ArrayList<>(permissions);
mAuthenticateRequest = new AuthenticationRequest();
mAuthenticateRequest.setAppId(client.getAppUid());
mAuthenticateRequest.setAppSecret(client.getAppSecret());
mAuthenticateRequest.setPermissions(mPermissions);
client.authenticate(100, mAuthenticateRequest);
}
});
}
...
});
}

Categories

Resources