Before referring me to other threads on this forum and marking my question as duplicate kindly read my question. I have to create a global application timeout. No matter which activity is user on, after specific amount of time the user will be displayed AlertDialog that his session has expired and he can exit or renew his session. I have read different solutions and used service as my solution.
public class InActivityTimer extends Service {
MyCounter timer;
#Override
public void onCreate() {
timer = new MyCounter(20 * 1000,1000);
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
timer.start();
return super.onStartCommand(intent, flags, startId);
}
private class MyCounter extends CountDownTimer{
public MyCounter(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
#Override
public void onFinish() {
Intent intent = new Intent("timeout_action");
sendBroadcast(intent);
stopSelf();
}
#Override
public void onTick(long millisUntilFinished) {
// Need AlertDialog code here
Toast.makeText(getApplicationContext(), ("Time Remaining: " + millisUntilFinished/1000)+"", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onDestroy() {
timer.cancel();
super.onDestroy();
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
}
The problem is that I can display the Toast without any problem but the AlertDialog is not displayed when called inside onFinish().
The first problem is to display the AlertDialog for whole application bearing in mind that the AlertDialog is displayed for some context. Also if somehow the AlertDialog is displayed then how to close the Application. On Activity I just close the activity by calling finish() so should I clear the Activities stack in this case?
The second complex part that I am facing is to display a popup when user click "Time remaining" link in the application which will show how much time is remaining for the Session to be timed out. This time should be exactly same as the time remaining in the service.
I can also use BroadcastReceiver and send update to the activity once the time is finished but wouldn't that be Activity specific because I want the timeout to act same regardless of which activity is user on. I want to avoid writing the same code on each activity.
Kindly guide me through with some solution.
If you use a fragment based design for your app, you can keep a root FragmentActivity in which all other elements of the app are displayed. This way you can use the context of the root FragmentActivity every time, to display your Dialog.
Additional: "Could you kindly refer to me some article.."
What you are doing is not common, and I would have to google search just like you to find any existing example similar to your case. I can however fill in a bit more detail on what I have proposed above.
If you are unfamiliar with using Fragments, read the Developer Documentation.
public class MainActivity extends FragmentActivity {
private static final int SPLASH_SCREEN_FRAGMENT = 0;
private static final int HOME_SCREEN_FRAGMENT = 1;
...
#Override
protected void onCreate(Bundle. savedInstanceState) {
super.onCreate(savedInstanceState);
// show your first fragment
Fragment splashFragment = new SplashFragment();
getSupportFragmentManager().beginTransaction().replace(android.R.id.content, splashFragment).commit();
// Start your service using the context of your FragmentActivity
// Your FragmentActivity will always be the current activity, and you will display
// all other elements of your app inside it as fragments
Intent intent = new Intent(this, InActivityTimer.class);
startService(intent);
}
// method for switching the displayed fragment
private void fragmentSwitcher(int fragmentType) {
Fragment currentFragment = new Fragment();
switch (currentFragmentType) {
case SPLASH_SCREEN_FRAGMENT:
currentFragment = new SplashScreenFragment();
break;
case HOME_SCREEN_FRAGMENT:
currentFragment = new HomeScreenFragment();
break;
...
}
getSupportFragmentManager().beginTransaction().replace(android.R.id.content, currentFragment).commit();
}
}
I have solved my issue with rather very simple approach.
#Override
public void onFinish() {
Intent intent = new Intent(getBaseContext(), TimeoutActivity.class);
startActivity(intent);
stopSelf();
}
and below is the onCreate method for my TimeoutActivity.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ContextThemeWrapper ctw = new ContextThemeWrapper(TimeoutDialogActivity.this, R.style.Theme_Base_AppCompat_Dialog_FixedSize);
final AlertDialog alertDialog = new AlertDialog.Builder(ctw).create();
alertDialog.setCancelable(false);
alertDialog.setTitle("Session Timeout !");
alertDialog.setTitle("Your session has expired.");
alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "Logout", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
alertDialog.dismiss();
finish();
}
});
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "Exit", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
alertDialog.dismiss();
finish();
}
});
alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "Renew Session", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
alertDialog.dismiss();
finish();
});
alertDialog.show();
}
Related
I'm working on app which is required user in active feature like if user is not available on the application more than 15 min. it shows some popup on the last activity we used, when we click okay it redirects to login screen.
It is working absolutely fine when i opened back my app exactly after 15 minutes to around 30 minutes .
My problem is now, when i open my app after 45 min or more than 1 hour, it doesn't work, it doesn't show in activity popup. it just opened the last activity i used.
I tried with below code added in splash activity:
if (!isTaskRoot()
&& getIntent().hasCategory(Intent.CATEGORY_LAUNCHER)
&& getIntent().getAction() != null
&& getIntent().getAction().equals(Intent.ACTION_MAIN)) {
finish();
return;
}
Here is my BaseActivity class used for in active state checking
public class MyBaseActivity extends AppCompatActivity {
AlertDialog alertDialog;
Context context;
public static final long DISCONNECT_TIMEOUT = 900000; // 15 min = 15 * 60 * 1000 ms
private Handler disconnectHandler = new Handler(){
public void handleMessage(Message msg) {
}
};
private Runnable disconnectCallback = new Runnable() {
#Override
public void run() {
LayoutInflater li = LayoutInflater.from(MyBaseActivity.this);
View promptsView = li.inflate(R.layout.acount_status_dialogue, null);
final TextView userInput = (TextView) promptsView.findViewById(R.id.txtTitle);
final TextView userInput1 = (TextView) promptsView.findViewById(R.id.txtTitle1);
userInput1.setText("USER IN-ACTIVE");
userInput.setText("Due to user is inactive from last 15 minutes. Please Login Again");
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(MyBaseActivity.this,R.style.DialogLevelsStyle);
// set prompts.xml to alertdialog builder
alertDialogBuilder.setView(promptsView);
// set dialog message
alertDialogBuilder
.setCancelable(false)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
//do things
Intent i = new Intent(MyBaseActivity.this, SignInActivity.class);
//i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
finish();
Constant.val = 1;
AccountUtils.setValue("1");
}
});
// create alert dialog
alertDialog = alertDialogBuilder.create();
// show it
alertDialog.show();
// Perform any required operation on disconnect
}
};
public void resetDisconnectTimer(){
Log.i("Main", "Invoking logout timer");
//disconnectHandler.removeCallbacks(disconnectCallback);
disconnectHandler.postDelayed(disconnectCallback, DISCONNECT_TIMEOUT);
}
public void stopDisconnectTimer(){
Log.i("Main", "cancel timer");
disconnectHandler.removeCallbacks(disconnectCallback);
}
#Override
public void onUserInteraction(){
resetDisconnectTimer();
}
#Override
public void onResume() {
super.onResume();
resetDisconnectTimer();
}
#Override
public void onStop() {
if (Constant.isAppIsInBackground(this)) {
stopDisconnectTimer();
resetDisconnectTimer();
}else {
stopDisconnectTimer();
}
super.onStop();
//stopDisconnectTimer();
}
}
Please find out my issue. thanks in advance.
Save the current time when the user put your app to background (for example in SharedPreferences), and when the user starts again your app calculate the diff and show what you want on the screen.
I have added a popup activity inside my app which is popping after 15 seconds of my app start. But when I am opening another activity and coming back to my main activity the popup showing again. I want it to appear only the first time when user is opening the app. What changes I should make? Appreciate the help.
Here is the popup code:
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (context != null) {
AlertDialog.Builder alert = new AlertDialog.Builder(context, R.style.MyAlertDialogStyle)
.setTitle("Title")
.setMessage("Message")
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// continue with delete
Intent intent = new Intent(MainActivity.this, WebActivity.class);
startActivity(intent);
}
})
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogInterface, int which) {
dialogInterface.dismiss();
// do nothing
}
});
mDialog = alert.create();
mDialog.getWindow().getAttributes().windowAnimations = R.style.MyAlertDialogStyle;
if (!((Activity) context).isFinishing())
mDialog.show();
// .setIcon(R.drawable.inr1)
// .show();
}
}
}, 15000);
You can do this if it needs to pop up only on application start
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
//Set some flag on global constant
}
}
save a tag(counter) in sharedPrefrances for that When the application starts up interpretation is the first time app start
You can use a global variable in the application class like this.
public class global extends Application
// some Boolean variable to hold status
And make sure you put this class inside android manifests application tag
android:name
Add this code in your MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
if(sharedPreferences.getBoolean("IS_FIRST_TIME", true)) {
//show your dialog here
//...
//change the value of your sharedPreferences
sharedPreferences.edit().putBoolean("IS_FIRST_TIME", false).apply();
}
}
please check the activity lifecycle.
Note:Remove Handler.
Note 2:Create method and call the dialog
Note 3:check the below method in lifecycle.
onPause ():
Called as part of the activity lifecycle when an activity is going into the background, but has not (yet) been killed. The counterpart to onResume(). When activity B is launched in front of activity A, this callback will be invoked on A. B will not be created until A's onPause() returns, so be sure to not do anything lengthy here.
save a boolean inside sharedpreferences read its value and determine weather running for the first time or not.
here is the code that will help you.
public void firstimeRun(boolean value) {
SharedPreferences sharedPreferences = getPrefrenceManager();
sharedPreferences.edit().putBoolean("key", value).apply();
}
public boolean isRunningForthefirstTime() {
SharedPreferences sharedPreferences = getPrefrenceManager();
return sharedPreferences.getBoolean("key", false);
}
private SharedPreferences getPrefrenceManager() {
return PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
}
Type finish(); after your startActivity(intent);
I am trying to release heap size by destroying the current activity, while going to another activity.
I am using finish(); on backPreess()
But this is not releasing the heap.
on setContentView()
The heap size increases 16Mb. I want to release this increase in the heap after going to another activity. Can any one help how to do this?
My code is as following:
package com.stancil.levels;
public class PaintActivity extends ZebraActivity implements
PaintView.LifecycleListener, PaintView1.LifecycleListener1 {
private static final int REQUEST_PICK_COLOR = 1;
....
....
public PaintActivity() {
_state = new State();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Constants.context = getApplicationContext();
setContentView(R.layout.paint);
..................
...................
...............
}
public void onPreparedToLoad() {
// We need to invoke InitPaintView in a callback otherwise
// the visibility changes do not seem to be effective.
new Handler() {
#Override
public void handleMessage(Message m) {
new InitPaintView();
Log.v("PaintActivity", "After InitPaintView Called");
}
}.sendEmptyMessage(0);
}
private class InitPaintView implements Runnable {
private Bitmap _originalOutlineBitmap;
private Handler _handler;
public InitPaintView() {
// Make the progress bar visible and hide the view
_paintView.setVisibility(View.GONE);
_progressBar.setProgress(0);
_progressBar.setVisibility(View.VISIBLE);
_state._savedImageUri = null;
_state._loadInProgress = true;
_originalOutlineBitmap=_imageBitmap;
_handler = new Handler() {
#Override
public void handleMessage(Message m) {
switch (m.what) {
case Progress.MESSAGE_INCREMENT_PROGRESS:
// Update progress bar.
_progressBar.incrementProgressBy(m.arg1);
break;
case Progress.MESSAGE_DONE_OK:
case Progress.MESSAGE_DONE_ERROR:
// We are done, hide the progress bar
// the paint view back on.
_state._loadInProgress = false;
_paintView.setVisibility(View.VISIBLE);
_progressBar.setVisibility(View.GONE);
initiatePopupWindow();
break;
}
}
};
new Thread(this).start();
}
public void run() {
Log.v("Wasimmmmmmmmmmmmmmmm", "qqqqq 22");
_paintView.loadFromBitmap(_originalOutlineBitmap, _handler);
}
}
private static class State {
// Are we just loading a new outline?
public boolean _loadInProgress;
// The resource ID of the outline we are coloring.
//public int _loadedResourceId;
//
// If we have already saved a copy of the image, we store the URI here
// so that we can delete the previous version when saved again.
public Uri _savedImageUri;
}
#Override
public void onBackPressed() {
new AlertDialog.Builder(this)
.setTitle("Exit")
.setMessage("Do you want to go to Main Menu?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
Constants.check_new=true;
Intent i=new Intent(PaintActivity.this,MainActivity.class);
// i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
finish();
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
}
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Do nothing.
}
}).show();
}
}
}
release yoru objects in onDestroy method, anyways, if there are no references to the detroyed activity, GC will automaticly clean up whenever its needed (it doesnt need to happen right after you closed your activity).
Alternatively theres a method to force running GC, but I wont even write about it cuz its not really a feature a typical application should use
Im at my wits end here. I have a Class which Implements the OnClickListener cous i need the same action on Buttons accros my Application. This used to work just fine. But since I added some functionality by getting some needed data from the app preferences. startActivity throws a null pointer exception.Here is the class:
//Imports
public class CallClickListener extends Activity implements View.OnClickListener {
protected AppPreferences appPrefs;
String contactPersonName;
String contactPersonTelephone;
String name;
public CallClickListener(Context context){
Log.d("TRACE", "init CallClick");
appPrefs = new AppPreferences(context);
try {
JSONObject object = appPrefs.getConsultantObject();
contactPersonName = object.getString("contactPersonName");
contactPersonTelephone = object.getString("contactPersonTelephone");
name = object.getString("name");
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onClick(View view) {
final View v = view;
AlertDialog.Builder alert = new AlertDialog.Builder(view.getContext());
alert.setTitle("Anrufen");
alert.setMessage("Kontakt für " + name + ", " + contactPersonName + " anrufen");
alert.setPositiveButton("Anrufen", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:"+contactPersonTelephone));
startActivity(callIntent);// this line throws the exception
}
});
alert.setNegativeButton("Abbrechen", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Toast.makeText(v.getContext(), "Abbruch", Toast.LENGTH_SHORT).show();
}
});
alert.show();
}
}
The Strings are all there from appPrefs, i also tried with hardcoding a phonenumber just incase. the Alert works fine, but as soon as i hit the positive Button, the app crashes
I add the Listener like this:
bCall.setOnClickListener(new CallClickListener(getApplicationContext()));
I added the necessary Call permissions.
I'm fairly new to Android dev, what am I missing?
Do this.... make the context object that you passed in the constructor into a field variable. and change startActivity to context.startActivity. It will work then.
EDIT: Highlighting the full solution.
bCall.setOnClickListener(new CallClickListener(getApplicationContext()));
should be changed to YourActivityClass.this instead of getApplicationContext.
Start Activity in the same task does not work with a context object that is not an Activity. So you need to either change the context to Activity or you start the activity in a new task. Also without calling startActivity on the context provided to your constructor you were getting the NPE because your CallClickListerner has no context.
Use activity context. Also check if you have initialized bCall. If you have not you will get NullPointerException.
bCall.setOnClickListener(ActivityName.this);
Also check this link to know when to use activity context and when to use application context
When to call activity context OR application context?
Edit:
Make sure you have added permission in manifest file
<uses-permission android:name="android.permission.CALL_PHONE" />
For reference use the below. My Class extends Activity
Button b= (Button) findViewById(R.id.button1);
b.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v1) {
// TODO Auto-generated method stub
final View v = v1;
AlertDialog.Builder alert = new AlertDialog.Builder(v.getContext());
alert.setTitle("Anrufen");
alert.setMessage("Kontakt für " );
alert.setPositiveButton("Anrufen", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:8095992052"));
startActivity(callIntent);// this line throws the exception
}
});
alert.setNegativeButton("Abbrechen", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Toast.makeText(v.getContext(), "Abbruch", Toast.LENGTH_SHORT).show();
}
});
alert.show();
}
});
The dialog:
public class ClearDialog extends Dialog {
private MainActivity context;
public ClearDialog(MainActivity context) {
super(context);
this.context = context;
setContentView(R.layout.clear_dialog);
setTitle("something");
setCanceledOnTouchOutside(false);
setCancelable(true);
}
/* not overriding anymore
#Override
public void onBackPressed() {
return;
}
still doesnt work */
#Override
protected void onStart() {
super.onStart();
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
editor.clear();
editor.commit();
ResourceHelpers.removeAllResources();
context.onResourcesDeleted();
}
}
The Activity:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.itemLogoff:
loginDialog.show(); //this is another dialog
break;
case R.id.itemSync:
Intent syncer = new Intent(MainActivity.this, SyncActivity.class);
MainActivity.this.startActivity(syncer);
break;
case R.id.itemClear:
new AlertDialog.Builder(this)
.setIcon(R.drawable.ic_action_alert)
.setTitle("something")
.setMessage("something")
.setPositiveButton("something", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
showDeleteDialog();
}
})
.setNegativeButton("something", null)
.show();
break;
}
return true;
}
private void showDeleteDialog() {
cd = new ClearDialog(this); //this is the dialog
cd.show();
}
public void onResourcesDeleted() {
cd.dismiss();
loginDialog.show();
}
So.. The user clicks on "Delete all data" from the ActionBar (optionsmenu). I open an AlertDialog asking if he's sure. Then if he's sure, I open a dialog that shows a spinning ProgressBar.
The problem: it won't dismiss!
The loginDialog (all data is lost so I want the user to login again...) comes up in the background. The ClearDialog won't dismiss...
I think that the problem is here (don't override in this way that method):
#Override
public void onBackPressed() {
return;
}
You can already obtain a modal dialog with .setCancelable(false)
Please take a loog at this documentation: http://developer.android.com/guide/topics/ui/dialogs.html#AlertDialog
Give the following property for dialogue
.setCancelable(true);
its just like .setTitle() or .setMessage in your code....
On top of StErMi's answer, which you should follow, also switch the two lines in your onResourcesDeleted() method. The login dialog is called, and takes over before your dismiss is called.
public void onResourcesDeleted() {
cd.dismiss();
loginDialog.show();
}