Android - How to retrieve data from activity - android

I am new to android so please excuse the newbie question. I have a game I am trying to port from an old Java applet to android. My goal is to get this functional and then post an article on a site like CodeProject (or a better one if there are ones more appropriate). The idea is to show that a person brand new to android development can create an app in a reasonable amount of time.
I am making some progress but have run into a problem. I have the main activity in which the user interacts with. I then created a menu item that in turn starts a second activity (call it child) with a modest number of checkbox's, seekbar's etc to fill in parameters. I can successfully pass the class containing all the options from main to child. But I cannot get the child to pass this data back to the main.
First here is my main code that starts the child activity:
public void addBalls()
{
Intent myIntent = new Intent(this, GameOptions.class);
Bundle b = new Bundle();
b.putSerializable("options", gameParams);
myIntent.putExtras(b);
startActivityForResult(myIntent,STATIC_OPTIONS_VALUE);
}
The data passed to the child (and hopefully back again) is:
public class GameOptionParams implements Serializable
{
private static final long serialVersionUID = 1L;
public int speedBarPosition;
public int vgravityBarPosition;
public int mgravityBarPosition;
public int viscosityBarPosition;
public int restititionBarPosition;
public boolean trace;
public boolean collide;
public boolean mush;
public boolean wrap;
public boolean flicker;
}
And here is the expected return (again in main)
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode)
{
case (STATIC_OPTIONS_VALUE) :
{
if (resultCode == Activity.RESULT_OK)
{
//retrieve intended options
Bundle b = data.getExtras();
gameParams = (GameOptionParams) b.getSerializable("options");
}
break;
}
}
}
The child activity successfully receives the gameParams data. It then interacts with the user to update the values and then I attempt to return it but it does not seem to get sent to main. Here is the child code in the onStop() override.
Maybe this code should not be in the onStop() override but I can't determine where else to place it.
#Override
public void onStop()
{
super.onStop();
//read widget values
gameParams.speedBarPosition = speedBar.GetPosition();
gameParams.vgravityBarPosition = vgravityBar.GetPosition();
gameParams.mgravityBarPosition = mgravityBar.GetPosition();
gameParams.viscosityBarPosition = viscosityBar.GetPosition();
gameParams.restititionBarPosition = restititionBar.GetPosition();
//todo save to persistent
Intent resultIntent = new Intent(this, TiltBall2ImpactActivity.class);
Bundle b = new Bundle();
b.putSerializable("options", gameParams);
resultIntent.putExtras(b);
setResult(Activity.RESULT_OK, resultIntent);
}
Back in the main onActivityResult override I always see requestCode=0, resultCode=0, data=null. I assume this is a typical newbie problem, I have been reading the sdk documentation, user forums etc and have come close to a solution but just not quite there yet. Any help would be appreciated.

Since this is sort of a setting menu for the game, I assume you are going to need these values for more than one activity. If so you extend the android.app.Application class.
In that class you can create attributes to hold your values. In any activity you can call
MyApplication myApp = (MyApplication)getApplicationContext();
where myApp is a singleton. So you will get the values you set from another activity.
You will need to add this code to your application tag in the manifest file for it to work
android:name=".MyApplication"
If you need to keep these values for next startup of the application, you need to use SharedPreferences. This is a good tutorial for that
http://saigeethamn.blogspot.com/2009/10/shared-preferences-android-developer.html

Assuming in your 'child' activity, the user has to press an 'OK' or 'Save' button then put the code to set the gameParams parameters in the button's onClick(...) handler.
Use the default constructor for instantiating the Intent, example...
Intent resultIntent = new Intent();
...then after creating the Bundle and adding gameParams to it and calling setResult(...), simply call finish() to terminate the 'child' activity. There aren't many occasions that I can think of to override onStop() and I suspect you don't want to be using it to attempt returning the Intent.

Related

What is the right way to hold and restore the data temporarily between activities in android?

I am creating an Android app. One of the functions is to collect some data (item name, item ID and the barcode string) from the user .
Activity1 is a form. User enters the item name and item number manually. For the barcode string, user clicks on the "scan" button then Activity2 (Scanner) is started in order to scan and read the barcode. Once the barcode is read, Activity1 (the form) starts again and all data should appear on the form.
When Activity2 starts by Intent, Activity1 is killed. So, I have to get the item name and item number and store them temporarily before staring the Intent. Then when Activity1 starts again, those data will be rendered on the form again.
Now I am thinking to use Intent Extra to keep the item name and number, and pass them to Activity2 and back to Activity1. Given that Activity2 doesn't need those data, I wonder if that is the right way to do in this scenario. Is there any better way? Should I use Shared Preferences instead?
In Your first activity use put extra argument to intent like this:
// Assuming Activity2.class is second activity
Intent intent = new Intent(this, Activity2.class);
intent.putExtra("variable_name", var); // here you are passing var to second activity
startActivity(intent);
Then in second activity retrieve argument like this:
String var2 = getIntent().getStringExtra(variable_name);
You could create a singleton class and expose setter(for saving) and getter (for retrieving) methods for the model objects (here two private string variables).This class will be alive with your application:
public class MyClass{
private static MyClass instance=null;
public static getInstance(){
if(instance==null){
instance=new MyClass();
}
return instance;
}
private String itemName;
private String itemNumber;
//setter and getter methods here
}
why need to kill the Activity 1, try to call
on Activity 1
declare private int SCAN_BARCODE_REQUEST = 101;
and then
//finish(); dont use this to destroy activity 1
startActivityForResult(new intent(this,Activity2.class), SCAN_BARCODE_REQUEST);
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == SCAN_BARCODE_REQUEST) {
if (resultCode == RESULT_OK) {
String barcode = data.getStringExtra("BARCODE");
//handle your barcode string here
}
}
}
on your Activity 2,
change your start Activity1 with
Intent intent = new Intent();
intent.putExtra("BARCODE", barcodeString);
setResult(RESULT_OK, intent);
finish();
You can use SharedPreferences.
You can learn how to use them here:
https://www.tutorialspoint.com/android/android_shared_preferences.htm
https://developer.android.com/training/basics/data-storage/shared-preferences.html
SharedPreferences is a really good solution for such applications. It is very simple and easy to use and implement.

OnActivityResult when having GUI parts outside Fragment

I have a two pane fragment design with the detail fragment dealing with the item clicked on the leftmost, listfragment.
Since I have many different list items, they have different GUI's each one of them, I want to put the handling of the GUI parts in different separate classes.
I'm using reflections to get the code slimmed inside the ItemDetailFragment:(code below is stripped down)
*//get class gui_handler for the object and get its constructor:*
Constructor<?> ctor = mListItem.getmGuiHandler().getConstructor(View.class);
*//create an object of the gui_handler class, pass the rootView as arg:*
gui_handler_base handlerObject = (gui_handler_base) ctor.newInstance(root);
*//run setup-method containing findViewById() and more GUI related stuff:*
handlerObject.setupGUI(mListItem, getActivity());
In setupGUI:
public void setupGUI(MyListItem item, final Activity activity) {
buttonRun.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
final String FILENAME = "XXX";
Intent I = new Intent(activity,KTActivity.class);
Bundle b = new Bundle();
b.putSerializable("FileName",FILENAME);
I.putExtras(b);
activity.startActivityForResult(I,1);
return;
}
});
#Override
public void onActivityResult(int requestCode, int resultCode,
Intent data) {
// I WANT TO END UP HERE WHEN THE ACTIVITY RETURNS;
}
I have one onActivityResult method in the gui_handler_base class and one in the ItemDetailFragment, but none of them are called.
How can I do this??
Your question is quite ambiguous. Why are you using onActivityResult? activity.startActivityForResult(I,1); why? WHy not just implement the onclicklistener class in your fragment class and be done with it? It seems like in trying to reduce code you created more headaches for yourself. Also take out numbers in your code and use constants. You have to call the intent and use the intent in the same fragment. Further more why are you extracting out the GUI functionality from the fragments? You shouldn't be. even if you are creating custom functionality, you would still deal with a fragment's functionality in the fragment.
//if you want to try this… normally when receiving a camera intent this is what you can do.
if (resultCode == Activity.RESULT_OK) {
switch (requestCode) {
case yourResultCode:
doSomethingWithYourResult;
break;
]

Accessing an Instance's Variable from another Activity - Android Developement

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.

How to always start from a startup activity on Android?

There are three different cases:
1) A user launches an app, navigates in it, pressed home and click on the app icon again to launch our app again.
2) A user launches an app, navigates in it, presses home, chooses recent and click on the app to launch our app again.
3) A user launches an app, navigates in it, click something in the app (TextView with a link), which calls another app (as example Email) and user clicks back button, which bring us back to our app.
I know about flag "clearTaskOnLaunch" flag, it solves case #1.
I know about about flag "excludeFromRecents", it solves case #2 (may be not the most user friendly solution, but it works).
What about case #3? I have a workaround right now. However, I will have to put it on all activities which can be lead to another app. I wonder, whether there is better way to solve it (without handling it in all such activities).
This should be handled on the Application level.
For API level 14, you can register an ActivityLifeCycleCallback in your Application class
public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)
You can use it, to know on an Application level, which activities are destroyed, paused, resumed etc etc. Whenever, an activity is paused, without a new activity being created/resumed, you should clear the Activity stack, and re-launch your startActivity
If you target SDK versions < 14, you should implement your own method, to know which activities are created/resumed and paused, and do the same whenever an activity is paused, without a new activity being created/resumed
1) define a public static normalPause = true variable in a Class.
2) in onPause method of all of your activities set it false (I am worry. We might not be in a normal pause)
2) in onCreate method of all of your activities set it true (Do not worry. We are in a normal pause)
3) in onResume of all of your Activities:
if(!Utilities.normalPause)
{
this.finish()
}
Enjoy!
It seems a similar question has already been asked. It sounds like the OP came up with a working solution. How do I collapse "child activities"?
EDIT:
Instead of using a button you can use a boolean to tell whether or not you need to collapse back to the main activity. Have your root activity extend from Activity and the child activities extend from CollapsableActivity. To get this to work in all cases I added startOutsideActivity() and startOutsideActivityForResult().
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class CollapsableActivity extends Activity {
private boolean returnToRoot;
public static final int COLLAPSE_BACK = -1; // something other than RESULT_CANEL (0)
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
returnToRoot = true;
}
#Override
protected void onStart() {
super.onStart();
returnToRoot = true;
}
#Override
protected void onRestart() {
super.onRestart();
// start collapsing the stack
if (returnToRoot) {
setResult(COLLAPSE_BACK);
finish();
}
}
#Override
public void startActivityForResult(Intent intent, int requestCode) {
super.startActivityForResult(intent, requestCode);
returnToRoot = false;
}
public void startOutsideActivityForResult(Intent intent, int requestCode) {
super.startActivityForResult(intent, requestCode);
returnToRoot = true;
}
#Override
public void startActivity(Intent intent) {
// call startActivityForResult to make sure and catch the collapse condition
super.startActivityForResult(intent, 0);
returnToRoot = false;
}
public void startOutsideActivity(Intent intent) {
super.startActivity(intent);
returnToRoot = true;
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == COLLAPSE_BACK) {
returnToRoot = true;
}
}
}
This worked properly for me in all cases you listed. The only difference is you need to call startOutsideActivity() or startOutsideActivityForResult() when you navigate away from you app. Personally, I think this adds clarity to your intentions. Hope it helps!
I know you don't want to manage it in all activities but you can do this and still handle the code in one place with a super activity
public abstract class BlundellActivity extends Activity {
#Override
public void onPause(){
// Whatever strategy you want
}
}
public class SomeActivity extends BlundellActivity {
// Do whatever you normally want to do
}
public class SomeActivity extends BlundellActivity {
// Do whatever you normally want to do here as well
}
Perhaps, android:noHistory is what you're looking for. If you declare all your activities except StartupActivity with this attribute, then they will be finished as the user navigates away from them and only StartupActivity will appear.
You can try this steps:
use one boolean static flag isFinish in StartupActivity with default false value.
in onCreate() of StartupActivity set isFinish value to false.
write below code in onResume() method of all activities in your project.
if(isFinish)
{
finish();
}
set isFinish value to true when you open any native app like email, browser etc.
or
5 . set isFinish value to true in onBackPress() method whenever you want to close application on back press.
Case 6: if android browser open on clicking on any link then use below code is onPause() method
if(isBrowserRunning("com.android.browser"))
{
isFinish = true;
finish();
}
////////////////
private boolean isBrowserRunning(String processName)
{
ActivityManager manager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
String packageName = manager.getRunningTasks(1).get(0).topActivity.getPackageName();
Log.i("LogTest", "Current process package name: " + packageName);
return processName.equalsIgnoreCase(packageName);
}
You can create a sample project to know other browser package name like opera mini, US browser etc.
add below permission in manifest:
<uses-permission
android:name="android.permission.GET_TASKS" />
You can call this.finish() on the onPause() of your Activity, that way the activity will be closed in the three cases.
You need to use bundle and pass appropriate parameter/or parameters from the calling app (i.e. click something in the app (TextView with a link)).
Retrieve the parameter in the called app (Email app).
You can send the name of the activity in the parameter.
Now being in Email app(the called app) Click of back button navigate back to your calling application.
Optionally you can save the state of activity from the caller program, as required.
You need to use Bundle, and Intent to implement this logic.
Code snippet:
In the calling program, we need to store parameters/data required for back button functionality in the called program.
Bundle bndleData = new Bundle();
Use putString(), putInt() methods of Bundle class.
String prefix = getPackageName().toString();
(this prefix can be stored in application level constants.java file as applicable)
bndleData.putString("ParentActivity", this.getLocalClassName());
Also store additional parameters if required
bndleData.putString("paramName", valueofParamName);
bndleData.putInt("IntChannelImage", chImageInt);
Intent intent = new Intent(v.getContext(), AMRChannelPlayer.class);
intent.putExtra(prefix + "bndleChnlData", bndleData);
startActivity(intent);
Caller Program:
Retrive the data, activity nae from bundle and use it in back button implementation:
prefix = getPackageName().toString();
Bundle extras = getIntent().getBundleExtra(prefix + "bndleData");
String parentActivity = extras.getString("ParentActivity");
extras.getString("paramName");
I hope this helps you.
Instead of using multiple solutions you can use a single one that solves all the problems.
Check this answer:
https://stackoverflow.com/a/8576529/327011
With a Broadcast and BroadcastReceivers in each activities of your application you can kill all activities whenever your application goes to background.
UPDATE:
To detect if your application when to background you can use onStop, check this to understand the theory: Activity side-by-side lifecycle
And this is the implementation: https://stackoverflow.com/a/5862048/327011
I think this is all you need :-)

Custom View calling startActivityForResult

I created custom compound view where I incorporate functionality to take pictures.
I'm calling it like this (from view):
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
((Activity)mContext).startActivityForResult(intent, index);
This part works good. What I don't know how to do is how do I implement onActivityResult inside my custom view?
Or should I catch this inside Activity and than re-route into my view? Doesn't look like very nice solution..
You actually can do it like this:
#Override
public void onClick(View v) {
final FragmentManager fm = ((FragmentActivity) getContext()).getSupportFragmentManager();
Fragment auxiliary = new Fragment() {
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
//DO WHATEVER YOU NEED
super.onActivityResult(requestCode, resultCode, data);
fm.beginTransaction().remove(this).commit();
}
};
fm.beginTransaction().add(auxiliary, "FRAGMENT_TAG").commit();
fm.executePendingTransactions();
auxiliary.startActivityForResult(new Intent(getContext(), ToStartActivity.class), 3333);
}
The trick is using an auxiliary temp fragment.
I'm having the same issue, as the initial question. I know that you all posted working solution, BUT, all the solutions lack one thing: encapsulation. What do I mean - If in one activity I have 10 views that should (on some event) start another activity it should be NORMAL to be able to start that new activity from the view that needs that activity. You all are trying to convince that is better to handle all new possible activites from the initial one - than why we added different logic in each view. We may want to RE-USE code, and create one custom view that can work INDEPENDENT to where we use it (work may include showing another activity to select something for example).
I know that this is not possible (or not yet), and is a clear proof that Android SDK is not ready yet to handle real big applications.
If you want an example:in any real business app that has for example, customer list (that should be a view) ,the view should be able to launch by itself addcustomer activity, edit customer activity and so on, independent from where you put that customer list view (control) - because in big apps you need to RE-use components (you may need to show the customer list control in a order product activity, in a timesheet activity and so on.).
One possible solution could be:
- start the new activity (using the view context (normally should be the parent activity).
- on the new activity closing event, either call directly a method in the calling view (depending on the case, and posibilities: either static that is handling the code that you normally would run on activityresult, either try to pass the instance of the calling view to the new activity, and do the same. In this way, you can handle your new activity, without letting the containing activity to know anything about it.
You need to catch this from your activity. The startActivityForResult is called on your activity, so it'll be the one launching the Intent and getting the result. I'd say that it's overall bad to launch it directly from the view's code. A better solution would be with a clickListener (or checkChangeListener, or whatever you want), set by your activity, and calling a method like "openImageCapture".
When the Intent returns, your activity will take care of the result and update your views as needed.
Views are there just for displaying stuff on the screen and getting user input, the activity is there to do the actual work.
Here's a static function to implementing #riwnodennyk's solution, while overcoming the Fragment must be static and not in anonymous class error:
public static void myStartActivityForResult(FragmentActivity act, Intent in, int requestCode, OnActivityResult cb) {
Fragment aux = new FragmentForResult(cb);
FragmentManager fm = act.getSupportFragmentManager();
fm.beginTransaction().add(aux, "FRAGMENT_TAG").commit();
fm.executePendingTransactions();
aux.startActivityForResult(in, requestCode);
}
public interface OnActivityResult {
void onActivityResult(int requestCode, int resultCode, Intent data);
}
#SuppressLint("ValidFragment")
public static class FragmentForResult extends Fragment {
private OnActivityResult cb;
public FragmentForResult(OnActivityResult cb) {
this.cb = cb;
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (cb != null)
cb.onActivityResult(requestCode, resultCode, data);
super.onActivityResult(requestCode, resultCode, data);
getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
}
}
Usage example:
Intent inPhonebook = new Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
myStartActivityForResult((FragmentActivity) getContext(),
inPhonebook, REQUEST_CODE_PICK_CONTACT, this::onContacts);
There is no way to catch onActivityResult from your view, only from Activity.
And its not safe to assume that's Context object is Activity. In general you should not rely on this fact. Even if it seems reasonable in case with views, you still should use only methods available trough Context interface. That's because your can't predict all side-effects on the Activity, when you're calling Activity specific functions.
Just make the same method inside your custom view
And inside the activitys onActivityResult call yourView.onActivityResult(...) and process the result inside your view..
Also as guys mentioned you must not always end up with Context being of Activity class. Usually when it is from inflated view.
But if you construct your view only in code and always use the activity instance you are good.

Categories

Resources