]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
android: Use ListView for log messages
authorTobias Brunner <tobias@strongswan.org>
Mon, 2 Jul 2018 16:05:13 +0000 (18:05 +0200)
committerTobias Brunner <tobias@strongswan.org>
Tue, 3 Jul 2018 09:31:44 +0000 (11:31 +0200)
This is hopefully a bit more efficient for large log files than the previous
single TextView.  The ListView widget also provides an auto-scroll mechanism.

src/frontends/android/app/src/main/java/org/strongswan/android/ui/LogFragment.java
src/frontends/android/app/src/main/java/org/strongswan/android/ui/LogScrollView.java [deleted file]
src/frontends/android/app/src/main/res/layout/log_fragment.xml
src/frontends/android/app/src/main/res/layout/log_list_item.xml [new file with mode: 0644]

index 2dda82a57fbc37887d0eebfe0e1266d91d2924a4..625940fd4f19419d97bb48193121a9abecaf5952 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2017 Tobias Brunner
+ * Copyright (C) 2012-2018 Tobias Brunner
  * HSR Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
 
 package org.strongswan.android.ui;
 
+import android.content.Context;
 import android.os.Bundle;
 import android.os.FileObserver;
 import android.os.Handler;
+import android.support.annotation.NonNull;
 import android.support.v4.app.Fragment;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.TextView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
 
 import org.strongswan.android.R;
 import org.strongswan.android.logic.CharonVpnService;
@@ -34,16 +37,15 @@ import java.io.FileReader;
 import java.io.StringReader;
 import java.util.ArrayList;
 
-public class LogFragment extends Fragment implements Runnable
+public class LogFragment extends Fragment
 {
+       private static String SCROLL_POSITION = "SCROLL_POSITION";
        private String mLogFilePath;
        private Handler mLogHandler;
-       private TextView mLogView;
-       private LogScrollView mScrollView;
-       private BufferedReader mReader;
-       private Thread mThread;
-       private volatile boolean mRunning;
+       private ListView mLog;
+       private LogAdapter mLogAdapter;
        private FileObserver mDirectoryObserver;
+       private int mScrollPosition;
 
        @Override
        public void onCreate(Bundle savedInstanceState)
@@ -51,7 +53,7 @@ public class LogFragment extends Fragment implements Runnable
                super.onCreate(savedInstanceState);
 
                mLogFilePath = getActivity().getFilesDir() + File.separator + CharonVpnService.LOG_FILE;
-               /* use a handler to update the log view */
+
                mLogHandler = new Handler();
 
                mDirectoryObserver = new LogDirectoryObserver(getActivity().getFilesDir().getAbsolutePath());
@@ -61,16 +63,39 @@ public class LogFragment extends Fragment implements Runnable
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
                View view = inflater.inflate(R.layout.log_fragment, null);
-               mLogView = (TextView)view.findViewById(R.id.log_view);
-               mScrollView = (LogScrollView)view.findViewById(R.id.scroll_view);
+
+               mLogAdapter = new LogAdapter(getActivity());
+               mLog = view.findViewById(R.id.log);
+               mLog.setAdapter(mLogAdapter);
+
+               mScrollPosition = -1;
+               if (savedInstanceState != null)
+               {
+                       mScrollPosition = savedInstanceState.getInt(SCROLL_POSITION, mScrollPosition);
+               }
                return view;
        }
 
+       @Override
+       public void onSaveInstanceState(Bundle outState)
+       {
+               super.onSaveInstanceState(outState);
+
+               if (mLog.getLastVisiblePosition() == (mLogAdapter.getCount() - 1))
+               {
+                       outState.putInt(SCROLL_POSITION, -1);
+               }
+               else
+               {
+                       outState.putInt(SCROLL_POSITION, mLog.getFirstVisiblePosition());
+               }
+       }
+
        @Override
        public void onStart()
        {
                super.onStart();
-               startLogReader();
+               mLogAdapter.restart();
                mDirectoryObserver.startWatching();
        }
 
@@ -79,118 +104,115 @@ public class LogFragment extends Fragment implements Runnable
        {
                super.onStop();
                mDirectoryObserver.stopWatching();
-               stopLogReader();
+               mLogAdapter.stop();
        }
 
-       /**
-        * Start reading from the log file
-        */
-       private void startLogReader()
+       private class LogAdapter extends ArrayAdapter<String> implements Runnable
        {
-               try
+               private BufferedReader mReader;
+               private Thread mThread;
+               private volatile boolean mRunning;
+
+               public LogAdapter(@NonNull Context context)
                {
-                       mReader = new BufferedReader(new FileReader(mLogFilePath));
+                       super(context, R.layout.log_list_item, R.id.log_line);
                }
-               catch (FileNotFoundException e)
+
+               public void restart()
                {
-                       mReader = new BufferedReader(new StringReader(""));
-               }
+                       if (mRunning)
+                       {
+                               stop();
+                       }
 
-               mLogView.setText("");
-               mRunning = true;
-               mThread = new Thread(this);
-               mThread.start();
-       }
+                       clear();
 
-       /**
-        * Stop reading from the log file
-        */
-       private void stopLogReader()
-       {
-               try
-               {
-                       mRunning = false;
-                       mThread.interrupt();
-                       mThread.join();
+                       try
+                       {
+                               mReader = new BufferedReader(new FileReader(mLogFilePath));
+                       }
+                       catch (FileNotFoundException e)
+                       {
+                               mReader = new BufferedReader(new StringReader(""));
+                       }
+                       mRunning = true;
+                       mThread = new Thread(this);
+                       mThread.start();
                }
-               catch (InterruptedException e)
+
+               public void stop()
                {
+                       try
+                       {
+                               mRunning = false;
+                               mThread.interrupt();
+                               mThread.join();
+                       }
+                       catch (InterruptedException e)
+                       {
+                       }
                }
-       }
 
-       /**
-        * Write the given log line to the TextView. We strip the prefix off to save
-        * some space in narrow views (it is not that helpful for regular users anyway).
-        *
-        * @param lines log lines to log
-        */
-       public void logLines(final ArrayList<String> lines)
-       {
-               mLogHandler.post(new Runnable() {
-                       @Override
-                       public void run()
-                       {
-                               mLogView.beginBatchEdit();
+               private void logLines(final ArrayList<String> lines)
+               {
+                       mLogHandler.post(() -> {
+                               boolean scroll = getCount() == 0;
+                               setNotifyOnChange(false);
                                for (String line : lines)
                                {
                                        if (getResources().getConfiguration().screenWidthDp < 600)
                                        {       /* strip off prefix (month=3, day=2, time=8, thread=2, spaces=3) */
                                                line = line.length() > 18 ? line.substring(18) : line;
                                        }
-                                       mLogView.append(line + '\n');
+                                       add(line);
                                }
-                               mLogView.endBatchEdit();
-                               /* calling autoScroll() directly does not work, probably because content
-                                * is not yet updated, so we post this to be done later */
-                               mScrollView.post(new Runnable() {
-                                       @Override
-                                       public void run()
-                                       {
-                                               mScrollView.autoScroll();
-                                       }
-                               });
-                       }
-               });
-       }
-
-       @Override
-       public void run()
-       {
-               ArrayList<String> lines = null;
+                               notifyDataSetChanged();
+                               if (scroll)
+                               {       /* scroll to the bottom or saved position after adding the first batch */
+                                       mLogHandler.post(() -> mLog.setSelection(mScrollPosition == -1 ? getCount() - 1 : mScrollPosition));
+                               }
+                       });
+               }
 
-               while (mRunning)
+               @Override
+               public void run()
                {
-                       try
-                       {       /* this works as long as the file is not truncated */
-                               String line = mReader.readLine();
-                               if (line == null)
-                               {
-                                       if (lines != null)
+                       ArrayList<String> lines = null;
+
+                       while (mRunning)
+                       {
+                               try
+                               {       /* this works as long as the file is not truncated */
+                                       String line = mReader.readLine();
+                                       if (line == null)
                                        {
-                                               logLines(lines);
-                                               lines = null;
+                                               if (lines != null)
+                                               {
+                                                       logLines(lines);
+                                                       lines = null;
+                                               }
+                                               /* wait until there is more to log */
+                                               Thread.sleep(1000);
                                        }
-                                       /* wait until there is more to log */
-                                       Thread.sleep(1000);
-                               }
-                               else
-                               {
-                                       if (lines == null)
+                                       else
                                        {
-                                               lines = new ArrayList<>();
+                                               if (lines == null)
+                                               {
+                                                       lines = new ArrayList<>();
+                                               }
+                                               lines.add(line);
                                        }
-                                       lines.add(line);
+                               }
+                               catch (Exception e)
+                               {
+                                       break;
                                }
                        }
-                       catch (Exception e)
+                       if (lines != null)
                        {
-                               break;
+                               logLines(lines);
                        }
                }
-               if (lines != null)
-               {
-                       logLines(lines);
-               }
        }
 
        /**
@@ -243,8 +265,7 @@ public class LogFragment extends Fragment implements Runnable
                                @Override
                                public void run()
                                {
-                                       stopLogReader();
-                                       startLogReader();
+                                       mLogAdapter.restart();
                                }
                        });
                }
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/LogScrollView.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/LogScrollView.java
deleted file mode 100644 (file)
index cf25022..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2012 Tobias Brunner
- * Copyright (C) 2012 Giuliano Grassi
- * Copyright (C) 2012 Ralf Sager
- * 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.
- */
-
-package org.strongswan.android.ui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.ScrollView;
-
-public class LogScrollView extends ScrollView
-{
-       private boolean mAutoScroll = true;
-
-       public LogScrollView(Context context)
-       {
-               super(context);
-       }
-
-       public LogScrollView(Context context, AttributeSet attrs)
-       {
-               super(context, attrs);
-       }
-
-       public LogScrollView(Context context, AttributeSet attrs, int defStyle)
-       {
-               super(context, attrs, defStyle);
-       }
-
-       @Override
-       public boolean onTouchEvent(MotionEvent ev)
-       {
-               /* disable auto-scrolling when the user starts scrolling around */
-               if (ev.getActionMasked() == MotionEvent.ACTION_DOWN)
-               {
-                       mAutoScroll = false;
-               }
-               return super.onTouchEvent(ev);
-       }
-
-       /**
-        * Call this to move newly added content into view by scrolling to the bottom.
-        * Nothing happens if auto-scrolling is disabled.
-        */
-       public void autoScroll()
-       {
-               if (mAutoScroll)
-               {
-                       fullScroll(View.FOCUS_DOWN);
-               }
-       }
-
-       @Override
-       protected void onScrollChanged(int l, int t, int oldl, int oldt)
-       {
-               super.onScrollChanged(l, t, oldl, oldt);
-               /* if the user scrolls to the bottom we enable auto-scrolling again */
-               if (t == getChildAt(getChildCount() - 1).getHeight() - getHeight())
-               {
-                       mAutoScroll = true;
-               }
-       }
-}
index 6d6abfe16440c741c919808cbc3d63cd6a6ab261..a51ad4d79a212d549166b86cb882595abf482443 100644 (file)
@@ -1,8 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    Copyright (C) 2012 Tobias Brunner
-    Copyright (C) 2012 Giuliano Grassi
-    Copyright (C) 2012 Ralf Sager
+    Copyright (C) 2012-2018 Tobias Brunner
     HSR Hochschule fuer Technik Rapperswil
 
     This program is free software; you can redistribute it and/or modify it
     android:layout_height="match_parent"
     android:orientation="vertical" >
 
-    <org.strongswan.android.ui.LogScrollView
-        android:id="@+id/scroll_view"
+    <ListView
+        android:id="@+id/log"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_margin="10dp"
+        android:dividerHeight="0dp"
+        android:divider="@null"
+        android:fadeScrollbars="false"
         android:scrollbarFadeDuration="0"
-        android:scrollbarAlwaysDrawVerticalTrack="true" >
+        android:scrollbarAlwaysDrawVerticalTrack="true"
+        android:transcriptMode="normal">
 
-        <TextView
-            android:id="@+id/log_view"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textSize="9sp"
-            android:typeface="monospace"
-            android:fontFamily="monospace" >
-        </TextView>
-
-    </org.strongswan.android.ui.LogScrollView>
+    </ListView>
 
 </LinearLayout>
diff --git a/src/frontends/android/app/src/main/res/layout/log_list_item.xml b/src/frontends/android/app/src/main/res/layout/log_list_item.xml
new file mode 100644 (file)
index 0000000..1fd42ce
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2012-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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/log_line"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:textSize="9sp"
+    android:typeface="monospace"
+    app:fontFamily="monospace" />