Export logs to SD card preference

fiaxh created

Change summary

src/main/AndroidManifest.xml                                         |   1 
src/main/java/eu/siacs/conversations/services/ExportLogsService.java | 141 
src/main/java/eu/siacs/conversations/ui/ExportLogsPreference.java    |  29 
src/main/res/values/strings.xml                                      |   3 
src/main/res/xml/preferences.xml                                     |   4 
5 files changed, 178 insertions(+)

Detailed changes

src/main/AndroidManifest.xml 🔗

@@ -149,6 +149,7 @@
                 android:name="android.support.PARENT_ACTIVITY"
                 android:value="eu.siacs.conversations.ui.SettingsActivity" />
         </activity>
+        <service android:name=".services.ExportLogsService" />
     </application>
 
 </manifest>

src/main/java/eu/siacs/conversations/services/ExportLogsService.java 🔗

@@ -0,0 +1,141 @@
+package eu.siacs.conversations.services;
+
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.support.v4.app.NotificationCompat;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.persistance.DatabaseBackend;
+import eu.siacs.conversations.persistance.FileBackend;
+import eu.siacs.conversations.xmpp.jid.Jid;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class ExportLogsService extends Service {
+
+	private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+	private static final String DIRECTORY_STRING_FORMAT = FileBackend.getConversationsFileDirectory() + "/logs/%s";
+	private static final String MESSAGE_STRING_FORMAT = "(%s) %s: %s\n";
+	private static final int NOTIFICATION_ID = 1;
+	private static AtomicBoolean running = new AtomicBoolean(false);
+
+	@Override
+	public int onStartCommand(Intent intent, int flags, int startId) {
+		if (running.compareAndSet(false, true)) {
+			new Thread(
+					new Runnable() {
+						DatabaseBackend databaseBackend = DatabaseBackend.getInstance(getBaseContext());
+						List<Account> accounts = databaseBackend.getAccounts();
+
+						@Override
+						public void run() {
+							List<Conversation> conversations = databaseBackend.getConversations(Conversation.STATUS_AVAILABLE);
+							conversations.addAll(databaseBackend.getConversations(Conversation.STATUS_ARCHIVED));
+
+							NotificationManager mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+							NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext());
+							mBuilder.setContentTitle(getString(R.string.notification_export_logs_title))
+									.setSmallIcon(R.drawable.ic_notification)
+									.setProgress(conversations.size(), 0, false);
+							startForeground(NOTIFICATION_ID, mBuilder.build());
+
+							int progress = 0;
+							for (Conversation conversation : conversations) {
+								writeToFile(conversation);
+								progress++;
+								mBuilder.setProgress(conversations.size(), progress, false);
+								mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
+							}
+
+							running.set(false);
+							stopForeground(true);
+						}
+
+						private void writeToFile(Conversation conversation) {
+							Jid accountJid = resolveAccountUuid(conversation.getAccountUuid());
+							Jid contactJid = conversation.getJid();
+
+							File dir = new File(String.format(DIRECTORY_STRING_FORMAT,
+									accountJid.toBareJid().toString()));
+							dir.mkdirs();
+
+							BufferedWriter bw = null;
+							try {
+								for (Message message : databaseBackend.getMessagesIterable(conversation)) {
+									if (message.getType() == Message.TYPE_TEXT || message.hasFileOnRemoteHost()) {
+										String date = simpleDateFormat.format(new Date(message.getTimeSent()));
+										if (bw == null) {
+											bw = new BufferedWriter(new FileWriter(
+													new File(dir, contactJid.toBareJid().toString() + ".txt")));
+										}
+										String jid = null;
+										switch (message.getStatus()) {
+											case Message.STATUS_RECEIVED:
+												jid = getMessageCounterpart(message);
+												break;
+											case Message.STATUS_SEND:
+											case Message.STATUS_SEND_RECEIVED:
+											case Message.STATUS_SEND_DISPLAYED:
+												jid = accountJid.toBareJid().toString();
+												break;
+										}
+										if (jid != null) {
+											String body = message.hasFileOnRemoteHost() ? message.getFileParams().url.toString() : message.getBody();
+											bw.write(String.format(MESSAGE_STRING_FORMAT, date, jid,
+													body.replace("\\\n", "\\ \n").replace("\n", "\\ \n")));
+										}
+									}
+								}
+							} catch (IOException e) {
+								e.printStackTrace();
+							} finally {
+								try {
+									if (bw != null) {
+										bw.close();
+									}
+								} catch (IOException e1) {
+									e1.printStackTrace();
+								}
+							}
+						}
+
+						private Jid resolveAccountUuid(String accountUuid) {
+							for (Account account : accounts) {
+								if (account.getUuid().equals(accountUuid)) {
+									return account.getJid();
+								}
+							}
+							return null;
+						}
+
+						private String getMessageCounterpart(Message message) {
+							String trueCounterpart = (String) message.getContentValues().get(Message.TRUE_COUNTERPART);
+							if (trueCounterpart != null) {
+								return trueCounterpart;
+							} else {
+								return message.getCounterpart().toString();
+							}
+						}
+					}).start();
+		}
+		return START_STICKY;
+	}
+
+	@Override
+	public IBinder onBind(Intent intent) {
+		return null;
+	}
+}

src/main/java/eu/siacs/conversations/ui/ExportLogsPreference.java 🔗

@@ -0,0 +1,29 @@
+package eu.siacs.conversations.ui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.preference.Preference;
+import android.util.AttributeSet;
+
+import eu.siacs.conversations.services.ExportLogsService;
+
+public class ExportLogsPreference extends Preference {
+
+    public ExportLogsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public ExportLogsPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public ExportLogsPreference(Context context) {
+        super(context);
+    }
+
+    protected void onClick() {
+		final Intent startIntent = new Intent(getContext(), ExportLogsService.class);
+		getContext().startService(startIntent);
+		super.onClick();
+    }
+}

src/main/res/values/strings.xml 🔗

@@ -367,6 +367,9 @@
 	<string name="conversations_foreground_service">Conversations</string>
 	<string name="pref_keep_foreground_service">Keep service in foreground</string>
 	<string name="pref_keep_foreground_service_summary">Prevents the operating system from killing your connection</string>
+	<string name="pref_export_logs">Export Logs</string>
+	<string name="pref_export_logs_summary">Write logs to SD card</string>
+	<string name="notification_export_logs_title">Writing logs to SD card</string>
 	<string name="choose_file">Choose file</string>
 	<string name="receiving_x_file">Receiving %1$s (%2$d%% completed)</string>
 	<string name="download_x_file">Download %s</string>

src/main/res/xml/preferences.xml 🔗

@@ -170,6 +170,10 @@
                     android:key="keep_foreground_service"
                     android:summary="@string/pref_keep_foreground_service_summary"
                     android:title="@string/pref_keep_foreground_service"/>
+                <eu.siacs.conversations.ui.ExportLogsPreference
+                        android:key="export_logs"
+                        android:title="@string/pref_export_logs"
+                        android:summary="@string/pref_export_logs_summary"/>
             </PreferenceCategory>
         </PreferenceScreen>