diff --git a/android/BOINC/res/layout/eventlog_layout_listitem.xml b/android/BOINC/res/layout/eventlog_layout_listitem.xml
index 7712a679a5..97d616a468 100644
--- a/android/BOINC/res/layout/eventlog_layout_listitem.xml
+++ b/android/BOINC/res/layout/eventlog_layout_listitem.xml
@@ -17,51 +17,39 @@
You should have received a copy of the GNU Lesser General Public License
along with BOINC. If not, see .
-->
-
+ android:layout_height="match_parent" >
+ android:focusable="false"
+ android:layout_margin="5dp" />
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/android/BOINC/src/edu/berkeley/boinc/BOINCActivity.java b/android/BOINC/src/edu/berkeley/boinc/BOINCActivity.java
index 4a6afc6fe5..cd3902f347 100644
--- a/android/BOINC/src/edu/berkeley/boinc/BOINCActivity.java
+++ b/android/BOINC/src/edu/berkeley/boinc/BOINCActivity.java
@@ -72,7 +72,7 @@ public class BOINCActivity extends TabActivity {
private BroadcastReceiver mClientStatusChangeRec = new BroadcastReceiver() {
@Override
public void onReceive(Context context,Intent intent) {
- Log.d(TAG, "ClientStatusChange - onReceive()");
+ //Log.d(TAG, "ClientStatusChange - onReceive()");
determineStatus();
}
@@ -142,10 +142,10 @@ public class BOINCActivity extends TabActivity {
try {
if(mIsBound) {
newStatus = Monitor.getClientStatus().setupStatus;
- Log.d(TAG,"determineStatus() old clientSetupStatus: " + clientSetupStatus + " - newStatus: " + newStatus);
if(newStatus != clientSetupStatus) { //only act, when status actually different form old status
+ Log.d(TAG,"determineStatus() client setup status changed! old clientSetupStatus: " + clientSetupStatus + " - new: " + newStatus);
clientSetupStatus = newStatus;
- layout();
+ layout();
}
if(intialStart && (clientSetupStatus == ClientStatus.SETUP_STATUS_NOPROJECT)) { // if it is first start and no project attached, show login activity
startActivity(new Intent(this,AttachProjectListActivity.class));
diff --git a/android/BOINC/src/edu/berkeley/boinc/EventLogActivity.java b/android/BOINC/src/edu/berkeley/boinc/EventLogActivity.java
index fa61eedb25..3a76060ca1 100644
--- a/android/BOINC/src/edu/berkeley/boinc/EventLogActivity.java
+++ b/android/BOINC/src/edu/berkeley/boinc/EventLogActivity.java
@@ -19,20 +19,24 @@
package edu.berkeley.boinc;
import java.util.ArrayList;
+import java.util.List;
import java.lang.StringBuffer;
import edu.berkeley.boinc.adapter.EventLogListAdapter;
import edu.berkeley.boinc.client.Monitor;
import edu.berkeley.boinc.rpc.Message;
-import android.content.BroadcastReceiver;
-import android.content.Context;
+import android.content.ComponentName;
import android.content.Intent;
-import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.IBinder;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuInflater;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
import android.widget.ListView;
import android.widget.TextView;
@@ -40,47 +44,26 @@ import android.widget.TextView;
public class EventLogActivity extends FragmentActivity {
private final String TAG = "BOINC EventLogActivity";
+
+ private Monitor monitor;
+ private Boolean mIsBound = false;
private ListView lv;
private EventLogListAdapter listAdapter;
private ArrayList data = new ArrayList();
- private int lastSeqno = 0;
-
- // Controls whether initialization of view elements of "projects_layout"
- // is required. This is the case, every time the layout switched.
- private Boolean initialSetupRequired = true;
- // BroadcastReceiver event is used to update the UI with updated information from
- // the client. This is generally called once a second.
- //
- private IntentFilter ifcsc = new IntentFilter("edu.berkeley.boinc.clientstatuschange");
- private BroadcastReceiver mClientStatusChangeRec = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.d(TAG, "ClientStatusChange - onReceive()");
-
- populateLayout();
- }
- };
-
+ // message retrieval
+ private Integer pastMsgsLoadingRange = 50; // amount messages loaded when end of list is reached
- //
- // Message Activity
- //
@Override
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate()");
+ doBindService();
+ setLayoutLoading();
+
super.onCreate(savedInstanceState);
}
-
- @Override
- public void onPause() {
- Log.d(TAG, "onPause()");
-
- unregisterReceiver(mClientStatusChangeRec);
- super.onPause();
- }
@Override
public void onResume() {
@@ -88,47 +71,87 @@ public class EventLogActivity extends FragmentActivity {
super.onResume();
- populateLayout();
-
- registerReceiver(mClientStatusChangeRec, ifcsc);
+ new RetrieveRecentMsgs().execute(); // refresh messages
}
@Override
protected void onDestroy() {
Log.d(TAG, "onDestroy()");
-
+ doUnbindService();
super.onDestroy();
}
- private void populateLayout() {
+ /*
+ * Service binding part
+ * only necessary, when function on monitor instance has to be called
+ */
+ private ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ Log.d(TAG,"onServiceConnected");
+ monitor = ((Monitor.LocalBinder)service).getService();
+ mIsBound = true;
+ initializeLayout();
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ monitor = null;
+ mIsBound = false;
+ }
+ };
+
+ private void doBindService() {
+ if(!mIsBound) {
+ getApplicationContext().bindService(new Intent(this, Monitor.class), mConnection, 0); //calling within Tab needs getApplicationContext() for bindService to work!
+ }
+ }
+
+ private void doUnbindService() {
+ if (mIsBound) {
+ getApplicationContext().unbindService(mConnection);
+ mIsBound = false;
+ }
+ }
+
+ // updates data list with most recent messages
+ private void loadRecentMsgs(ArrayList tmpA) {
+ // Prepend new messages to the event log
try {
- // Read messages from state saved in ClientStatus
- ArrayList tmpA = Monitor.getClientStatus().getMessages();
-
- if(tmpA == null) {
+ int y = 0;
+ for (int x = tmpA.size()-1; x >= 0; x--) {
+ data.add(y, tmpA.get(x));
+ y++;
+ }
+ } catch (Exception e) {} //IndexOutOfBoundException
+ listAdapter.notifyDataSetChanged();
+ }
+
+ // appends older messages to data list
+ private void loadPastMsgs(List tmpA) {
+ // Append old messages to the event log
+ try {
+ for(int x = tmpA.size()-1; x >= 0; x--) {
+ data.add(tmpA.get(x));
+ }
+ } catch (Exception e) {} //IndexOutOfBoundException
+
+ listAdapter.notifyDataSetChanged();
+ }
+
+ private void initializeLayout() {
+ try {
+ // check whether monitor is bound
+ if(!mIsBound) {
setLayoutLoading();
return;
}
-
- // Switch to a view that can actually display messages
- if (initialSetupRequired) {
- initialSetupRequired = false;
- setContentView(R.layout.eventlog_layout);
- lv = (ListView) findViewById(R.id.eventlogList);
- listAdapter = new EventLogListAdapter(EventLogActivity.this, lv, R.id.eventlogList, data);
- }
-
- // Add new messages to the event log
- for (Message msg: tmpA) {
- if (msg.seqno > lastSeqno) {
- data.add(msg);
- lastSeqno = msg.seqno;
- }
- }
-
- // Force list adapter to refresh
- listAdapter.notifyDataSetChanged();
+
+ setContentView(R.layout.eventlog_layout);
+ lv = (ListView) findViewById(R.id.eventlogList);
+ listAdapter = new EventLogListAdapter(EventLogActivity.this, lv, R.id.eventlogList, data);
+ lv.setOnScrollListener(new EndlessScrollListener(5));
+ // initial data retrieval
+ new RetrievePastMsgs().execute();
} catch (Exception e) {
// data retrieval failed, set layout to loading...
setLayoutLoading();
@@ -139,7 +162,6 @@ public class EventLogActivity extends FragmentActivity {
setContentView(R.layout.generic_layout_loading);
TextView loadingHeader = (TextView)findViewById(R.id.loading_header);
loadingHeader.setText(R.string.eventlog_loading);
- initialSetupRequired = true;
}
@Override
@@ -217,4 +239,94 @@ public class EventLogActivity extends FragmentActivity {
startActivity(Intent.createChooser(emailIntent, "Send mail..."));
}
+
+ // onScrollListener for list view, implementing "endless scrolling"
+ public final class EndlessScrollListener implements OnScrollListener {
+
+ private int visibleThreshold = 5;
+ private int previousTotal = 0;
+ private boolean loading = true;
+
+ public EndlessScrollListener(int visibleThreshold) {
+ this.visibleThreshold = visibleThreshold;
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+ if (loading) {
+ if (totalItemCount > previousTotal) {
+ loading = false;
+ previousTotal = totalItemCount;
+ }
+ }
+ if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
+ new RetrievePastMsgs().execute();
+ loading = true;
+ }
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ }
+ }
+
+ private final class RetrieveRecentMsgs extends AsyncTask> {
+
+ private Integer mostRecentSeqNo = 0;
+
+ @Override
+ protected void onPreExecute() {
+ if(!mIsBound) cancel(true); // cancel execution if monitor is not bound yet
+ try {
+ mostRecentSeqNo = data.get(0).seqno;
+ } catch (Exception e) {} //IndexOutOfBoundException
+ }
+
+ @Override
+ protected ArrayList doInBackground(Void... params) {
+ return monitor.getEventLogMessages(mostRecentSeqNo);
+ }
+
+ @Override
+ protected void onPostExecute(ArrayList result) {
+ // back in UI thread
+ loadRecentMsgs(result);
+ }
+ }
+
+ private final class RetrievePastMsgs extends AsyncTask> {
+
+ private Integer mostRecentSeqNo = null;
+ private Integer pastSeqNo = null;
+
+ @Override
+ protected void onPreExecute() {
+ if(!mIsBound) cancel(true); // cancel execution if monitor is not bound yet
+ try {
+ mostRecentSeqNo = data.get(0).seqno;
+ pastSeqNo = data.get(data.size()-1).seqno;
+ Log.d("RetrievePastMsgs","mostRecentSeqNo: " + mostRecentSeqNo + " ; pastSeqNo: " + pastSeqNo);
+ if(pastSeqNo==0) {
+ Log.d("RetrievePastMsgs", "cancel, all past messages are present");
+ cancel(true); // cancel if all past messages are present
+ }
+ } catch (Exception e) {} //IndexOutOfBoundException
+ }
+
+ @Override
+ protected List doInBackground(Void... params) {
+ Integer startIndex = 0;
+ if(mostRecentSeqNo != null && pastSeqNo != null && mostRecentSeqNo != 0 && pastSeqNo != 0) startIndex = mostRecentSeqNo - pastSeqNo + 1;
+ Integer lastIndexOfList = 0;
+ if(mostRecentSeqNo != null) lastIndexOfList = mostRecentSeqNo - 1;
+ //Log.d("RetrievePastMsgs", "calling monitor with: " + startIndex + lastIndexOfList);
+ return monitor.getEventLogMessages(startIndex, pastMsgsLoadingRange, lastIndexOfList);
+ }
+
+ @Override
+ protected void onPostExecute(List result) {
+ // back in UI thread
+ loadPastMsgs(result);
+ }
+ }
}
diff --git a/android/BOINC/src/edu/berkeley/boinc/PrefsActivity.java b/android/BOINC/src/edu/berkeley/boinc/PrefsActivity.java
index 7e9fcd7786..81418c2cd7 100644
--- a/android/BOINC/src/edu/berkeley/boinc/PrefsActivity.java
+++ b/android/BOINC/src/edu/berkeley/boinc/PrefsActivity.java
@@ -71,8 +71,6 @@ public class PrefsActivity extends FragmentActivity {
/*
* Service binding part
* only necessary, when function on monitor instance has to be called
- * currently in Prefs- and DebugActivity
- *
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
diff --git a/android/BOINC/src/edu/berkeley/boinc/adapter/EventLogListAdapter.java b/android/BOINC/src/edu/berkeley/boinc/adapter/EventLogListAdapter.java
index 1993dc626c..614ef30e3e 100644
--- a/android/BOINC/src/edu/berkeley/boinc/adapter/EventLogListAdapter.java
+++ b/android/BOINC/src/edu/berkeley/boinc/adapter/EventLogListAdapter.java
@@ -117,7 +117,12 @@ public class EventLogListAdapter extends ArrayAdapter implements OnItem
viewEventLog.cbCheck.setChecked(listView.isItemChecked(position));
viewEventLog.tvMessage.setText(getMessage(position));
viewEventLog.tvDate.setText(getDate(position));
- viewEventLog.tvProjectName.setText(getProject(position));
+ if(getProject(position).isEmpty()){
+ viewEventLog.tvProjectName.setVisibility(View.GONE);
+ } else {
+ viewEventLog.tvProjectName.setVisibility(View.VISIBLE);
+ viewEventLog.tvProjectName.setText(getProject(position));
+ }
return vi;
}
diff --git a/android/BOINC/src/edu/berkeley/boinc/client/ClientStatus.java b/android/BOINC/src/edu/berkeley/boinc/client/ClientStatus.java
index abd90d8bf9..39f072f181 100644
--- a/android/BOINC/src/edu/berkeley/boinc/client/ClientStatus.java
+++ b/android/BOINC/src/edu/berkeley/boinc/client/ClientStatus.java
@@ -27,7 +27,6 @@ import android.os.PowerManager.WakeLock;
import android.util.Log;
import edu.berkeley.boinc.rpc.CcStatus;
import edu.berkeley.boinc.rpc.GlobalPreferences;
-import edu.berkeley.boinc.rpc.Message;
import edu.berkeley.boinc.rpc.Project;
import edu.berkeley.boinc.rpc.Result;
import edu.berkeley.boinc.rpc.Transfer;
@@ -51,7 +50,6 @@ public class ClientStatus {
private ArrayList projects;
private ArrayList transfers;
private GlobalPreferences prefs;
- private ArrayList messages; //debug tab
// setup status
public Integer setupStatus = 0;
@@ -130,12 +128,11 @@ public class ClientStatus {
/*
* called frequently by Monitor to set the RPC data. These objects are used to determine the client status and parse it in the data model of this class.
*/
- public synchronized void setClientStatus(CcStatus status,ArrayList results,ArrayList projects, ArrayList transfers, ArrayList msgs) {
+ public synchronized void setClientStatus(CcStatus status,ArrayList results,ArrayList projects, ArrayList transfers) {
this.status = status;
this.results = results;
this.projects = projects;
this.transfers = transfers;
- this.messages = msgs;
parseClientStatus();
Log.d(TAG,"parsing results: computing: " + computingParseError + computingStatus + computingSuspendReason + " - network: " + networkParseError + networkStatus + networkSuspendReason);
if(!computingParseError && !networkParseError && !setupStatusParseError) {
@@ -203,23 +200,6 @@ public class ClientStatus {
return projects;
}
- //Debug Tab
- public synchronized ArrayList getMessages() {
- if(messages == null) { //check in case monitor is not set up yet (e.g. while logging in)
- Log.d(TAG, "messages is null");
- return null;
- }
- return messages;
- }
-
- public synchronized void resetMessages() {
- if(messages == null) { //check in case monitor is not set up yet (e.g. while logging in)
- Log.d(TAG, "messages is null");
- return;
- }
- messages.clear();
- }
-
/*
* parses RPC data to ClientStatus data model.
*/
diff --git a/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java b/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java
index 4775ad83bd..e822d32363 100644
--- a/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java
+++ b/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java
@@ -32,6 +32,8 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
+
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
@@ -851,6 +853,40 @@ public class Monitor extends Service {
return auth;
}
+ // returns client messages to be shown in EventLog tab.
+ // start is counting from beginning, e.g. start=0 number=50
+ // needs lastIndexOfList as reference for consistency
+ // returns 50 most recent messages
+ public List getEventLogMessages(int startIndex, int number, int lastIndexOfList) {
+ //Log.d(TAG,"getEventLogMessage from start: " + startIndex + " amount: " + number + "lastIndexOfList: " + lastIndexOfList);
+ Integer requestedLastIndex = startIndex + number;
+ if(lastIndexOfList == 0) { // list is empty, initial start
+ lastIndexOfList = rpc.getMessageCount() - 1; // possible lastIndexOfList if all messages were read
+ //Log.d(TAG,"list ist empty, initial start, read message count: " + (lastIndexOfList+1));
+ }
+ if(requestedLastIndex > lastIndexOfList + 1) { // requesting more messages than actually present
+ number = lastIndexOfList - startIndex + 1;
+ //Log.d(TAG,"getEventLogMessage requesting more messages than left, number changed to " + number);
+ }
+ Integer param = lastIndexOfList + 1 - startIndex - number;
+ //Log.d(TAG,"getEventLogMessage calling RPC with: " + param);
+ try {
+ ArrayList tmpL = rpc.getMessages(param);
+ List msgs = tmpL.subList(0, tmpL.size() - startIndex); // tmp.size - start is amount of actually new values. Usually equals number, except for end of list
+ //Log.d(TAG,"getEventLogMessages returning " + msgs.size() + " messages with oldest element seq no: " + msgs.get(0).seqno + " and recent seq no: " + msgs.get(msgs.size()-1).seqno);
+ return msgs;
+ } catch (Exception e) {
+ //Log.w(TAG,"error in retrieving sublist", e);
+ return null;
+ }
+ }
+
+ // returns client messages that are more recent than given seqNo
+ public ArrayList getEventLogMessages(int seqNo) {
+ //Log.d(TAG, "getEventLogMessage more recent than seqNo: " + seqNo);
+ return rpc.getMessages(seqNo);
+ }
+
private final class ClientMonitorAsync extends AsyncTask {
private final String TAG = "BOINC ClientMonitorAsync";
@@ -884,16 +920,9 @@ public class Monitor extends Service {
ArrayList projects = rpc.getProjectStatus();
if(showRpcCommands) Log.d(TAG, "getTransers");
ArrayList transfers = rpc.getFileTransfers();
- ArrayList msgs = new ArrayList();
- // retrieve messages only, if tabs are actually enabled. very resource intense with logging on emulator!
- if(getResources().getBoolean(R.bool.tab_eventlog)) {
- Integer count = rpc.getMessageCount();
- msgs = rpc.getMessages(count - 250); //get the most recent 250 messages
- if(showRpcCommands) Log.d(TAG, "getMessages, count: " + count);
- }
if( (status != null) && (results != null) && (projects != null) && (transfers != null)) {
- Monitor.getClientStatus().setClientStatus(status, results, projects, transfers, msgs);
+ Monitor.getClientStatus().setClientStatus(status, results, projects, transfers);
} else {
Log.d(TAG, "client status connection problem");
}