Working
This commit is contained in:
parent
57fe7ab08a
commit
f983ba504b
20
README.md
Normal file
20
README.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# RCS Exporter
|
||||||
|
|
||||||
|
I've been using the app SMS Backup & Restore for years. After getting a new phone and transferring the messages over I noticed some were missing.
|
||||||
|
|
||||||
|
I tried to find out why this is happening - because of a "new" message type called RCS: https://en.wikipedia.org/wiki/Rich_Communication_Services
|
||||||
|
|
||||||
|
Essentially those messages are stored in a separate database on the phone, hence not considered regular text messages.
|
||||||
|
|
||||||
|
The above mentioned program is not able to export those messages (as of September 2021).
|
||||||
|
|
||||||
|
I didn't want to reinvent the wheel - there are already enough backup/restore apps for SMS and MMS. Instead I just wanted to be able to export the RCS messages, too. Preferably it should be a format the SMS BR can then import.
|
||||||
|
|
||||||
|
So if you're interested:
|
||||||
|
1. Backup your SMS and MMS messages with another program.
|
||||||
|
2. Import them on the target phone.
|
||||||
|
3. Export the RCS using this app.
|
||||||
|
4. Import those on the new phone as well.
|
||||||
|
|
||||||
|
Remarks:
|
||||||
|
- This program has only been tested with text messages. I don't know if images, etc. can be exported as well.
|
@ -11,6 +11,7 @@ android {
|
|||||||
targetSdk 30
|
targetSdk 30
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
|
useLibrary 'org.apache.http.legacy'
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,10 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<!-- https://developer.android.com/about/versions/pie/android-9.0-changes-28#apache-p-->
|
||||||
|
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@ -1,29 +1,40 @@
|
|||||||
package de.server47.smsexport;
|
package de.server47.smsexport;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class MainActivity extends Activity
|
public class MainActivity extends Activity
|
||||||
{
|
{
|
||||||
Button bRead;
|
Button bRead, bWrite;
|
||||||
|
|
||||||
final static int typeSMS = 0;
|
final static int typeSMS = 0;
|
||||||
final static int typeMMS = 1;
|
final static int typeMMS = 1;
|
||||||
final static int typeICS = 2;
|
final static int typeICS = 2;
|
||||||
|
|
||||||
|
final static int requestCodeExport = 815;
|
||||||
|
|
||||||
|
String contentToWrite = "bla";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState)
|
protected void onCreate(@Nullable Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
@ -31,6 +42,7 @@ public class MainActivity extends Activity
|
|||||||
setContentView(R.layout.main_activity);
|
setContentView(R.layout.main_activity);
|
||||||
|
|
||||||
bRead = (Button)findViewById(R.id.bRead);
|
bRead = (Button)findViewById(R.id.bRead);
|
||||||
|
bWrite = (Button)findViewById(R.id.bWrite);
|
||||||
|
|
||||||
bRead.setOnClickListener(new View.OnClickListener()
|
bRead.setOnClickListener(new View.OnClickListener()
|
||||||
{
|
{
|
||||||
@ -48,16 +60,24 @@ public class MainActivity extends Activity
|
|||||||
export.append("backup_date=\"1631273111057\"");
|
export.append("backup_date=\"1631273111057\"");
|
||||||
export.append("type=\"full\">");
|
export.append("type=\"full\">");
|
||||||
// 05.05.2001 19:15:00
|
// 05.05.2001 19:15:00
|
||||||
String pattern = "dd-MM-yyyy H:m:s";
|
String pattern = "dd.MM.yyyy H:m:s";
|
||||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
|
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
|
||||||
|
|
||||||
for(int i = 0; i < response.size(); i++)
|
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(
|
export.append(
|
||||||
"<sms protocol=\"0\"" +
|
"<sms protocol=\"0\"" +
|
||||||
"address=\"" + response.get(i).get("remote_uri").replace("tel:", "") + "\"" +
|
"address=\"" + response.get(i).get("remote_uri").replace("tel:", "") + "\"" +
|
||||||
"date=\"" + response.get(i).get("date") + "\"" +
|
"date=\"" + response.get(i).get("date") + "\"" +
|
||||||
"type=\"1\"" +
|
"type=\"" + response.get(i).get("type") + "\"" +
|
||||||
"subject=\"null\"" +
|
"subject=\"null\"" +
|
||||||
"body=\"" + response.get(i).get("body") + "\"" +
|
"body=\"" + response.get(i).get("body") + "\"" +
|
||||||
"toa=\"null\"" +
|
"toa=\"null\"" +
|
||||||
@ -66,19 +86,92 @@ public class MainActivity extends Activity
|
|||||||
"read=\"1\"" +
|
"read=\"1\"" +
|
||||||
"status=\"-1\"" +
|
"status=\"-1\"" +
|
||||||
"locked=\"0\"" +
|
"locked=\"0\"" +
|
||||||
"date_sent=\"0\"" +
|
"date_sent=\"" + response.get(i).get("date_sent") + "\"" +
|
||||||
"sub_id=\"-1\"" +
|
"sub_id=\"-1\"" +
|
||||||
"readable_date=\"" + 05.05.2001 19:15:00 + "\"" +
|
"readable_date=\"" + simpleDateFormat.format(cal.getTime()) + "\"" +
|
||||||
"contact_name=\"(Unknown)\"/>";
|
"contact_name=\"(Unknown)\"/>"
|
||||||
response.get(i).get()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export.append("</smses>");
|
export.append("</smses>");
|
||||||
|
contentToWrite = export.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bWrite.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)
|
||||||
|
{
|
||||||
|
Uri uriTree = data.getData();
|
||||||
|
exportFiles(uriTree, contentToWrite);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.", 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)
|
List<Map<String,String>> readMessages(int messageType)
|
||||||
|
@ -2,8 +2,16 @@
|
|||||||
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_margin="@dimen/defaultMargin"
|
||||||
android:gravity="center_horizontal">
|
android:gravity="center_horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline2" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/bRead"
|
android:id="@+id/bRead"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -11,4 +19,11 @@
|
|||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:text="Read" />
|
android:text="Read" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/bWrite"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="Write" />
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
@ -1,3 +1,4 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<dimen name="fab_margin">16dp</dimen>
|
<dimen name="fab_margin">16dp</dimen>
|
||||||
|
<dimen name="defaultMargin">25px</dimen>
|
||||||
</resources>
|
</resources>
|
@ -1,5 +1,5 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">SmsExport</string>
|
<string name="app_name">RcsExport</string>
|
||||||
<string name="action_settings">Settings</string>
|
<string name="action_settings">Settings</string>
|
||||||
<!-- Strings used for fragments for navigation -->
|
<!-- Strings used for fragments for navigation -->
|
||||||
<string name="first_fragment_label">First Fragment</string>
|
<string name="first_fragment_label">First Fragment</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user