Appcelerator custom module receiving callback for onActivityResult - java

I'm trying to create a custom module in Appcelerator for the new Square API for Android. I have everything the way I want it, but the main problem is that I want to be able to notify the caller that the payment was successful for if it failed. The Square API says this:
After Square finishes, Android invokes Activity.onActivityResult() on the activity passed to the constructor. The request code passed to this method will be passed to onActivityResult(). The result code is Activity.RESULT_CANCELED if the payment was canceled or Activity.RESULT_OK if the payment succeeded.
I've been passing the TiContext.currentActivity to the constructor:
public SquareModule(TiContext tiContext) {
super(tiContext);
ourSquare = new Square(tiContext.getActivity());
}
And then in the method that actually runs the payment, I have this that basically tries to set the passed in callback to the onResult handlers of the current activity using the registerResultHandler in the TiActivitySupportHelper class.
public void runPayment(KrollInvocation invocation, int price, String description, KrollCallback handler) {
Log.i(LCAT, "runPayment called");
// Register the passed in function as a handler on the onResult stack
this.resultCallback = handler;
Activity activity = invocation.getTiContext().getActivity();
TiActivitySupportHelper support = new TiActivitySupportHelper(activity);
int code = support.getUniqueResultCode();
support.registerResultHandler(code, this);
// Some of the payment work here
ourSquare.squareUp(Bill.containing(advice), code);
}
The main module class implements TiActivityResultHandler and implements onResult and onError. These methods are not being called at all. And of course the passed in method isn't being called either.
For completeness, see the implementation of the onResult and onError handlers:
#Override
public void onResult(Activity activity, int requestCode, int resultCode, Intent data)
{
Log.i(LCAT, "onResult Called");
if (resultCallback == null) return;
KrollDict event = new KrollDict();
event.put(TiC.EVENT_PROPERTY_REQUEST_CODE, requestCode);
event.put(TiC.EVENT_PROPERTY_RESULT_CODE, resultCode);
event.put(TiC.EVENT_PROPERTY_INTENT, new IntentProxy(getTiContext(), data));
event.put(TiC.EVENT_PROPERTY_SOURCE, this);
resultCallback.callAsync(event);
}
#Override
public void onError(Activity activity, int requestCode, Exception e)
{
Log.i(LCAT, "onError Called");
if (resultCallback == null) return;
KrollDict event = new KrollDict();
event.put(TiC.EVENT_PROPERTY_REQUEST_CODE, requestCode);
event.put(TiC.EVENT_PROPERTY_ERROR, e.getMessage());
event.put(TiC.EVENT_PROPERTY_SOURCE, this);
resultCallback.callAsync(event);
}
And also see the Appcelerator JS calling the method in the module:
square.runPayment(2, 'Testing123', function(e) {
label1.text = 'Payment Successful!';
});

For those that come upon this question. The answer can be found in the module here:
https://github.com/hidef/Appcelerator-Square-Module (see the LaunchSquare.java class)
Basically, I used an Activity object that I created to receive the Square API's onResult update. I then was able to pass that back cleanly to the module class and hand it back via callback to the calling application.

Related

How can I find context and start a new Activity from Android firebase ML-Kit BarcodeScannerProcessor onSuccess

I am using the quickstart-android code provided by google but after many attempts I cam unable to find a context that is not returning null. The BarcodeScannerProcessor is not itself an Activity, so I have attempted to create an instance of the LivePreviewActivity and use that as the context in the intent, but it's null.
The goal is to once a valid barcode is recognized I want to open a new activity that allows a user to verify value and on the push of a button call a webservice to post the barcode to a database via API. I am having a hard time finding a valid context and the app is crashing when it trys to execute the Intent.
Starting at line 97-107:
https://github.com/jamiekeefer/quickstart-android/blob/master/mlkit/app/src/main/java/com/google/firebase/samples/apps/mlkit/java/barcodescanning/BarcodeScanningProcessor.java
for (int i = 0; i < barcodes.size(); ++i) {
FirebaseVisionBarcode barcode = barcodes.get(i);
BarcodeGraphic barcodeGraphic = new BarcodeGraphic(graphicOverlay, barcode);
graphicOverlay.add(barcodeGraphic);
System.out.println(barcode.getRawValue());
if (!barcode.getRawValue().equals("") ) {
System.out.println("Got the number:" + barcode.getRawValue() + " Context: " + mContext); //OLD SCHOOL DEBUG OUTPUT
//enter code to start activity
Intent intent = new Intent(mContext, SendScannedBarcode.class);
String message = scannedBarcode;
intent.putExtra(EXTRA_MESSAGE, message);
mContext.startActivity(intent);
}
}
You can back up in the repo to see the instance of the LivePreviewActivity where I trying to get context.
I have tried a number of things and read about Context, Views and Activities and basically have completely confused myself. The only tuts I can find are using Kotlin, which is not helping clarify things.
I appreacite any help in indentifying or contruting a valid Intent from this Context. Thank you.
So I am assuming that in your LivePreviewActivity you are creating an object of the class BarcodeScanningProcessor. What you can do is change the constructor in the BarcodeScanningProcessor class to accept a context and then you pass in your LivePreviewActivity's context.
This is what the code should look like:
In BarcodeScanningProcessor:
public BarcodeScanningProcessor(Context context) {
// Note that if you know which format of barcode your app is dealing with, detection will be
// faster to specify the supported barcode formats one by one, e.g.
// new FirebaseVisionBarcodeDetectorOptions.Builder()
// .setBarcodeFormats(FirebaseVisionBarcode.FORMAT_QR_CODE)
// .build();
detector = FirebaseVision.getInstance().getVisionBarcodeDetector();
this.mContext = context;
}
Then in LivePreviewActivity:
In the particular case of your activity you would do:
case BARCODE_DETECTION:
Log.i(TAG, "Using Barcode Detector Processor");
cameraSource.setMachineLearningFrameProcessor(new BarcodeScanningProcessor(getApplicationContext()));
break;
Or if you just wanted to create an object of the class you could do:
BarcodeScanningProcessor bsp = new BarcodeScanningProcessor(getApplicationContext());
This should now give your BarcodeScanningProcessor class the context of your activity. Now, in BarcodeScanningProcessor, mContext should not be null and will have the context of your activity. I hope this answers your question.
try this create Application class
import android.app.Application;
public class MyApplication extends Application {
static MyApplication instance;
#Override
public void onCreate() {
super.onCreate();
instance=this;
}
public static MyApplication getInstance() {
return instance;
}
}
Register in manifest file
<application
..
android:name="com.yourpackage.MyApplication"
..>
.
.
.
</application>
start activity using this MyApplication.
Intent intent = new Intent(MyApplication.getInstance(), SendScannedBarcode.class);
String message = scannedBarcode;
intent.putExtra(EXTRA_MESSAGE, message);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MyApplication. getInstance().startActivity(intent);
Another way of handling the issue is create new constructor of BarcodeScanningProcessor which takes interface call back and once processing is done pass back result to caller.
public interface BarcodeUpdateListener {
#UiThread
void onBarcodeDetected(Barcode barcode);
}
private BarcodeUpdateListener callback;
public BarcodeScanningProcessor(BarcodeUpdateListener callback){
this.callback = callback;
detector = FirebaseVision.getInstance().getVisionBarcodeDetector();
}
Once you get the result pass result to caller
callback.onBarcodeDetected(<Barcode>)
You can get the context from graphicOverlay:
Context context = graphicOverlay.getContext();

Values of a List don't change programmatically

I have an List called messages property in my Activity.In the synchronization(),I called getDateMessage(upadated_at) function.In this function value of messages has changed but when program go to synchronization messages list is empty.
private List<message_model> messages = new ArrayList<>();
private void synchronization() {
getDateMessage(upadated_at);
Log.e("MSDF",messages.toString()+" list tostring");
}
private void getDateMessage(String date) {
MessengerActivity.APIInterface apiInterface = app_net.getRetrofitInstance().create(MessengerActivity.APIInterface.class);
retrofit2.Call<List<message_model>> call = apiInterface.getMessageDate(Ptoken, date);
call.enqueue(new Callback<List<message_model>>() {
#Override
public void onResponse(Call<List<message_model>> call, Response<List<message_model>> response) {
if(response.isSuccessful()) {
messages.addAll(response.body());
Log.e("MSDF",response.body().toString()+" responsebody in call");
Log.e("MSDF",messages.toString()+" message in call");
Log.e("MESSAGE", "getDateMessage successful");
}
}
#Override
public void onFailure(Call<List<message_model>> call, Throwable t) {
Log.e("MESSAGE", "getDateMessage" + t.toString());
}
});
}
And This is my logcat.
09-30 14:34:53.714 10763-10763/idea.mahdi.bime E/MSDF: [] list tostring
09-30 14:34:54.104 10763-10763/idea.mahdi.bime E/MSDF: [message_model{id=33, thread_id=2, user_id=15, body='چطوری', created_at='2018-09-29 10:28:26', updated_at='2018-09-29 10:28:26', deleted_at='null'}, message_model{id=30, thread_id=2, user_id=15, body='سلام', created_at='2018-09-29 09:30:40', updated_at='2018-09-29 09:30:40', deleted_at='null'}, message_model{id=7, thread_id=2, user_id=15, body='hi', created_at='2018-09-24 09:55:46', updated_at='2018-09-24 09:55:46', deleted_at='null'}] responsebody in api
09-30 14:34:54.104 10763-10763/idea.mahdi.bime E/MSDF: [message_model{id=33, thread_id=2, user_id=15, body='چطوری', created_at='2018-09-29 10:28:26', updated_at='2018-09-29 10:28:26', deleted_at='null'}, message_model{id=30, thread_id=2, user_id=15, body='سلام', created_at='2018-09-29 09:30:40', updated_at='2018-09-29 09:30:40', deleted_at='null'}, message_model{id=7, thread_id=2, user_id=15, body='hi', created_at='2018-09-24 09:55:46', updated_at='2018-09-24 09:55:46', deleted_at='null'}] message in api
09-30 14:34:54.104 10763-10763/idea.mahdi.bime
E/MESSAGE: getDateMessage successful
The problem is that when you call getDataMessage() it performs an asynchronous call (the retrofit enqueue() method). The server will be called to get the messages in a backgroud thread, while the android application will keep in the main thread.
Therefore, Log.e("MSDF",messages.toString()+" list tostring"); is called before the retrofit call is made, hence, there is no current data available yet. You should make sure that you are doing something with the data after it is completed loaded.
private List<message_model> messages = new ArrayList<>();
private void synchronization() {
getDateMessage(upadated_at);
// Anything you put here will be called before the data (messages) is loaded.
// Do not work with your messages here, they'll be null.
}
private void getDateMessage(String date) {
MessengerActivity.APIInterface apiInterface = app_net.getRetrofitInstance().create(MessengerActivity.APIInterface.class);
retrofit2.Call<List<message_model>> call = apiInterface.getMessageDate(Ptoken, date);
call.enqueue(new Callback<List<message_model>>() {
#Override
public void onResponse(Call<List<message_model>> call, Response<List<message_model>> response) {
if(response.isSuccessful()) {
messages.addAll(response.body());
Log.e("MSDF",response.body().toString()+" responsebody in call");
Log.e("MSDF",messages.toString()+" message in call");
Log.e("MESSAGE", "getDateMessage successful");
// Anything you want to do with the messages should be placed here. When you are sure the data is completed.
Log.e("MSDF",messages.toString()+" list tostring");
}
}
#Override
public void onFailure(Call<List<message_model>> call, Throwable t) {
Log.e("MESSAGE", "getDateMessage" + t.toString());
}
});
}
It's worth checking if (response.body() != null) before doing something with it to avoid NPE.
EDIT
As it was asked in the comments. A good solution (Google recommends it) is to fetch the data using a view model as described in this android dev guide article.
ViewModel approach is good because:
The data persist during configuration changes (for example, if you rotate your device, your list of messages will be still in your app).
It does not cause memory leaks.
You separate view data ownership from UI controller logic.
You can see the other advantages in the article.
1 - Add the view model dependecies in your build.gradle(Module:app) file
dependencies {
def lifecycle_version = "1.1.1"
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
}
See here the latest version.
2 - Create a ViewModel class
MessageViewModel.java
public class MessagesViewModel extends ViewModel {
private MutableLiveData<List<message_model>> messagesList;
public LiveData<List<message_model>> getMessages() {
if (messagesList == null) {
messagesList = new MutableLiveData<List<message_model>>();
loadMessages();
}
return messagesList;
}
private void loadMessages() {
MessengerActivity.APIInterface apiInterface = app_net.getRetrofitInstance().create(MessengerActivity.APIInterface.class);
retrofit2.Call<List<message_model>> call = apiInterface.getMessageDate(Ptoken, date);
call.enqueue(new Callback<List<message_model>>() {
#Override
public void onResponse(Call<List<message_model>> call, Response<List<message_model>> response) {
if(response.isSuccessful()) {
if (response.body() != null) {
messagesList.setValue(response.body());
}
}
}
#Override
public void onFailure(Call<List<message_model>> call, Throwable t) {
// Handle failure
}
});
}
}
3 - Get the messages in your activity
public class MainActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
MessagesViewModel model = ViewModelProviders.of(this).get(MessagesViewModel.class);
model.getMessages().observe(this, messagesList -> {
// Do whatever you want with the list of messages.
});
}
}
Look how clean your activity is now.
Then you can implement a SwipeRefreshLayout if you want to allow your users to refresh the data.
If it is not enough, you can check this ReposViewModel
Finally, if calling retrofit is the main core of your app that is going to be released to the public, you should introduce MVVM approach using Dagger 2 and RxJava, as described in this article. (This is advanced)

possible alternative to static inner classes to prevent memory leaks in android/java?

lately i have been researching about memory leaks in java/android and pretty much everywhere it says that instead of anonymous classes i should use static inner classes with weak references.
so, in my android app i started doing that but very quickly got tired of it because it's a lot of boilerplate code... i think have an alternative solution which i would prefer to use, but i'm juts not sure that it is a valid alternative to static inner classes in terms of preventing memory leaks. as i said before, i haven't seen this solution suggested anywhere else (all say to use static inner classes) so thats why im not sure my alternative will work.
ill use a simple example from my app:
i have a class called WebClient which handles asynchronous web requests and it accepts an interface called iCallback which returns the response from the server to the caller, and in my activity once i get this callback i need to dismiss a dialog, and maybe perform some activity related things (like trigger onBackPressed() and setResult()).
so here is my static inner class i have created:
private static class CallBack implements WebClient.ICallback
{
private WeakReference<ProgressDialog> mProgDiag;
private WeakReference<BaseActivity> mActivity;
public CallBack(BaseActivity activity, ProgressDialog progDiag)
{
this.mProgDiag = new WeakReference<>(progDiag);
this.mActivity = new WeakReference<>(activity);
}
#Override
public void onCallback(String data)
{
String responseAsString = Utils.extractStringFromResponse(...);
final BaseActivity parentActivity = mActivity.get();
ProgressDialog dialog = mProgDiag.get();
if(dialog != null)
{
dialog.dismiss();
}
if (responseAsString == null)
{
if(parentActivity != null)
{
Utils.makeServerErrorDialog(parentActivity,
new iDialogButtonClickedListener()
{
#Override
public void onDialogButtonClicked()
{
parentActivity.onBackPressed();
}
});
}
return;
}
//everything is ok
if (responseAsString.equals("1"))
{
if(parentActivity != null)
{
Intent result = new Intent();
result.putExtra(...);
parentActivity.setResult(Activity.RESULT_OK, result);
}
}
else
{
Utils.reportErrorToServer(...);
if(parentActivity != null)
{
parentActivity.setResult(Activity.RESULT_CANCELED);
}
}
if(parentActivity != null)
{
parentActivity.onBackPressed();
}
}
}
so for every variable i need in this static inner class i have to create a new weak reference, then retrieve the object itself, and then every time i want to access it i need to check whether it's null... that seems like a lot of code to me.
and here is my suggested alternative:
public abstract class BaseActivity extends AppCompatActivity
implements WebClient.ICallback
{
private static final String TAG = "BaseActivity";
WebClient.ICallback mCallBack;
ProgressDialog mProgDiag;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(...);
mCallBack = this;
//some code to invoke a server request on button click
//and passing mCallBack to the request
}
#Override
public void onCallback(String data)
{
String responseAsString = Utils.extractStringFromResponse(...);
mProgDiag.dismiss();
if (responseAsString == null)
{
Utils.makeServerErrorDialog(this,
new iDialogButtonClickedListener()
{
#Override
public void onDialogButtonClicked()
{
onBackPressed();
}
});
return;
}
//everything is ok
if (responseAsString.equals("1"))
{
Intent result = new Intent();
result.putExtra(...);
setResult(Activity.RESULT_OK, result);
}
else
{
Utils.reportErrorToServer(...);
setResult(Activity.RESULT_CANCELED);
}
onBackPressed();
}
#Override
protected void onPause()
{
mCallBack = null;
super.onPause();
}
#Override
protected void onResume()
{
super.onResume();
mCallBack = this;
}
}
to me this seems much cleaner: no creating and retrieving instances of weak references for every variable i need access to, i can directly invoke activity methods (e.g. onBackPressed()), and no checking for null everywhere.
the only place i would now have to check for null is inside WebClient class before invoking the callBack method.
so my question is, does this approach achieve the same result in terms of preventing memory leaks? is it a "worthy" alternative to static inner classes?
Unfortunately, your approach does not work. By implementing the WebClient.ICallback in your activity, rather than an inner class, you don't get rid of the leak. The leak happens not because the references to activity and dialog are implicit in an anonymous class, or in lambda, or in a non-static inner class instance; the happens when the WebClient keeps this reference while the activity is gone (it is not destroyed, because there is a strong reference to it).
The special mCallBack that you set to null when the activity is paused, gains nothing. Just as well, you can simply pass your activity instance to the WebClient. Now there is a strong reference to your activity, which is managed by someone (async handlers of the WebClient), who is not under your control. If you are unlucky, the async handler will get stuck somewhere and will never release this reference.
Please read this detailed explanation.
Note that WebView itself can cause a memory leak, if special measures are not undertaken!

Android Developer documentation IntentService: Are we defining the same class twice?

So I'm following this example in Android developers:
http://developer.android.com/training/run-background-service/create-service.html
Creating a background service with IntentService.
Note that we define the class RSSPullService in the first code example:
public class RSSPullService extends IntentService {
#Override
protected void onHandleIntent(Intent workIntent) {
// Gets data from the incoming Intent
String dataString = workIntent.getDataString();
...
// Do work here, based on the contents of dataString
...
}
}
In the following page, Reporting Work Status:
http://developer.android.com/training/run-background-service/report-status.html
I'm confused, are we defining the same class again to get the status?
public final class Constants {
...
// Defines a custom Intent action
public static final String BROADCAST_ACTION =
"com.example.android.threadsample.BROADCAST";
...
// Defines the key for the status "extra" in an Intent
public static final String EXTENDED_DATA_STATUS =
"com.example.android.threadsample.STATUS";
...
}
public class RSSPullService extends IntentService {
...
/*
* Creates a new Intent containing a Uri object
* BROADCAST_ACTION is a custom Intent action
*/
Intent localIntent =
new Intent(Constants.BROADCAST_ACTION)
// Puts the status into the Intent
.putExtra(Constants.EXTENDED_DATA_STATUS, status);
// Broadcasts the Intent to receivers in this app.
LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
...
}
Dont get confused,
both the classes are same
First one is to show how we create a service extending IntentService
Then they gave a example to send data to this IntentService
At last they gave example to shows how the same IntentService is returning result back.
Second code is just another example they changed the content of old intent service class
That's two separate examples, no need to define it twice, just use one definition. The code from first example (creating Intent Service) is just merged with code from Reporting Work example.

Get result from an activity after finish(); in an Android unit test

I'm currently writing some Android unit tests, and while I've gotten most things to work the way I want, one thing has left me kind of stumped.
I have the following code in my activity under test:
Intent result = new Intent();
result.putExtra("test", testinput.getText().toString());
setResult(Activity.RESULT_OK, result);
finish();
I'm trying to figure out how to use Instrumentation (or whatever) to be able to read the result of the activity, or get at the intent after the activity is finished.
Can anyone help?
You can use reflection and grab the values directly from the Activity.
protected Intent assertFinishCalledWithResult(int resultCode) {
assertThat(isFinishCalled(), is(true));
try {
Field f = Activity.class.getDeclaredField("mResultCode");
f.setAccessible(true);
int actualResultCode = (Integer)f.get(getActivity());
assertThat(actualResultCode, is(resultCode));
f = Activity.class.getDeclaredField("mResultData");
f.setAccessible(true);
return (Intent)f.get(getActivity());
} catch (NoSuchFieldException e) {
throw new RuntimeException("Looks like the Android Activity class has changed it's private fields for mResultCode or mResultData. Time to update the reflection code.", e);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Or you could also use Robolectric and shadow the Activity under test. Then, ShadowActivity provides you with methods to easily know if an Activity is finishing and for retrieving its result code.
As an example, one of my tests looks like this:
#Test
public void testPressingFinishButtonFinishesActivity() {
mActivity.onCreate(null);
ShadowActivity shadowActivity = Robolectric.shadowOf(mActivity);
Button finishButton = (Button) mActivity.findViewById(R.id.finish_button);
finishButton.performClick();
assertEquals(DummyActivity.RESULT_CUSTOM, shadowActivity.getResultCode());
assertTrue(shadowActivity.isFinishing());
}
You can do this by writing a special activity whose only purpose is to start the activity you are testing for result and save the result for you to assert correctness on.
For example, you could create an activity named ResultReceiverActivity. Give it three methods: getResultCode, getResultData, and getReceivedRequestCode, which can be used to verify that the tested activity returned the right values. You would create a test case that extends ActivityInstrumentationTestCase2 and the generic parameter would be ResultReceiverActivity. Calling getActivity will get you the activity instance.
public class ReturnedResultTest
extends ActivityInstrumentationTestCase2<ResultReceiverActivity> {
public void testReturnedResult() {
ResultReceiverActivity a = getActivity();
assertEquals(Activity.RESULT_OK, a.getResultCode());
assertEquals("myResult", a.getResultData().getStringExtra("test"));
assertEquals(0x9999, a.getReceivedRequestCode());
}
}
ResultReceiverActivity needs to override onActivityResult, of course, and should just store the values of that methods parameter in its fields, like so:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
this.receivedRequestCode = requestCode;
this.resultCode = resultCode;
this.resultData = data;
}
Of course, you may want to customize the activity that ResultReceiverActivity starts, and you can easily do that by using getIntent in its onCreate method. In your test case, call setActivityIntent before calling getActivity to set which Intent is used to start the activity.
I'm not sure if it is different for unit tests, but you should be able to use onActivityResult as seen here: StartingActivities. You just start the Activity with startActivityForResult(intent, requestCode) and then use
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
back in the activity that used startActivityForResult.

Categories