Best UI design to handle no internet connection - android

My app like many others needs an internet connection constantly. In my main activity I have:
#Override
public void onStart() {
if (!isOnline()) {
Intent disconnected = new Intent(this, Disconnected.class);
startActivity(disconnected);
}
else {
...
}
}
The Disconnected activity displays a TextView with a message and a retry Button.
Now the problem with this is that if the user presses back he immediately ends up in the Disconnected activity again and isn't able to exit the application using back.
What's an elegant way to handle no internet connection? My main activity is a ListView, should I just have hidden Views that only appear when there's no connection? (I don't feel this is the right way though).

There can be several options
Option 1:
Use AlertDialog to show the disconnection. Set it to non cancelable and with two buttons like retry and exit.
Option 2:
start disconnection activity using start activity for result if the network disconnection occurs. In the new activity there is a retry button. If retry is set the set OK and finish. And if back button press set CANCEL. now in first activity in onActivityResult if you get RESULT_OK then retry connection.

If your starting Activity is just the if statement, and only meant for checking if the user have an internet connection, you could simply finish that Activity when you enter the Disconnected Activity
You do this by calling StartActivity with the FLAG_ACTIVITY_CLEAR_TOP flag set.
#Override
public void onStart() {
if (!isOnline()) {
Intent disconnected = new Intent(this, Disconnected.class);
disconencted.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(disconnected);
}
else {
... // Here you would want to do the same thing;
// if not, the user could navigate back to this activity as well
}
}
I couldn't test this, but I think it should work.

Related

android onresume - conflict with AlertDialog

In my app I am using location services, and after first install the app asks for Location permission. If the user click OK, permission is granted, if Cancel, then I have another dialog with some info.
Then - if the user has turned off the GPSon his device, a dialog will come up which asks to enable GPS - if Ok is clicked, the device settings are opened and here the user can enable the GPS.
As far as now everything works fine. But I need to restart the activity after the user is back from settings. (So I can load some items according the location).
For this I used onresume():
#Override
protected void onResume() { //restart activity after back from GPS settings
String action = getIntent().getAction();
// Prevent endless loop by adding a unique action, don't restart if action is present
if(action == null || !action.equals("created")) {
Intent intent = new Intent(this, Okoli.class);
startActivity(intent);
finish();
}
// Remove the unique action so the next time onResume is called it will restart
else
getIntent().setAction(null);
super.onResume();
}
I used there a unique action to avoid loop restart, so in oncreate I am setting also getIntent().setAction("created");
Now this is working fine - the activity restarts after the user is back from settings, but it conflicts with Permission dialog which I mentioned as first.
So if I have the onResume function, and the user installs the app, the Location permission dialog comes up, but in this case the user can't click CANCEL, because the dialog is looping forever if he clicks cancel. So it is appearing again and again until he clicks OK.
When I remove the whole onResume section from my code, then the Permission dialog works fine, but I need onresume for restarting activity.
okey, finally I store a value to SharedPreferences - when user doesn't allow the location access, and then I check this value onResume and only restart the activity if the value is not set. Works fine!

Android: BackStack misbehaves, if application was started from another app

I have an application, which starts with a SplashScreenActivity. Afterwards, a LoginActivity is shown, or if the user is already logged in, a MainActivity is shown. If the application is already running, SplashScreenActivity is dismissed with the following
//SplashScreenActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Adding this check for following cases
if (!isTaskRoot())
{
String intentAction = getIntent().getAction();
if (getIntent().hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
finish();
return;
}
if(getIntent().getCategories().contains(GCMIntentService.INTENT_CATEGORY_GH_NOTIFICATION)){
finish();
return;
}
}
Problem occurs
If I start the application from another activity like PlayStore, it resumes at the right activity if already running. This is the Intent I'm using to reproduce within a second app
//AnotherApplication.apk
Intent launchIntent = getPackageManager().getLaunchIntentForPackage("my.package.name");
startActivity(launchIntent);
However, this action is somehow breaking the Backstack. Instead of closing the application on backpress in the MainActivity, it restarts the application.
//MainActivity.class
#Override
public void onBackPressed() {
if (getNavDrawerMain().isDrawerOpen()) {
getNavDrawerMain().closeDrawer();
} else {
closeApp();
}
}
protected void closeApp() {
if (doubleBackToExitPressedOnce) {
//super.onBackPressed(); //i tried both, but behaviour is the same
finish();
return;
}
this.doubleBackToExitPressedOnce = true;
new Handler().postDelayed(new Runnable() {
#Override
public void run()
doubleBackToExitPressedOnce = false;
}
}, 500);
}
I used breakpoints and found out that MainActivity:onDestroy() get called, but instead of resuming application to the HomeScreen, it always restarts and I don't know why.
I tried the following:
- Used different launchmodes like singleTask and singleInstance, but it didn't make any difference. onNewIntent is called, but if i call finish, HomeActivity restarts
- as commeted below, i tried moveTaskToBack(true), but Activity is restaring too (and we really want to close the app instead of moving it to the BackStack)
Try with moveTaskToBack(true); instead of finish(); to close the app. It will then go to OnRestart() and then OnStart()->OnResume() (and won't go to OnCreate).
And make sure you don't have the "Don't keep activities" marked at Developer Options in your Android Settings (destroy every activity as soon as the user leaves it).
Trying adding this flag to your intent starting your app: RESET_TASK_IF_NEEDED, URL=http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
What it does:
If set, and this activity is either being started in a new task or bringing to the top an existing task, then it will be launched as the front door of the task.
You may also use:
http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_REORDER_TO_FRONT
What it does:
If set in an Intent passed to Context.startActivity(), this flag will cause the launched activity to be brought to the front of its task's history stack if it is already running.
Which one you use depend on the desired end result.
If you can't control who starts you you need to set or launch mode to single task or single instance.
Described here:
http://inthecheesefactory.com/blog/understand-android-activity-launchmode/en
The interesting part:
singleTask
This mode is quite different from standard and singleTop. An Activity with singleTask launchMode is allowed to have only one instance in the system (a.k.a. Singleton). If there is an existed Activity instance in the system, the whole Task hold the instance would be moved to top while Intent would be delivered through onNewIntent() method. Otherwise, new Activity would be created and placed in the proper Task.

Passing current Intent as extra to another Activity

I have a problem with my Login screen. When it's started, I check for network connection, and if it's disabled, I want to show NoNetworkActivity. And the same for every other screen: when Activity is launched, I check network connection and navigate to NoNetworkActivity is needed. When navigating, I want to save the Intent which launched this previous activity and finish it to disable the Back button redirection when on NoNetworkActivity. So, when connection is restored, I want to launch that intent and get actual state of the app before this error:
LoginActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
if (!App.getInstance().isNetworkConnected()) {
Intent noNetwork = new Intent(this, NoNetworkActivity.class);
noNetwork.putExtra(NoNetworkActivity.EXTRA_FAILED_INTENT, getIntent());
startActivity(noNetwork);
finish();
}
...
NoNetworkActivity
private void checkNetworkConnection() {
mCheckButton.setVisibility(View.INVISIBLE);
mProgressBar.setVisibility(View.VISIBLE);
if (App.getInstance().isNetworkConnected()) {
Intent failedIntent = getIntent().getParcelableExtra(EXTRA_FAILED_INTENT);
startActivity(failedIntent);
finish();
} else {
mCheckButton.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.INVISIBLE);
App.toast("Connection failed");
}
}
And it's getting strange: startActivity(failedIntent) does NOTHING. I've tried to remove finish() from next line, and NoNetworkActivity just stays on top without anything happening.
And one more thing. You can suggest passing Activity actual class names instead of intents, but I realy need Intent. That's because I'm using a lot of starting actions for every activity and a bunch of extras.
Thanks in advance for any help. Cheers!
Very bad approach. Don't use it.
First, you don't need to finish previous activity just to disable Back action. You can override onBackPressed().
Second, you don't need to start parent activity again. Just call a new activity with startActivityForResult(); and override onActivityResult() callback.
Third, but most important. Why do you want to call a new activity just to show 'No Network' message? And what if network won't be re-established? Just create isNetworkEnabled() method and call it when user attempts to get data from the Internet, before sending actual request to server. If no network - notify a user with an alert or toast.
I suggest you use fragments instead of activities first of all.
Using fragments you can set retainInstance(true);
To disable coming back from an activity to the previous :
1)call finish() on that activity
2)
Intent i = new Intent();
i.setClass(this, MyActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(i);`
It works with an explicit Intent.
In LoginActivity substitute:
noNetwork.putExtra(NoNetworkActivity.EXTRA_FAILED_INTENT, getIntent());
with:
noNetwork.putExtra(NoNetworkActivity.EXTRA_FAILED_INTENT, new Intent(this, LoginActivity.class));
Btw, Alexander Zhak has some good points in his answer

Android Lifecycle Behavior

Having an issue with lifecycle events and need some help if possible. I have read the other posts with similar issue and error however, still stuck .... Explaination may be a little long.
I have an App that requires the user to login when any 'backgrounding' event occurs (App switch, screen sleep, phone call and so on). Previously I was using the method of 'getRunningTasks' to check is my app is no longer top of stack to trigger the login flags and activity. I am now using a suggested timer method via onPause / onResume in my Application class which works great.
Now the issue at hand, and the questions:
App into background by Device Home button.
App is in the background, all my checks and flags are properly set. When I relaunch the app (Icon press), it loads the last activity, then from the onResume function - performs a startIntent for the login activity - based on the flag set in the timer event. The user logs in, and returns to the activity they were on (Stock List). All Good.
App into background by App Switch
timer event fires and all login flags are properly done as previous example.
--> Click 'device Home'button, then App icon --- App loads properly and loads the Login Activity, users logs in -- all good.
Problem area
2b. App into background by App Switch --> Example, Android pull down menu, jump to device settings - then return to app via the device back button
timer event fires and all login flags are properly done as previous example.
--> Click device 'return/back' button to return directly to the app.
User goes to Login screen, clicks 'Login' button - gets app crash due to :
java.lang.RuntimeException: Performing pause of activity that is not resumed
followed by
java.lang.RuntimeException: Performing stop of activity that is not resumed
My own system Logs in the lifecycle events all seem to fire in the appropriate order.
Basic overview from app re-launch:
-> DashBoard onResume
-> check for login flag and call intent for Login activity
Dashboard onPause
Login onResume
Login - user login hit button
Login onPause --- activity finish
Return to Dashboard onResume
-> Load details
The only diff is in the last senario ... the DashBoard onPause happens after the Login onResume. Not sure if this is the problem, or an Async delay issue with the lifecycle events.
My Manifest includes:
android:minSdkVersion="10" --- Maintaining old API due to clients with older devices
android:targetSdkVersion="19"
Any help or suggestions on what may be out of whack here? or how to avoid this error.
Thanks
--
Some code:
From BaseActivity.java
~~~~~~
private AppPreferences mAppPrefs;
protected MyApplication mMyApp;
#Override
protected void onPause() {
super.onPause();
//start timer
mMyApp.startActivityTransitionTimer();
}
#Override
protected void onResume() {
super.onResume();
//update current Activity Name
mMyApp.setCurrentActivity(this);
mMyApp.stopActivityTransitionTimer();
mAppPrefs = new AppPreferences(getApplicationContext());
if (mAppPrefs.getAppDestroyed() == true
|| mAppPrefs.getExitType() == AppPreferences.EXITTYPE_FULLEXIT) {
// we want to fully exit/close the app
this.finish();
} else {
if (mAppPrefs.getForceLoginState() == true) {
//~~~~ process login events
Intent intent = new Intent(this, LoginScreen.class);
startActivity(intent);
return;
}
}
}
DashboardActivity extends BaseActivity
{
#Override
protected void onPause() {
super.onPause();
if (((MyApplication)this.getApplication()).getProcessingLogout()){
// if we are already processing a login, do not continue here
return;
}
// ~~~ activity specific onPause event such as unregisterReceivers
((MyApplication)this.getApplication()).startActivityTransitionTimer();
}
#Override
protected void onResume() {
super.onResume();
if (((MyApplication)this.getApplication()).getProcessingLogout()){
// if we are already processing a login, do not continue here
return;
}
// ~~~ activity specific onResume events such as registerReceivers
}
}

OnActivityResult called prematurely with Settings menu

In my Application, before going into the App, I have to check my wifi connection and take the user to the wifi settings if wifi is not enabled.
I dont want to use WifiManager.setWifiEnabled(); as I want to give the user the opportunity to set it.
I have referred the link, onActivityResult() called prematurely
and also, onActivityResult and the Settings Menu
But it is not working for me. The OnActivityResult() and onResume() is called almost at the same time I get into the Settings menu.
Here is my code,
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setMessage("You are not currently connected to any network. Please turn on the network connection to continue.")
alert.setPositiveButton("Settings", new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int arg1)
{
Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);
startActivityForResult(intent,SETTINGSCODE);
dialog.cancel();
}
});
alert.show();
In the onActivityResult(), I am checking the network state again which is called before changing the settings.
What can i do so that OnActivityResult()
will be called only after coming back from the settings menu?
Please help me with this issue..!!
This is how I solved this issue.
In reference to the answer posted for a similar question, I changed startActivityForResult() to startActivity() as the order of the action calls is like
startActivityForResult()
onActivityResult()
onCreate() (new activity)
setResult() or finish()
The control is taken to the settings page where the user can switch on/off the Wi-Fi and later come back to the app. :)

Categories

Resources