When using Android startActivityForResult, I don't have any guarantee about what I'll get in the Intent returned by onActivityResult.
I would like to define some kind of interface to limit the possibility of errors when transmitting data from an Activity to another (eg mistyped variable name).
Is there a way to do that? For example could I use something similar to the Android Interface Definition Language but between Activitys?
There are 2 scenarios when passing data between two activities A,B.
Activity A wants to pass data on Activity B ( through the startActivity Intent )
Activity B wants to return data on Activity A when it ends using setResult
on both cases i suggest to create some public static final variables for the extra keys to use.
For example if you need to pass an integer using the key "rating" from A to B i would probably do
class A extends Activity {
public static final String RESULT_STATUS = "RESULT_STATUS";
// Whatever ....
public void startB(int rating) {
Intent toStart = new Intent(this, B.class);
toStart.putExtra(B.EXTRA_RATING, rating);
startActivityForResult(toStart, 0);
}
public void onActivityResult(int requestCode /* 0 in our case */, int resultCode, Intent data) {
if (resultCode == RESULT_OK ) {
String returnedStatus = data.getStringExtra(RESULT_STATUS);
// Whatever ....
}
}
class B extends Activity {
public static final String EXTRA_RATING = "EXTRA_RATING";
public void onCreate(Bundle b) {
// Whatever ....
int rating = getIntent().getIntExtra(EXTRA_RATING,0);
}
// Whatever ....
public void returnDataAndFinish(String status) {
Intent result = new Intent();
result.putExtra(A.RESULT_STATUS, status);
setResult(RESULT_OK, result);
finish();
}
}
Related
So I've got to the point where my Android Activities and Fragments are getting a little messy. And I'd like to apply some good coding practice. However, every time I come up with a good SOLID design, Android gets in the way!
As a concrete example, I have a fragment that allows a user to update their details (name, email etc). It also allows them to take a picture to use as their profile picture.
I want to move this "picture taking" code out into a separate interface. Let's call it PhotoTaker. I then want a class called AndroidPhotoTaker that will implement this interface to start the correct Intent and return the filename of the picture taken.
public interface PhotoTaker {
Uri capturePhoto();
}
public class AndroidPhotoTaker implements PhotoTaker {
private Context _context;
#Inject
public AndroidPhotoTaker(Context context) {
_context = context;
}
#Override
public Uri capturePhoto() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(_context.getPackageManager()) != null) {
File photoFile = new File("some/path/to/a/file");
Uri photoUri = FileProvider.getUriForFile(_context, _context.getApplicationContext().getPackageName() + ".provider", photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
_context.startActivity(takePictureIntent);
return photoUri;
}
return null;
}
}
Assume that the correct context gets injected in via Dagger2.
My problem is that I can no longer call startActivityForResult() as I did in the fragment. And thus I no longer have the onActivityResult() callback to process the result. The only thing I can really find is _context.startActivity(), but that doesn't seem right.
And there's no callback -- I want to be able to get any extra data from these intents in the future.
How have other people solved this problem? What's the general solution here? i.e. how do people abstract away Android intent calls that have callbacks?
In my opinion abstracting away the calback handling makes the coding far too complicated: you have to implement your own
Activity-Baseclass that catches calls that android makes to onActivityResult() and forward these calls to your self-defined
IOnActivityResult interface that your hosting activity must implement.
Instead of this you can create a helper class with static method that creates and analyses Intents.
public class MyPhotoTakerClientActivity extends Activity {
private void onTakePhotoClicked() {
Intent takePictureIntent = PhotoTakerHelper.createIntent("some/path/where/to/store/the/resultfile.jpg");
if (PhotoTakerHelper.canProcess(this, takePictureIntent)) {
startActivityForResult(takePictureIntent, TakePhotoID);
} else {
... error processing
}
}
protected void onActivityResult(final int requestCode,
final int resultCode, final Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
switch (requestCode) {
case TakePhotoID :
Uri uri = PhotoTakerHelper.getPhotoUri(intent);
showPhoto(uri);
break;
...
In my apps all Activities have a static method showActivity that receives all parameters that can travel from the calling activity to sub-activity:
public class FotoGalleryActivity extends Activity {
public static void showActivity(Activity context, GalleryFilterParameter filter,
QueryParameter query, int requestCode) {
Intent intent = new Intent(context, FotoGalleryActivity.class);
if (filter != null) {
intent.putExtra(EXTRA_FILTER, filter.toString());
}
if (query != null) {
intent.putExtra(EXTRA_QUERY, query.toReParseableString());
}
if (requestCode != 0) {
context.startActivityForResult(intent, requestCode);
} else {
context.startActivity(intent);
}
}
}
The calling activity does not need to know how the parameters travel. All it needs is a call to FotoGalleryActivity.showActivity(getActivity(), getFilter(), null, ID_GALLERY);
In my main activity (where everything happens in my application) I call a variety as of now just two other activities which end up calling back to my MainActivity via button press. How do I distinguish between these two Intents back to my MainActivity? I have seperate operations I want to prefrom based on things I did back in the two seperate activites.
Heres what I tried:
Intent intent = getIntent();
String s_message = intent.getStringExtra(AppSettings.EXTRA_MESSAGE);
String f_message = intent.getStringExtra(ViewFavorites.EXTRA_MESSAGE);
if(s_message != null) {
//do something
} else if (f_message != null) {
//do something
}
But when I run my application I find when exiting the two activities that they are prefroming the methods I do not wish them to...am I going about this wrong?
What I do is simply set an Extra in my passing Intent then compare that. Something like this. When creating the Intent add an Extra to compare to
intent.putExtra("source", "appSettings");
then in your Activity check what that value is
Intent intent = getIntent();
String source = intent.getStringExtra("source"); // get that value here
if(s_message != null) {
if ("appSettings".equals(source)){
//do something
} else if (viewFavorites.equals(source)) {
//do something else
}
}
You could use variations of this as far as how you assign the Extra but this is a simple example that works well for me, especially when there are just a few Activites that will be calling this one.
Set a different ACTION on each intent, then use if(getIntent().getAction().equals(ACTION)) to distinguish between intents.
public class MainActivity extends Activity {
public static final String ACTION_ONE = "com.yourpackage.ACTION_ONE";
public static final String ACTION_TWO = "com.yourpackage.ACTION_TWO";
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if(intent.getAction() != null){
if(intent.getAction.equals(ACTION_ONE){
//DO SOMETHING
} else if (intent.getAction.equals(ACTION_TWO){
//DO SOMETHING
}
}
}
.....
}
Then when you start your main activity with an intent:
Intent intent = new Intent(MY_CURRENT_CONTEXT, MainActivity.class); //Or MainActivity subclass
add
intent.setAction(ACTION_ONE);
or whichever action is specific to what your intent is trying to accomplish.
I have creted a program that has 3 Activities: MainActivity, UpgradeActivity and UpgradesActivity.
Main Activity contains a timer and it also contains an instance of a Vehicle class.
public class MainActivity extends Activity {
TextView vehicleSpeed, vehicleName, vehicleDistance, vehicleLocation,
vehicleStatus, vehicleNews, vehicleInfo, vehicleMoney;
ProgressBar vehicleFuel;
public static Vehicle vehicle;
boolean launched;
public static PartType selectedType;
Handler handler = new Handler();
I have a button in MainActivity, that when pressed will take me to a page where i can select which part of the vehicle i wish to upgrade. For example i select: Engine. The engine Button takes me to the Upgrade Activity. In this activity i can buy the upgrade which should be applied to the vehicle in MainActivity. For the purpose of this question, lets say it set vehicles speed to +3.
My question is in regards to how to access the vehicle instance inside the MainActivity from the UpgradeActivity. I've tried making the instance static but that didn't work. How do i gain access and how can i change the vehicles variables from the other activities.
Here is where i am making the instance:
#Override
protected void onCreate(Bundle savedInstanceState) {
this.selectedType = PartType.Antenna;
this.launched = false;
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vehicle = new Vehicle();
vehicle.setupCar();
Here is where i am accessing the variable in Upgrades, it call the upgrade function inside of the Vehicle Class:
buyUp1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
MainActivity.vehicle.upgradeEngine(MainActivity.vehicle.engineLvl + 1);
Intent activityChangeIntent = new Intent(UpgradesActivity.this, MainActivity.class);
UpgradesActivity.this.startActivity(activityChangeIntent);
}
});
And this is the function within the Vehicle Class:
public void upgradeEngine(int lvl) {
engineLvl += 3;
engine = parts.getEngine(lvl);
}
The vehicle Stores an integer called: EngineLvl. This determines what level the cars engine is. The level is incremented by +3 everytime the engine is updated.
The problem is that the engine level never changes. Even if i make the Vehicle instance and all of the variables within vehicle STATIC;
MAINACTIVITY:
Vehicle
Button to UpgradesActivity
UPGRADESACTIVITY:
Button to UpgradeActivity
UPGRADEACTIVITY:
Change vehicle enginelvl Int
Button back to MainActivity
Main>Upgrades>Upgrade
Thank you for your time
Hard to say what is wrong without seeing the code, but I would advise you to use the Intent extras to move your data between activities. You will need to make your Vehicle class implement Parcelable (there is an example of how to implement Parcelable on that page, and countless others on the net). You pass your object as extra to the intent launching your UpgradeActivity like this :
Intent upgradeIntent = new Intent(this, UpgradeActivity.class);
upgradeIntent.putExtra("com.example.model.Vehicle", yourVehicleObject);
startActivityForResult(upgradeIntent, UPGRADE_CAR_REQUEST_CODE);
//UPGRADE_CAR_REQUEST_CODE is a unique private static final int
Then you can retrieve it in your UpgradeActivity onCreate method :
Intent intent = getIntent();
Vehicle vehicleFromLastActivity = intent.getParcelableExtra("com.example.model.Vehicle");
Before going back to your MainActivity you do something like this :
Intent dataIntent = new Intent();
dataIntent.putExtra("com.example.model.Vehicle", yourModifiedVehicleObject);
setResult(RESULT_OK, dataIntent);
finish();
Then finally in MainActivity you need to handle the result, it is done like this :
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case UPGRADE_CAR_REQUEST_CODE:
if(resultCode == android.app.Activity.RESULT_OK) {
Vehicle modifiedObject = data.getParcelableExtra("com.example.model.Vehicle");
// Now you can use that object which is coming from UpgradeActivity
}
}
}
Of course this could cause issues if your Vehicle class takes a lot of memory. You could then consider passing only the relevant information through the Intent to rebuild the object in the UpgradeVehicle Activity, for example only the id & name.
I am trying to send information from my main activity class to another class in my app package. The information is used to update an AlertDialog automatically. Here is the code I've been working with:
//This is in MainActivity.class
//Tests to see if SyncDialog will update itself with new intents.
public void testLoop() {
int n = 0;
Intent intent = new Intent(this, SyncDialog.class);
while (n != 10) {
intent.putExtra(TEST_NUMBER, n);
n++;
}
}
//This is in SyncDialog.class
//This method should get the int value from MainActivity
protected void onNewIntent(Intent intent) {
int n = intent.getIntExtra(TEST_NUMBER, 0);
showNextDialog(n);
}
TEST_NUMBER is defined as a public constant at the top of MainActivity, and I even tried importing MainActivity into SyncDialog.class.
Is there any way to fix this?
You will have to make it a static constant if you want to access it in the way you are attempting. In MainActivity:
public static String TEST_NUMBER = "test";
and in SyncDialog:
intent.getExtra(MainActivity.TEST_NUMBER, 0)
I have an activity which is a container for several fragments. One of the fragments starts another activity and from the second activity I want to send some data to one of the fragments. How can I do that? Basically the first activity stays beyond the second one and one of the EditViews will be updated with a new value when the second activity closes. I could've used an intent but how can I send it if the activity is already started? Thank you.
You would need to start your second activity using startActivityForResult(). In your second activity before you finish it, you need to add the data to a bundle pass this to an intent and then set the result to the intent.
Bundle bundle = new Bundle();
bundle.putString("myData", "myValue");
Intent intent = new Intent();
intent.putExtra(bundle);
setResult(intent, 0);
finish();
And then in activity 1 there should be an onactivityresult method which retrieves the value from the intent and sets it where you want in your fragment
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Bundle bundle = data.getData();
string value = bundle.getString("myData");
}
I'm not sure if I have it exactly right as remembering it at the top of my head but should be enough to get you started I think.
If you want to pass data back to its containing activity from your fragment, can do it by declaring an interface handler and through that interface pass the data. And ensure your containing activity implements those interfaces.
For example: In your fragment, declare this interface as follows :
public interface CallBackInterface {
public void onCallBack(String param);
}
//Declare this interface in your fragment
CallBackInterface callBk;
#Override
public void onAttach(Activity a) {
super.onAttach(a);
callBk= (CallBackInterface ) a;
}
Within your fragment, when you need to handle the passing of data, just call it on the "callBk " object:
public void callBack(String param) {
callBk.onCallBack(param);
}
Finally, in your containing activity which implements CallBackInterface ...
#Override
public void onCallBack(String param) {
Log.d("TAG","hi " + param);
}