285 lines
10 KiB
Java
285 lines
10 KiB
Java
package de.server47.messageexport;
|
|
|
|
import android.app.Activity;
|
|
import android.content.Intent;
|
|
import android.database.Cursor;
|
|
import android.net.Uri;
|
|
import android.os.AsyncTask;
|
|
import android.os.Bundle;
|
|
import android.util.Log;
|
|
import android.view.View;
|
|
import android.widget.Button;
|
|
import android.widget.ProgressBar;
|
|
import android.widget.RadioButton;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.annotation.Nullable;
|
|
import androidx.documentfile.provider.DocumentFile;
|
|
|
|
import org.apache.commons.lang3.StringEscapeUtils;
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.Calendar;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
public class MainActivity extends Activity
|
|
{
|
|
Button bExport;
|
|
RadioButton rbRcs, rbSms, rbMms;
|
|
ProgressBar pbStatus;
|
|
|
|
final static int typeSMS = 0;
|
|
final static int typeMMS = 1;
|
|
final static int typeRCS = 2;
|
|
|
|
final static int requestCodeExport = 815;
|
|
|
|
long messageCount = 0;
|
|
|
|
@Override
|
|
protected void onCreate(@Nullable Bundle savedInstanceState)
|
|
{
|
|
super.onCreate(savedInstanceState);
|
|
setContentView(R.layout.main_activity);
|
|
|
|
bExport = (Button)findViewById(R.id.bExport);
|
|
rbRcs = (RadioButton)findViewById(R.id.rbRcs);
|
|
rbSms = (RadioButton)findViewById(R.id.rbSms);
|
|
rbMms = (RadioButton)findViewById(R.id.rbMms);
|
|
pbStatus = (ProgressBar)findViewById(R.id.pbStatus);
|
|
|
|
bExport.setOnClickListener(new View.OnClickListener()
|
|
{
|
|
@Override
|
|
public void onClick(View v)
|
|
{
|
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
|
startActivityForResult(intent, requestCodeExport);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
|
{
|
|
super.onActivityResult(requestCode, resultCode, data);
|
|
|
|
if(resultCode == RESULT_OK)
|
|
{
|
|
switch (requestCode)
|
|
{
|
|
case requestCodeExport:
|
|
if (resultCode == RESULT_OK)
|
|
{
|
|
AsyncTask<Void, Integer, Void> asyncTaskExport = new AsyncTask<Void, Integer, Void>()
|
|
{
|
|
@Override
|
|
protected Void doInBackground(Void... voids)
|
|
{
|
|
Uri uriTree = data.getData();
|
|
String dataToWrite = getExportString();
|
|
if(!StringUtils.isEmpty(dataToWrite))
|
|
exportFiles(uriTree, dataToWrite);
|
|
else
|
|
Toast.makeText(MainActivity.this, "Error reading messages. Can\'t export.", Toast.LENGTH_SHORT).show();
|
|
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
protected void onProgressUpdate(Integer... values)
|
|
{
|
|
super.onProgressUpdate(values);
|
|
pbStatus.setProgress(values[0]);
|
|
}
|
|
};
|
|
|
|
asyncTaskExport.execute();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
String getExportString(AsyncTask<Void,Integer,Void> asyncTaskReference)
|
|
{
|
|
List<Map<String,String>> response;
|
|
|
|
if(rbSms.isChecked())
|
|
response = readMessages(typeSMS);
|
|
else if(rbMms.isChecked())
|
|
response = readMessages(typeMMS);
|
|
else
|
|
response = readMessages(typeRCS);
|
|
|
|
if(response.size() > 0)
|
|
{
|
|
messageCount = response.size();
|
|
|
|
asyncTaskReference.publishProgress((int)Math.round(messageCount/response.size()));
|
|
|
|
StringBuilder export = new StringBuilder();
|
|
|
|
export.append("<?xml-stylesheet type=\"text/xsl\" href=\"sms.xsl\"?>");
|
|
export.append("<smses count=\"" + String.valueOf(response.size()) + "\"");
|
|
export.append(" backup_set=\"" + String.valueOf(Calendar.getInstance().getTimeInMillis()) + "\"");
|
|
export.append(" backup_date=\"" + String.valueOf(Calendar.getInstance().getTimeInMillis()) + "\"");
|
|
export.append(" type=\"full\">");
|
|
|
|
// 05.05.2001 19:15:00
|
|
String pattern = "dd.MM.yyyy H:m:s";
|
|
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
|
|
|
|
for(int i = 0; i < response.size(); i++)
|
|
{
|
|
Calendar cal = Calendar.getInstance();
|
|
cal.setTimeInMillis(Long.parseLong(response.get(i).get("date")));
|
|
|
|
/*
|
|
type: 1 = inbound
|
|
type: 2 = outbound
|
|
*/
|
|
|
|
export.append("<sms protocol=\"0\"");
|
|
|
|
if(!StringUtils.isEmpty(response.get(i).get("remote_uri")))
|
|
export.append(" address=\"" + response.get(i).get("remote_uri").replace("tel:", "") + "\"");
|
|
else
|
|
export.append(" address=\"" + response.get(i).get("address") + "\"");
|
|
|
|
export.append(
|
|
" date=\"" + response.get(i).get("date") + "\"" +
|
|
" type=\"" + response.get(i).get("type") + "\"" +
|
|
" subject=\"null\"" +
|
|
" body=\"" + StringEscapeUtils.escapeXml11(response.get(i).get("body")).replace("\n", " ") + "\"" +
|
|
" toa=\"null\"" +
|
|
" sc_toa=\"null\"" +
|
|
" service_center=\"null\"" +
|
|
" read=\"1\"" +
|
|
" status=\"-1\"" +
|
|
" locked=\"0\"" +
|
|
" date_sent=\"" + response.get(i).get("date_sent") + "\"" +
|
|
" sub_id=\"-1\"" +
|
|
" readable_date=\"" + simpleDateFormat.format(cal.getTime()) + "\"" +
|
|
" contact_name=\"(Unknown)\"/>"
|
|
);
|
|
}
|
|
|
|
export.append("</smses>");
|
|
return export.toString();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
void exportFiles(Uri uriTree, String content)
|
|
{
|
|
String fileName = "Message_export_" + String.valueOf(Calendar.getInstance().getTimeInMillis()) + ".xml";
|
|
|
|
DocumentFile directory = DocumentFile.fromTreeUri(this, uriTree);
|
|
|
|
// Clean up
|
|
// for(DocumentFile file : directory.listFiles())
|
|
// {
|
|
// /*
|
|
// On some few users' devices it seems this caused a crash because file.getName() was null.
|
|
// The reason for that remains unknown, but we don't want the export to crash because of it.
|
|
// */
|
|
// if(!StringUtils.isEmpty(file.getName()))
|
|
// {
|
|
// if (file.getName().equals(XmlFileInterface.settingsFileName) && file.canWrite())
|
|
// file.delete();
|
|
// else if (file.getName().equals(prefsFileName) && file.canWrite())
|
|
// file.delete();
|
|
// }
|
|
// }
|
|
|
|
DocumentFile exportFile = directory.createFile("text/xml", fileName);
|
|
|
|
if(exportFile.canWrite())
|
|
{
|
|
try
|
|
{
|
|
OutputStream out = getApplicationContext().getContentResolver().openOutputStream(exportFile.getUri());
|
|
out.write(content.getBytes());
|
|
out.close();
|
|
|
|
Toast.makeText(MainActivity.this, "Export complete. " + String.valueOf(messageCount) + " messages have been exported.", Toast.LENGTH_LONG).show();
|
|
}
|
|
catch (IOException e)
|
|
{
|
|
e.printStackTrace();
|
|
Toast.makeText(MainActivity.this, "Error while writing file.", Toast.LENGTH_LONG).show();
|
|
}
|
|
}
|
|
// else
|
|
// Toast.makeText(MainActivity.this, getResources().getString(R.string.ConfigurationExportError), Toast.LENGTH_LONG).show();
|
|
}
|
|
|
|
List<Map<String,String>> readMessages(int messageType)
|
|
{
|
|
// public static final String INBOX = "content://sms/inbox";
|
|
// public static final String SENT = "content://sms/sent";
|
|
// public static final String DRAFT = "content://sms/draft";
|
|
|
|
Uri path = null;
|
|
|
|
switch (messageType)
|
|
{
|
|
case typeSMS:
|
|
path = Uri.parse("content://sms/inbox");
|
|
break;
|
|
case typeMMS:
|
|
path = Uri.parse("content://mms/inbox");
|
|
break;
|
|
case typeRCS:
|
|
path = Uri.parse("content://im/chat");
|
|
break;
|
|
default:
|
|
path = Uri.parse("content://sms/inbox");
|
|
break;
|
|
}
|
|
|
|
List<Map<String,String>> returnList = new ArrayList<>();
|
|
|
|
Cursor cursor = getContentResolver().query(path, null, null, null, null);
|
|
|
|
if (cursor.moveToFirst())
|
|
{
|
|
// must check the result to prevent exception
|
|
long messageCounter = 0;
|
|
do
|
|
{
|
|
messageCounter++;
|
|
String message = null;
|
|
Map<String,String> messageMap = new HashMap();
|
|
for(int i=0; i < cursor.getColumnCount(); i++)
|
|
{
|
|
messageMap.put(cursor.getColumnName(i), cursor.getString(i));
|
|
message += cursor.getColumnName(i) + ": " + cursor.getString(i) + ", ";
|
|
// Log.i("message", msgData);
|
|
}
|
|
Log.i("Message", "Message nr. " + messageCounter + " " + message);
|
|
|
|
if(!StringUtils.isEmpty(messageMap.get("body")))
|
|
returnList.add(messageMap);
|
|
else
|
|
Log.i("Message", "Skipping a message with an empty body.");
|
|
}
|
|
while (cursor.moveToNext());
|
|
}
|
|
else
|
|
{
|
|
// empty box, no SMS
|
|
Log.i("message", "no messages");
|
|
}
|
|
|
|
return returnList;
|
|
}
|
|
} |