I have a Activity in which when i click on refresh action bar icon it starts an async task. I have callbacks of async task in mainactity. in preexecute i am displaying progressbar on action bar and in postexecute i am displaying back refresh item. But when there is screen rotation after async task is started, onpostexecute runs but it is not calling invalidate options menu.
please help me out.
here is my MainActivity:
package in.cdac.enbee;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
public class MainActivity extends Activity implements RefreshTask.TaskCallbacks {
// Our created menu to use
private Menu mymenu;
volatile static boolean isrefreshing=false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
Log.d("Debug", "OncreateOptionsMenu");
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
// We should save our menu so we can use it to reset our updater.
mymenu = menu;
if(isrefreshing) {
menu.findItem(R.id.action_refresh).setActionView(R.layout.action_progressbar);
} else {
menu.findItem(R.id.action_refresh).setActionView(null);;
}
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
switch(item.getItemId()) {
case R.id.action_refresh:
// Do animation start
new RefreshTask(this).execute();
return true;
case R.id.action_settings:
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
protected void onSaveInstanceState (Bundle savedInstanceState) {
//Log.d("Debug", "OnSave");
//Always call superclass first
super.onSaveInstanceState(savedInstanceState);
// Restore value of members from saved state
//savedInstanceState.putBoolean("isRefreshing", isrefreshing);
}
#Override
protected void onRestoreInstanceState (Bundle savedInstanceState) {
//Log.d("Debug", "OnRestore");
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
// Restore value of members from saved state
//isrefreshing = savedInstanceState.getBoolean("isRefreshing");
} else {
// Probably initialize members with default values for a new instance
//isrefreshing = false;
}
}
#Override
public void onPreExecute() {
// TODO Auto-generated method stub
isrefreshing = true;
invalidateOptionsMenu();
}
#Override
public void onCancelled() {
// TODO Auto-generated method stub
isrefreshing = false;
invalidateOptionsMenu();
}
#Override
public void onPostExecute(Boolean done) {
// TODO Auto-generated method stub
isrefreshing = false;
if(done) {
Toast.makeText(this, "Done refreshing", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Downloading Failed", Toast.LENGTH_SHORT).show();
}
invalidateOptionsMenu();
}
}
and my async task:
package in.cdac.enbee;
import android.os.AsyncTask;
public class RefreshTask extends AsyncTask<Void, Void, Boolean> {
/**
*
*/
static interface TaskCallbacks {
void onPreExecute();
void onCancelled();
void onPostExecute(Boolean done);
}
private TaskCallbacks mCallbacks;
public RefreshTask(TaskCallbacks mCallbacks) {
this.mCallbacks = mCallbacks;
}
#Override
protected void onPreExecute() {
mCallbacks.onPreExecute();
}
#Override
protected void onCancelled() {
if (mCallbacks != null) {
mCallbacks.onCancelled();
}
}
#Override
protected Boolean doInBackground(Void... nope) {
try {
// Set a time to simulate a long update process.
Thread.sleep(4000);
return true;
} catch (Exception e) {
return false;
}
}
#Override
protected void onPostExecute(Boolean done) {
if (mCallbacks != null) {
mCallbacks.onPostExecute(done);
}
}
}
and my layout file for progressbar:
<?xml version="1.0" encoding="utf-8"?>
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</ProgressBar>
Thank you.
You are passing your Acitvity object to AsyncTask. This approach does not work. Basically android system can destroy and later recreate your activity at any time (one example is when orientation changes) and you have to be prepared for this situation.
What you are seeing now is that your code is being called but it uses old activity, not the one created after orientation change, so your code has no effect.
I do not know if this is the proper way or not, but I did have a little workaround and it works.
I am creating an reference to RefreshTask Async task in mainactivity. Initially making it null.
static RefreshTask rt=null;
then added a method in async task class.
public void updateobject(TaskCallbacks mCallbacks) {
this.mCallbacks = mCallbacks;
}
and in mainactivity, in onRestoreInstanceState, calling that method.
#Override
protected void onRestoreInstanceState (Bundle savedInstanceState) {
//Log.d("Debug", "OnRestore");
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
if(rt != null) {
rt.updateobject(this);
}
}
Please let me know what you think.
Also, suggest me an alternative way of doing it.
Related
I've been using AsyncTasks for a while however, I've recently encountered a scenario where I'm unsure of how to handle correctly. Since I thought it would be a somewhat common scenario I decided to ask the question here.
So, I'm trying to use an AsyncTask to make a simple call to sign a user in to the app. After the call completes, if it succeeds, the user should be taken to another activity. This logic is simple. The problem arrises when the user navigates away from the app before the sign in call returns. In such a case, what should I do in onPostExecute()?
What I've seen some apps do is they continue with the call anyways, as long as the activity is still around, and will launch the next activity. However this creates a weird experience where the user navigates away from the app, then several seconds later, the app just pops back up in their face. Of course, I would like to avoid doing this.
Update
Example code:
public class ExampleActivity extends Activity {
private boolean mIsPaused;
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
Button btnSignIn = (Button) findViewById(R.id.btn_sign_in);
btnSignIn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
new SignInTask(ExampleActivity.this).execute();
}
});
...
}
#Override
protected void onPause() {
super.onPause();
mIsPaused = true;
}
#Override
protected void onResume() {
super.onResume();
mIsPaused = false;
}
private boolean isPaused() {
return mIsPaused;
}
...
private static class SignInTask extends AsyncTask<Void, Void, SomeResult> {
private final WeakReference<ExampleActivity> mAct;
public SignInTask(ExampleActivity act) {
mAct = new WeakReference<ExampleActivity>(act);
}
#Override
protected SomeResult doInBackground(Void... params) {
return mApi.signIn(creds);
}
#Override
protected void onPostExecute(SomeResult result) {
if (result.getCode() == OK) {
ExampleActivity act = mAct.get();
if (act != null) {
if (act.isPaused()) {
// do something
} else {
startActivity(new Intent(act, NextActivity.class));
}
} else {
// do something
}
}
}
}
}
made your AsyncTask class as static inner class.
Pretty interesting problem... Going with what you've started by using booleans, you could save the response the Activity receives to the SharedPreferences in the event it is paused, or continue processing normally if it is not. If the Activity later resumes (or is recreated), check whether or not there is a saved response and handle accordingly. I was thinking something along the lines of:
import org.json.JSONObject;
import android.app.Activity;
import android.os.Bundle;
public class TaskActivity extends Activity {
private static final String KEY_RESPONSE_JSON = "returned_response";
private boolean paused = false;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
// don't setup here, wait for onPostResume() to figure out what to do
}
#Override
public void onPostResume(){
super.onPostResume();
paused = false;
if(isSavedResponseAvailable()) processResponse(getSavedResponse());
else setup();
}
#Override
public void onPause(){
paused = true;
super.onPause();
}
private void setup(){
// normal setup
}
public void onReceiveResponse(JSONObject response){
if(paused) setSavedResponse(response);
else processResponse(response);
}
private void processResponse(JSONObject response){
// Continue with processing as if they never left
getSharedPreferences(this.getClass().getName(), 0).edit().clear().commit(); // Clear everything so re-entering won't parse old data
}
private boolean isSavedResponseAvailable(){
return getSavedResponse() != null;
}
private JSONObject getSavedResponse(){
try{
return new JSONObject(getSharedPreferences(this.getClass().getName(), 0).getString(KEY_RESPONSE_JSON, ""));
}
catch(Exception e){ }
return null;
}
private void setSavedResponse(JSONObject response){
getSharedPreferences(this.getClass().getName(), 0).edit().putString(KEY_RESPONSE_JSON, response.toString()).commit();
}
}
Clearly that's assuming your response from the task is JSON, but there's no reason you couldn't extend that to save the data individually and rebuild the necessary response object from the saved preference data.
As far as clean approaches go, though... I give this about a 3/10, but I can't think of anything better (well, other than making the TaskActivity abstract and forcing implementations to override setup(), processResponse(), isResponseAvailable(), getSavedResponse(), and setSavedResponse(), but that would only be mildly better for like a 4/10)
I would suggest putting a try/catch statement in the post execute - as far as I know what would happen in this situation is that you would get some kind of Window Manager exception.
What I would STRONGLY recommend, however, is stopping any async tasks (with the cancel method) on the onPause method, meaning that you won't interrupt them.
http://developer.android.com/reference/android/os/AsyncTask.html#cancel(boolean)
public final boolean cancel (boolean mayInterruptIfRunning)
Added in API level 3
Attempts to cancel execution of this task. This attempt will fail if the task has already completed, already been cancelled, or could not be cancelled for some other reason. If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.
Calling this method will result in onCancelled(Object) being invoked on the UI thread after doInBackground(Object[]) returns. Calling this method guarantees that onPostExecute(Object) is never invoked. After invoking this method, you should check the value returned by isCancelled() periodically from doInBackground(Object[]) to finish the task as early as possible.
Parameters
mayInterruptIfRunning true if the thread executing this task should be interrupted; otherwise, in-progress tasks are allowed to complete.
Returns
false if the task could not be cancelled, typically because it has already completed normally; true otherwise
See Also
isCancelled()
onCancelled(Object)
boolean isRunning; //set it to true in onResume, and false in onStop
boolean isWaiting; // set it to true in onPostExecute, if "isRunning" is false
check in onResume whether isWaiting is true, if yes, take user to another screen.
Use the cancel() of AsynchTask class onBackPress() of Activty class
public class ExampleActivity extends Activity {
private boolean mIsPaused;
SignInTask singleTaskObj;
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
Button btnSignIn = (Button) findViewById(R.id.btn_sign_in);
btnSignIn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
singleTaskObj = new SignInTask(ExampleActivity.this).execute();
}
});
...
}
#Override
protected void onPause() {
super.onPause();
mIsPaused = true;
}
#Override
protected void onResume() {
super.onResume();
mIsPaused = false;
}
protected void onBackPressed()
{
singleTaskObj.cancel();
}
private boolean isPaused() {
return mIsPaused;
}
...
private static class SignInTask extends AsyncTask<Void, Void, SomeResult> {
private final WeakReference<ExampleActivity> mAct;
public SignInTask(ExampleActivity act) {
mAct = new WeakReference<ExampleActivity>(act);
}
#Override
protected SomeResult doInBackground(Void... params) {
return mApi.signIn(creds);
}
#Override
protected void onPostExecute(SomeResult result) {
if (result.getCode() == OK) {
ExampleActivity act = mAct.get();
if (act != null) {
if (act.isPaused()) {
// do something
} else {
startActivity(new Intent(act, NextActivity.class));
}
} else {
// do something
}
}
}
}
}
It's probably just a silly mistake but I cannot fix it. I have a Google+ sign in a button, which is shown when the user is not logged in. I also have a sign out button, which is GONE when the user is logged in.Everything works except that when I go back to the activity (onResume) I can see the red Google+ button for about a second and than it gets hidden and the sign out button appears. How can I remove this one second during which I can still see the Google+ button ?
This is my layout:
XML code:
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:id="#+id/startGameView"
android:src="#drawable/play"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="#+id/startGameView"
android:layout_centerHorizontal="true"
android:layout_marginBottom="46dp">
<!-- show achievements -->
<Button
android:id="#+id/show_achievements"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Achievements"/>
<!-- show leaderboards -->
<Button
android:id="#+id/show_leaderboard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Leaderboard"/>
</LinearLayout>
Code in the activity:
public class StartActivity extends BaseGameActivity implements View.OnClickListener{
private ImageView mPlay;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
mPlay = (ImageView)findViewById(R.id.startGameView);
mPlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//play animations
YoYo.with(Techniques.Pulse)
.duration(200)
.playOn(findViewById(R.id.startGameView));
Intent intent = new Intent(StartActivity.this, MainActivity.class);
startActivity(intent);
}
});
findViewById(R.id.sign_in_button).setOnClickListener(this);
findViewById(R.id.sign_out_button).setOnClickListener(this);
findViewById(R.id.show_achievements).setOnClickListener(this);
findViewById(R.id.show_leaderboard).setOnClickListener(this);
//mSignOutButton = (Button)findViewById(R.id.sign_out_button);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_start, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onSignInFailed() {
findViewById(R.id.sign_in_button).setVisibility(View.VISIBLE);
findViewById(R.id.sign_out_button).setVisibility(View.GONE);
}
#Override
public void onSignInSucceeded() {
findViewById(R.id.sign_in_button).setVisibility(View.GONE);
findViewById(R.id.sign_out_button).setVisibility(View.VISIBLE);
}
#Override
public void onClick(View view) {
if (view.getId() == R.id.sign_in_button) {
beginUserInitiatedSignIn();
}else if (view.getId() == R.id.sign_out_button) {
signOut();
findViewById(R.id.sign_in_button).setVisibility(View.VISIBLE);
findViewById(R.id.sign_out_button).setVisibility(View.GONE);
}else if (view.getId() == R.id.show_achievements){
Toast.makeText(StartActivity.this,"achivements",Toast.LENGTH_SHORT).show();
startActivityForResult(Games.Achievements.getAchievementsIntent(getApiClient()), 1);
}else if(view.getId() == R.id.show_leaderboard){
Toast.makeText(StartActivity.this,"leaderboard",Toast.LENGTH_SHORT).show();
startActivityForResult(Games.Leaderboards.getLeaderboardIntent(
getApiClient(), getString(R.string.number_of_solved_math_problems_leaderboard)), 2);
}
}
BaseActivity code:
public abstract class BaseGameActivity extends FragmentActivity implements
GameHelper.GameHelperListener {
// The game helper object. This class is mainly a wrapper around this object.
protected GameHelper mHelper;
// We expose these constants here because we don't want users of this class
// to have to know about GameHelper at all.
public static final int CLIENT_GAMES = GameHelper.CLIENT_GAMES;
public static final int CLIENT_APPSTATE = GameHelper.CLIENT_APPSTATE;
public static final int CLIENT_PLUS = GameHelper.CLIENT_PLUS;
public static final int CLIENT_ALL = GameHelper.CLIENT_ALL;
// Requested clients. By default, that's just the games client.
protected int mRequestedClients = CLIENT_GAMES;
private final static String TAG = "BaseGameActivity";
protected boolean mDebugLog = false;
/** Constructs a BaseGameActivity with default client (GamesClient). */
protected BaseGameActivity() {
super();
}
/**
* Constructs a BaseGameActivity with the requested clients.
* #param requestedClients The requested clients (a combination of CLIENT_GAMES,
* CLIENT_PLUS and CLIENT_APPSTATE).
*/
protected BaseGameActivity(int requestedClients) {
super();
setRequestedClients(requestedClients);
}
/**
* Sets the requested clients. The preferred way to set the requested clients is
* via the constructor, but this method is available if for some reason your code
* cannot do this in the constructor. This must be called before onCreate or getGameHelper()
* in order to have any effect. If called after onCreate()/getGameHelper(), this method
* is a no-op.
*
* #param requestedClients A combination of the flags CLIENT_GAMES, CLIENT_PLUS
* and CLIENT_APPSTATE, or CLIENT_ALL to request all available clients.
*/
protected void setRequestedClients(int requestedClients) {
mRequestedClients = requestedClients;
}
public GameHelper getGameHelper() {
if (mHelper == null) {
mHelper = new GameHelper(this, mRequestedClients);
mHelper.enableDebugLog(mDebugLog);
}
return mHelper;
}
#Override
protected void onCreate(Bundle b) {
super.onCreate(b);
if (mHelper == null) {
getGameHelper();
}
mHelper.setup(this);
}
#Override
protected void onStart() {
super.onStart();
mHelper.onStart(this);
}
#Override
protected void onStop() {
super.onStop();
mHelper.onStop();
}
#Override
protected void onActivityResult(int request, int response, Intent data) {
super.onActivityResult(request, response, data);
mHelper.onActivityResult(request, response, data);
}
protected GoogleApiClient getApiClient() {
return mHelper.getApiClient();
}
protected boolean isSignedIn() {
return mHelper.isSignedIn();
}
protected void beginUserInitiatedSignIn() {
mHelper.beginUserInitiatedSignIn();
}
protected void signOut() {
mHelper.signOut();
}
protected void showAlert(String message) {
mHelper.makeSimpleDialog(message).show();
}
protected void showAlert(String title, String message) {
mHelper.makeSimpleDialog(title, message).show();
}
protected void enableDebugLog(boolean enabled) {
mDebugLog = true;
if (mHelper != null) {
mHelper.enableDebugLog(enabled);
}
}
#Deprecated
protected void enableDebugLog(boolean enabled, String tag) {
Log.w(TAG, "BaseGameActivity.enabledDebugLog(bool,String) is " +
"deprecated. Use enableDebugLog(boolean)");
enableDebugLog(enabled);
}
protected String getInvitationId() {
return mHelper.getInvitationId();
}
protected void reconnectClient() {
mHelper.reconnectClient();
}
protected boolean hasSignInError() {
return mHelper.hasSignInError();
}
protected GameHelper.SignInFailureReason getSignInError() {
return mHelper.getSignInError();
}
If you cloned (or refreshed) the samples recently, you'll notice that GameHelper and BaseGameActivity are not longer used. There is an informative video about this change: Game On! - The death of BaseGameActivity. If you just implement the interfaces to get the callbacks for the state: GoogleApiClient.ConnectionCallbacks and GoogleApiClient.OnConnectionFailedListener your problem should go away.
More specific details on how to use these interfaces is at https://developers.google.com/games/services/training/signin
As I see in GameHelper class, it disconnects from googleApiClient on onStop() and connect on onStart(). This is causing of blinking buttons.
If you don't want to change GameHelper implementation, make some UI improvement to make it less annoying.
I'm trying to build an RSS reader and put the rss feed fetch as ansyctask,
that returns a feed in list view, or returns a text view saying "no internet connection"
but the app still crashes, I don't know what's wrong, can you help please.
here is the code:
package rss;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ListView;
import android.widget.TextView;
import com.enporan.polytechoran.R;
public class RSSActivity extends ActionBarActivity {
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news);
rssfeedget alpha = new rssfeedget();
alpha.execute();
}
private class rssfeedget extends AsyncTask<String, Void, FeedSource> {
protected void onPreExecute() {
}
#Override
protected FeedSource doInBackground(String... params) {
FeedSource f = new HttpFeedSource();
if(f!=null)
return f;
else {
return null;
}
}
#Override
protected void onPostExecute(FeedSource result){
ListView rssItemList = (ListView) findViewById(R.id.rssListview);
rssItemList.setVerticalFadingEdgeEnabled(true);
if(doInBackground()==null){
TextView tv= (TextView) findViewById(R.id.textView2);
tv.setText("No internet Connection...");
}
else{
RSSItemAdapter adapter = new RSSItemAdapter(getApplicationContext(), R.layout.rssitem, doInBackground().getFeed());
rssItemList.setAdapter(adapter);
}
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_news, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
As #coelho pointed out, the FeedSource.getFeed() shouldn't be executed in the UI thread. You must now that the onPreExecute and onPostExecute methods are executed inside the UI thread, while the doInBackground method isn't.
Here's what you can do: in your AsyncTask class, add a private member: private List<RSSItem> result; (replace RSSItem here by the type of the collection returned by getFeed).
Then, update doInBackground:
FeedSource f = new HttpFeedSource();
if (f != null)
return f;
else {
this.result = f.getFeed(); // Execute getFeed in doInBackground
return null;
}
Then, in the onPostExecute method, you'll be able to use this private member as this:
RSSItemAdapter adapter = new RSSItemAdapter(getApplicationContext(), R.layout.rssitem, this.result);
Here is the code:
private class rssfeedget extends AsyncTask<String, Void, List<RSSItem>> {
private List<RSSItem> result;
protected void onPreExecute() {
}
#Override
protected List<RSSItem> doInBackground(String... params) {
FeedSource f = new HttpFeedSource();
if(f.getFeed()==null)
return null;
else {
this.result = f.getFeed(); // Execute getFeed in doInBackground
return result;
}
}
#Override
protected void onPostExecute(List<RSSItem> result){
if(doInBackground()==null){
TextView tv= (TextView) findViewById(R.id.textView2);
tv.setText("No internet Connection...");
}
else{
ListView rssItemList = (ListView) findViewById(R.id.rssListview);
rssItemList.setVerticalFadingEdgeEnabled(true);
RSSItemAdapter adapter = new RSSItemAdapter(getApplicationContext(), R.layout.rssitem, this.result);
rssItemList.setAdapter(adapter);
}
}
}
I am trying to implement this tutorial, for handling Configuration changes while running background tasks. Everything works fine, and the app does not crash after a configuration change. In the tutorial, a progress bar is used to display progress. But in my own implementation i want to use a Progress Dialog.
I have used progress Dialog's lots of times, so calling it and getting to appear is not the problem. My problem is that unlike the progress Bar, the progress dialog gets dismissed on configuration change. Just like that.
Here is my code:
My MainActivity:
private TaskFragment mTaskFragment;
private ProgressDialog mProgressDialog;
private TextView mPercent;
private Button mButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate(Bundle)");
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Initialize views
mButton = (Button) findViewById(R.id.task_button);
mButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (mTaskFragment.isRunning()) {
mTaskFragment.cancel();
} else {
mTaskFragment.start();
}
}
});
mProgressBar = new ProgressDialog(this);
FragmentManager fm = getSupportFragmentManager();
mTaskFragment = (TaskFragment) fm.findFragmentByTag("task");
// If the Fragment is non-null, then it is currently being
// retained across a configuration change.
if (mTaskFragment == null) {
mTaskFragment = new TaskFragment();
fm.beginTransaction().add(mTaskFragment, "task").commit();
}
if (mTaskFragment.isRunning()) {
mButton.setText(getString(R.string.cancel));
} else {
mButton.setText(getString(R.string.start));
}
}
/****************************/
/***** CALLBACK METHODS *****/
/****************************/
#Override
public void onPreExecute() {
Log.i(TAG, "onPreExecute()");
mProgressBar.setTitle("Wacky");
mProgressBar.setMessage("wack");
mProgressBar.setIndeterminate(true);
mProgressBar.show();
mButton.setText(getString(R.string.cancel));
mButton.setText(getString(R.string.cancel));
Toast.makeText(this, R.string.task_started_msg, Toast.LENGTH_SHORT).show();
}
#Override
public void onProgressUpdate(int percent) {
//Log.i(TAG, "onProgressUpdate(" + percent + "%)");
}
#Override
public void onCancelled() {
Log.i(TAG, "onCancelled()");
mButton.setText(getString(R.string.start));
Toast.makeText(this, R.string.task_cancelled_msg, Toast.LENGTH_SHORT).show();
}
#Override
public void onPostExecute() {
Log.i(TAG, "onPostExecute()");
mButton.setText(getString(R.string.start));
Toast.makeText(this, R.string.task_complete_msg, Toast.LENGTH_SHORT).show();
}
My headless Fragment that holds my asyncTask
/**
* This Fragment manages a single background task and retains itself across
* configuration changes.
*/
public class TaskFragment extends Fragment {
public static final String TAG = TaskFragment.class.getSimpleName();
/**
* Callback interface through which the fragment can report the task's
* progress and results back to the Activity.
*/
public static interface TaskCallbacks {
public void onPreExecute();
public void onProgressUpdate(int percent);
public void onCancelled();
public void onPostExecute();
}
public TaskCallbacks mCallbacks;
public DummyTask mTask;
public boolean mRunning;
/**
* Android passes us a reference to the newly created Activity by calling this
* method after each configuration change.
*/
#Override
public void onAttach(Activity activity) {
Log.i(TAG, "onAttach(Activity)");
super.onAttach(activity);
if (!(activity instanceof TaskCallbacks)) {
throw new IllegalStateException("Activity must implement the TaskCallbacks interface.");
}
// Hold a reference to the parent Activity so we can report back the task's
// current progress and results.
mCallbacks = (TaskCallbacks) activity;
}
/**
* This method is called only once when the Fragment is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate(Bundle)");
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
/**
* This method is <em>not</em> called when the Fragment is being retained
* across Activity instances.
*/
#Override
public void onDestroy() {
Log.i(TAG, "onDestroy()");
super.onDestroy();
cancel();
}
/*****************************/
/***** TASK FRAGMENT API *****/
/*****************************/
/**
* Start the background task.
*/
public void start() {
if (!mRunning) {
mTask = new DummyTask(this, mCallbacks);
mTask.execute();
mRunning = true;
}
}
/**
* Cancel the background task.
*/
public void cancel() {
if (mRunning) {
mTask.cancel(false);
mTask = null;
mRunning = false;
}
}
/**
* Returns the current state of the background task.
*/
public boolean isRunning() {
return mRunning;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
Log.i(TAG, "onActivityCreated(Bundle)");
super.onActivityCreated(savedInstanceState);
}
}
My Background Task (in a seperate outer class)
/**
* A dummy task that performs some (dumb) background work and proxies progress
* updates and results back to the Activity.
*/
public class DummyTask extends AsyncTask<Void, Integer, Void> {
private TaskFragment fragment;
private TaskCallbacks callbacks;
private ProgressDialog mProgressBar;
MainActivity activity;
public DummyTask(TaskFragment taskFragment, TaskCallbacks mCallbacks) {
// TODO Auto-generated constructor stub
this.fragment = taskFragment;
this.callbacks = mCallbacks;
}
#Override
protected void onPreExecute() {
// Proxy the call to the Activity
fragment.mCallbacks.onPreExecute();
fragment.mRunning = true;
}
#Override
protected Void doInBackground(Void... ignore) {
for (int i = 0; !isCancelled() && i < 100; i++) {
//Log.i(TAG, "publishProgress(" + i + "%)");
SystemClock.sleep(100);
publishProgress(i);
}
return null;
}
#Override
protected void onProgressUpdate(Integer... percent) {
// Proxy the call to the Activity
fragment.mCallbacks.onProgressUpdate(percent[0]);
}
#Override
protected void onCancelled() {
// Proxy the call to the Activity
fragment.mCallbacks.onCancelled();
fragment.mRunning = false;
}
#Override
protected void onPostExecute(Void ignore) {
// Proxy the call to the Activity
fragment.mCallbacks.onPostExecute();
fragment.mRunning = false;
}
}
I am thinking it is the context which i am passing the progress dialog in the onCreate method of my Main Activity. Thanks for your help.
First, Activity is subclass of Context, you should know this already. Second, if Activity is destroyed, it is don't have a window anymore. Third, Dialog uses Context (read Activity) not because it wants so, but because it uses window associated with Activity to display itself.
It should be perfectly understandable, why after destroying activity during configuration change, Dialog no longer visible to you.
Method of preserving objects you using is good, but it can't preserve anything that would be destroyed during configuration change, such as any object that related to non-application Context, all this objects you need create manually every time Context changes.
You should use onSaveInstanceState(Bundle) to store state of ProgressDialog (shown or not) and show it again in your onCreate(Bunde) using value stored in Bundle.
Just an example
#Override
protected void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putBoolean("SHOW_DIALOG", mProgressBar.isShowing());
}
//...
#Override
protected void onCreate(Bundle savedInstanceState) {
//...
if (savedInstanceState != null){
if (savedInstanceState.getBoolean("SHOW_DIALOG") && mTaskFragment.isRunning()){
mProgressBar.setTitle("Wacky");
mProgressBar.setMessage("wack");
mProgressBar.setIndeterminate(true);
mProgressBar.show();
}
}
//...
Is there an option to modify the content view on the android panorama client? For example I want to display the action bar on top. But currently the action bar is just shown at the beginning and subsequently hidden by the loaded panorama client, since the panorama client is always shown in full screen mode, although it is started in an extra fragment.
I tried now to put the panorama client in a seperated frame through a fragment - this is my code so far:
1. This is the activity whit the panorama fragment and and a text field:
public class PanoramaActivity extends Activity {
public static final String TAG = PanoramaActivity.class.getSimpleName();
private ActionBar actionBar;
private Fragment panoramaClient = new PanoramaClientFragment();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_snow);
actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
FragmentManager fragMan = getFragmentManager();
FragmentTransaction fragTrans = fragMan.beginTransaction();
fragTrans.replace(R.id.panoramaCLientFrame, panoramaClient, "PANO");
fragTrans.commit();
//Non fullscreen
requestWindowFeature(Window.FEATURE_ACTION_BAR);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.panorama, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
break;
default:
return super.onOptionsItemSelected(item);
}
return true;
}
}
2.And this is the fragment class with the panorama client:
public class PanoramaClientFragment extends Fragment implements ConnectionCallbacks,
OnConnectionFailedListener, OnPanoramaInfoLoadedListener {
private View view;
private PanoramaClient panoramaClient;
public static final String TAG = PanoramaClientFragment.class.getSimpleName();
public PanoramaClientFragment() {
// TODO Auto-generated constructor stub
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanteState){
view = inflater.inflate(R.layout.panorama_client, container, false);
return view;
}
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
panoramaClient = new PanoramaClient(getActivity().getApplicationContext(), this, this);
//Non fullscreen
//getActivity().requestWindowFeature(Window.FEATURE_ACTION_BAR);
//getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
//getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
#Override
public void onStart() {
super.onStart();
panoramaClient.connect();
}
#Override
public void onPanoramaInfoLoaded(ConnectionResult result, Intent viewerIntent) {
if (result.isSuccess()) {
Log.i(TAG, "found viewerIntent: " + viewerIntent);
if (viewerIntent != null) {
startActivity(viewerIntent);
}
} else {
Log.e(TAG, "error: " + result);
}
}
#Override
public void onConnectionFailed(ConnectionResult status) {
Log.e(TAG, "connection failed: " + status);
}
#Override
public void onConnected(Bundle arg0) {
Uri uri = Uri.parse("android.resource://" + this.getActivity().getPackageName() + "/" + R.raw.pano1);
panoramaClient.loadPanoramaInfo(this, uri);
}
#Override
public void onDisconnected() {
// Do nothing.
}
#Override
public void onStop() {
super.onStop();
panoramaClient.disconnect();
}
}
If i uncomment the three "non fullscreen"-lines in the fragment class, the app crashes and says:
android.util.AndroidRuntimeException: requestFeature() must be called before adding content
Thanks for your replies.
Greetings.
Try to create a fragment and add the panorama client activity to it.
I think you cannot show the action bar for one simple reason, when your panorama info is loaded you are starting a new activity which is completely out of your control.
if (viewerIntent != null) {
startActivity(viewerIntent);
}
So your code tries to modify the activity that handles the PanoramaClient instance, but not the one that loads the panorama image.