android: Show connection errors as banner, not as modal dialog
authorTobias Brunner <tobias@strongswan.org>
Fri, 8 Jun 2018 13:41:46 +0000 (15:41 +0200)
committerTobias Brunner <tobias@strongswan.org>
Tue, 3 Jul 2018 09:31:35 +0000 (11:31 +0200)
12 files changed:
src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnStateFragment.java
src/frontends/android/app/src/main/res/drawable/error_background.xml [new file with mode: 0644]
src/frontends/android/app/src/main/res/layout/imc_state_fragment.xml
src/frontends/android/app/src/main/res/layout/vpn_state_fragment.xml
src/frontends/android/app/src/main/res/values-de/strings.xml
src/frontends/android/app/src/main/res/values-pl/strings.xml
src/frontends/android/app/src/main/res/values-ru/strings.xml
src/frontends/android/app/src/main/res/values-ua/strings.xml
src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml
src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml
src/frontends/android/app/src/main/res/values/colors.xml
src/frontends/android/app/src/main/res/values/strings.xml

index 81211d6..d682be4 100644 (file)
@@ -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()
+               mErrorText.setText(getString(R.string.error_introduction) + " " + getString(textid));
+               mErrorDetails.setText(text);
+               mErrorDetails.setOnClickListener(new OnClickListener()
+               {
+                       @Override
+                       public void onClick(View v)
                        {
-                               @Override
-                               public void onClick(DialogInterface dialog, int which)
+                               Intent intent;
+                               if (show_instructions)
                                {
-                                       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);
+                                       intent = new Intent(getActivity(), RemediationInstructionsActivity.class);
+                                       intent.putParcelableArrayListExtra(RemediationInstructionsFragment.EXTRA_REMEDIATION_INSTRUCTIONS,
+                                                       new ArrayList<RemediationInstruction>(instructions));
                                }
-                       })
-                       .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
-                       {
-                               @Override
-                               public void onClick(DialogInterface dialog, int id)
+                               else
                                {
-                                       clearError();
-                                       dialog.dismiss();
+                                       intent = new Intent(getActivity(), LogActivity.class);
                                }
-                       }).create();
-               mErrorDialog.setOnDismissListener(new DialogInterface.OnDismissListener()
-               {
-                       @Override
-                       public void onDismiss(DialogInterface dialog)
-                       {
-                               mErrorDialog = null;
+                               startActivity(intent);
                        }
                });
-               mErrorDialog.show();
+               mErrorView.setVisibility(View.VISIBLE);
        }
 }
diff --git a/src/frontends/android/app/src/main/res/drawable/error_background.xml b/src/frontends/android/app/src/main/res/drawable/error_background.xml
new file mode 100644 (file)
index 0000000..9f82757
--- /dev/null
@@ -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>
index 43219cf..ef5a63d 100644 (file)
@@ -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" >
 
index 92ad536..6807ae3 100644 (file)
 <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
+    <LinearLayout
+        android:id="@+id/vpn_error"
+        android:visibility="gone"
         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" >
+        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_marginRight="5dp"
-            android:gravity="top"
-            android:text="@string/state_label"
-            android:textColor="?android:textColorPrimary"
-            android:textSize="20sp" />
+            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" />
 
-        <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" />
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginBottom="8dp"
+            android:orientation="horizontal"
+            android:gravity="end" >
 
-        <TextView
-            android:id="@+id/vpn_profile_label"
-            android:layout_width="wrap_content"
+            <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_marginRight="5dp"
-            android:gravity="top"
-            android:text="@string/profile_label"
-            android:textColor="?android:textColorPrimary"
-            android:textSize="20sp"
-            android:visibility="gone" >
-        </TextView>
+            android:layout_marginBottom="10dp"
+            android:layout_marginLeft="20dp"
+            android:layout_marginRight="20dp"
+            android:layout_marginTop="10dp"
+            android:columnCount="2"
+            android:rowCount="2" >
 
-        <TextView
-            android:id="@+id/vpn_profile_name"
-            android:layout_width="wrap_content"
+            <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: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" />
+            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>
+        <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>
index 564a6cc..17b456d 100644 (file)
     <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>
index 8441c0a..bfa09ad 100644 (file)
     <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>
index 20259ef..cae13c4 100644 (file)
     <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>
index 1fc1c29..58e977f 100644 (file)
     <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>
index 8a886c5..7b2a644 100644 (file)
     <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>
index f2529a5..b085a11 100644 (file)
     <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>
index eedfe23..11d4e24 100644 (file)
@@ -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>
index 584a2d9..dc90b11 100644 (file)
     <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>