android: Show connection errors as banner, not as modal dialog

This commit is contained in:
Tobias Brunner
2018-07-03 11:31:35 +02:00
parent 64b7a6d622
commit 6987ddb313
12 changed files with 215 additions and 138 deletions
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2016 Tobias Brunner
* Copyright (C) 2012-2018 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* HSR Hochschule fuer Technik Rapperswil
@@ -20,19 +20,18 @@ package org.strongswan.android.ui;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -60,7 +59,10 @@ public class VpnStateFragment extends Fragment implements VpnStateListener
private int mColorStateSuccess;
private Button mActionButton;
private ProgressBar mProgress;
private AlertDialog mErrorDialog;
private LinearLayout mErrorView;
private TextView mErrorText;
private Button mErrorDetails;
private Button mDismissError;
private long mErrorConnectionID;
private VpnStateService mService;
private final ServiceConnection mServiceConnection = new ServiceConnection()
@@ -128,12 +130,25 @@ public class VpnStateFragment extends Fragment implements VpnStateListener
});
enableActionButton(null);
mErrorView = view.findViewById(R.id.vpn_error);
mErrorText = view.findViewById(R.id.vpn_error_text);
mErrorDetails = view.findViewById(R.id.error_details);
mDismissError = view.findViewById(R.id.dismiss_error);
mProgress = (ProgressBar)view.findViewById(R.id.progress);
mStateView = (TextView)view.findViewById(R.id.vpn_state);
mColorStateBase = mStateView.getCurrentTextColor();
mProfileView = (TextView)view.findViewById(R.id.vpn_profile_label);
mProfileNameView = (TextView)view.findViewById(R.id.vpn_profile_name);
mDismissError.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
clearError();
}
});
return view;
}
@@ -156,7 +171,6 @@ public class VpnStateFragment extends Fragment implements VpnStateListener
{
mService.unregisterListener(this);
}
hideErrorDialog();
}
@Override
@@ -238,13 +252,10 @@ public class VpnStateFragment extends Fragment implements VpnStateListener
{
if (error == ErrorState.NO_ERROR)
{
hideErrorDialog();
mErrorView.setVisibility(View.GONE);
return false;
}
else if (mErrorDialog != null)
{ /* we already show the dialog */
return true;
}
mErrorConnectionID = connectionID;
mProfileNameView.setText(name);
showProfile(true);
@@ -257,24 +268,24 @@ public class VpnStateFragment extends Fragment implements VpnStateListener
case AUTH_FAILED:
if (imcState == ImcState.BLOCK)
{
showErrorDialog(R.string.error_assessment_failed);
showError(R.string.error_assessment_failed);
}
else
{
showErrorDialog(R.string.error_auth_failed);
showError(R.string.error_auth_failed);
}
break;
case PEER_AUTH_FAILED:
showErrorDialog(R.string.error_peer_auth_failed);
showError(R.string.error_peer_auth_failed);
break;
case LOOKUP_FAILED:
showErrorDialog(R.string.error_lookup_failed);
showError(R.string.error_lookup_failed);
break;
case UNREACHABLE:
showErrorDialog(R.string.error_unreachable);
showError(R.string.error_unreachable);
break;
default:
showErrorDialog(R.string.error_generic);
showError(R.string.error_generic);
break;
}
return true;
@@ -293,75 +304,46 @@ public class VpnStateFragment extends Fragment implements VpnStateListener
mActionButton.setVisibility(text != null ? View.VISIBLE : View.GONE);
}
private void hideErrorDialog()
{
if (mErrorDialog != null)
{
mErrorDialog.dismiss();
mErrorDialog = null;
}
}
private void clearError()
{
if (mService != null)
{
mService.disconnect();
if (mService.getConnectionID() == mErrorConnectionID)
{
mService.disconnect();
mService.setError(ErrorState.NO_ERROR);
}
}
updateView();
}
private void showErrorDialog(int textid)
private void showError(int textid)
{
final List<RemediationInstruction> instructions = mService.getRemediationInstructions();
final boolean show_instructions = mService.getImcState() == ImcState.BLOCK && !instructions.isEmpty();
int text = show_instructions ? R.string.show_remediation_instructions : R.string.show_log;
mErrorDialog = new AlertDialog.Builder(getActivity())
.setMessage(getString(R.string.error_introduction) + " " + getString(textid))
.setCancelable(false)
.setNeutralButton(text, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
clearError();
dialog.dismiss();
Intent intent;
if (show_instructions)
{
intent = new Intent(getActivity(), RemediationInstructionsActivity.class);
intent.putParcelableArrayListExtra(RemediationInstructionsFragment.EXTRA_REMEDIATION_INSTRUCTIONS,
new ArrayList<RemediationInstruction>(instructions));
}
else
{
intent = new Intent(getActivity(), LogActivity.class);
}
startActivity(intent);
}
})
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int id)
{
clearError();
dialog.dismiss();
}
}).create();
mErrorDialog.setOnDismissListener(new DialogInterface.OnDismissListener()
mErrorText.setText(getString(R.string.error_introduction) + " " + getString(textid));
mErrorDetails.setText(text);
mErrorDetails.setOnClickListener(new OnClickListener()
{
@Override
public void onDismiss(DialogInterface dialog)
public void onClick(View v)
{
mErrorDialog = null;
Intent intent;
if (show_instructions)
{
intent = new Intent(getActivity(), RemediationInstructionsActivity.class);
intent.putParcelableArrayListExtra(RemediationInstructionsFragment.EXTRA_REMEDIATION_INSTRUCTIONS,
new ArrayList<RemediationInstruction>(instructions));
}
else
{
intent = new Intent(getActivity(), LogActivity.class);
}
startActivity(intent);
}
});
mErrorDialog.show();
mErrorView.setVisibility(View.VISIBLE);
}
}
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 Tobias Brunner
HSR Hochschule fuer Technik Rapperswil
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid
android:color="@color/panel_separator" />
</shape>
</item>
<item android:bottom="2dp">
<shape>
<solid
android:color="@android:color/white" />
</shape>
</item>
</layer-list>
@@ -16,7 +16,6 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="2dp"
android:background="@drawable/state_background"
android:orientation="vertical" >
@@ -18,80 +18,137 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="2dp"
android:background="@drawable/state_background"
android:orientation="vertical" >
android:orientation="vertical"
android:animateLayoutChanges="true" >
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="10dp"
android:columnCount="2"
android:rowCount="2" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:gravity="top"
android:text="@string/state_label"
android:textColor="?android:textColorPrimary"
android:textSize="20sp" />
<TextView
android:id="@+id/vpn_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="top"
android:text="@string/state_disabled"
android:textColor="?android:textColorSecondary"
android:textSize="20sp" />
<TextView
android:id="@+id/vpn_profile_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:gravity="top"
android:text="@string/profile_label"
android:textColor="?android:textColorPrimary"
android:textSize="20sp"
android:visibility="gone" >
</TextView>
<TextView
android:id="@+id/vpn_profile_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="top"
android:textSize="20sp"
android:visibility="gone" >
</TextView>
</GridLayout>
<ProgressBar
android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:indeterminate="true"
<LinearLayout
android:id="@+id/vpn_error"
android:visibility="gone"
style="@style/Widget.AppCompat.ProgressBar.Horizontal" />
<Button
android:id="@+id/action"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:text="@string/disconnect"
style="?android:attr/borderlessButtonStyle" >
</Button>
android:layout_height="match_parent"
android:background="@drawable/error_background"
android:orientation="vertical" >
<TextView
android:id="@+id/vpn_error_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="24dp"
android:layout_marginBottom="12dp"
android:text="Failed to establish VPN: Server is unreachable"
android:textColor="@color/primary_dark"
android:textSize="16sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
android:orientation="horizontal"
android:gravity="end" >
<Button
android:id="@+id/dismiss_error"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:text="@string/dismiss"
android:textColor="@color/primary"
android:textSize="14sp"
android:textStyle="bold"
style="?android:attr/borderlessButtonStyle" >
</Button>
<Button
android:id="@+id/error_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/show_log"
android:textColor="@color/primary"
android:textSize="14sp"
android:textStyle="bold"
style="?android:attr/borderlessButtonStyle" >
</Button>
</LinearLayout>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/state_background"
android:orientation="vertical" >
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="10dp"
android:columnCount="2"
android:rowCount="2" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:gravity="top"
android:text="@string/state_label"
android:textColor="?android:textColorPrimary"
android:textSize="20sp" />
<TextView
android:id="@+id/vpn_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="top"
android:text="@string/state_disabled"
android:textColor="?android:textColorSecondary"
android:textSize="20sp" />
<TextView
android:id="@+id/vpn_profile_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:gravity="top"
android:text="@string/profile_label"
android:textColor="?android:textColorPrimary"
android:textSize="20sp"
android:visibility="gone" >
</TextView>
<TextView
android:id="@+id/vpn_profile_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="top"
android:textSize="20sp"
android:visibility="gone" >
</TextView>
</GridLayout>
<ProgressBar
android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:indeterminate="true"
android:visibility="gone"
style="@style/Widget.AppCompat.ProgressBar.Horizontal" />
<Button
android:id="@+id/action"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:text="@string/disconnect"
style="?android:attr/borderlessButtonStyle" >
</Button>
</LinearLayout>
</LinearLayout>
@@ -163,6 +163,7 @@
<string name="state_disconnecting">Trennen&#8230;</string>
<string name="state_disabled">Kein aktives Profil</string>
<string name="state_error">Fehler</string>
<string name="dismiss">Ausblenden</string>
<!-- IMC state fragment -->
<string name="imc_state_label">Assessment:</string>
@@ -163,6 +163,7 @@
<string name="state_disconnecting">Przerywam połączenie&#8230;</string>
<string name="state_disabled">Brak aktywnego VPN</string>
<string name="state_error">Błąd</string>
<string name="dismiss">Dismiss</string>
<!-- IMC state fragment -->
<string name="imc_state_label">Assessment:</string>
@@ -160,6 +160,7 @@
<string name="state_disconnecting">Отключение&#8230;</string>
<string name="state_disabled">Нет активных VPN</string>
<string name="state_error">Ошибка</string>
<string name="dismiss">Dismiss</string>
<!-- IMC state fragment -->
<string name="imc_state_label">Assessment:</string>
@@ -161,6 +161,7 @@
<string name="state_disconnecting">Роз\'єднання&#8230;</string>
<string name="state_disabled">Немає активних VPN</string>
<string name="state_error">Помилка</string>
<string name="dismiss">Dismiss</string>
<!-- IMC state fragment -->
<string name="imc_state_label">Assessment:</string>
@@ -160,6 +160,7 @@
<string name="state_disconnecting">断开连接中&#8230;</string>
<string name="state_disabled">无活跃VPN</string>
<string name="state_error">错误</string>
<string name="dismiss">Dismiss</string>
<!-- IMC state fragment -->
<string name="imc_state_label">评估详情:</string>
@@ -160,6 +160,7 @@
<string name="state_disconnecting">結束連線中&#8230;</string>
<string name="state_disabled">無運作中的VPN</string>
<string name="state_error">錯誤</string>
<string name="dismiss">Dismiss</string>
<!-- IMC state fragment -->
<string name="imc_state_label">評估詳情:</string>
@@ -22,7 +22,7 @@
name="primary">#A2042C</color>
<color
name="primary_dark">#000000</color>
name="primary_dark">#323232</color>
<color
name="error_text">#D9192C</color>
@@ -163,6 +163,7 @@
<string name="state_disconnecting">Disconnecting&#8230;</string>
<string name="state_disabled">No active VPN</string>
<string name="state_error">Error</string>
<string name="dismiss">Dismiss</string>
<!-- IMC state fragment -->
<string name="imc_state_label">Assessment:</string>