Commit dc9cc8f0 authored by axet's avatar axet 🍄

Merge branch 'smsgate-1.9.10'

parents b636691c 4170550d
Pipeline #55725955 passed with stage
in 31 seconds
......@@ -8,8 +8,8 @@ android {
applicationId "com.github.axet.smsgate"
minSdkVersion 9
targetSdkVersion 26
versionCode 218
versionName "1.9.9"
versionCode 219
versionName "1.9.10"
}
packagingOptions {
exclude 'META-INF/LICENSE'
......@@ -39,7 +39,7 @@ android {
dependencies {
testImplementation 'junit:junit:4.12'
implementation 'com.github.axet:android-library:1.30.15' // implementation project(':android-library')
implementation 'com.github.axet:android-library:1.30.26' // implementation project(':android-library')
implementation 'com.android.support:design:25.3.1'
implementation 'com.intellij:annotations:12.0'
implementation 'com.beetstra.jutf7:jutf7:1.0.0'
......
......@@ -14,7 +14,6 @@ import android.provider.Telephony;
import android.telephony.SmsMessage;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
......@@ -84,16 +83,25 @@ public class SmsStorage {
m.phone = cursor.getString(cursor.getColumnIndex(Telephony.Sms.ADDRESS));
m.body = cursor.getString(cursor.getColumnIndex(Telephony.Sms.BODY));
m.thread = cursor.getString(cursor.getColumnIndex(Telephony.Sms.THREAD_ID));
m.read = cursor.getInt(cursor.getColumnIndex(Telephony.Sms.READ)) == 1;
return m;
}
public static String querySort(String sort, int off, int max) {
if (max > 0)
return sort + " LIMIT " + max + (off >= 0 ? " OFFSET " + off : "");
else
return sort;
}
public static class Message {
public long id;
public long date;
public int type;
public String phone;
public int type; // Telephony.Sms.TYPE and Telephony.TextBasedSmsColumns.MESSAGE_TYPE_ ...
public String phone; // Telephony.Sms.ADDRESS
public String body;
public String thread;
public boolean read; // seen?
public Message() {
}
......@@ -122,40 +130,50 @@ public class SmsStorage {
resolver = context.getContentResolver();
}
public void add(Message m) {
Date date;
public long add(Message m) {
long date;
if (m.date == 0)
date = new Date();
date = System.currentTimeMillis();
else
date = new Date(m.date);
date = m.date;
ContentValues values = new ContentValues();
values.put(Telephony.Sms.ADDRESS, m.phone);
values.put(Telephony.Sms.BODY, m.body);
Uri uri;
int type = m.type;
if (m instanceof InboxMessage) {
if (m instanceof InboxMessage)
uri = Telephony.Sms.Inbox.CONTENT_URI;
} else if (m instanceof SentMessage) {
else if (m instanceof SentMessage)
uri = Telephony.Sms.Sent.CONTENT_URI;
} else {
else
throw new RuntimeException("unknown type");
}
values.put(Telephony.Sms.TYPE, type);
values.put(Telephony.Sms.TYPE, m.type);
values.put(Telephony.Sms.PROTOCOL, 0);
values.put(Telephony.Sms.SERVICE_CENTER, "");
values.put(Telephony.Sms.DATE, date.getTime());
values.put(Telephony.Sms.STATUS, -1);
values.put(Telephony.Sms.DATE, date);
values.put(Telephony.Sms.STATUS, Telephony.TextBasedSmsColumns.STATUS_NONE);
if (m.thread != null)
values.put(Telephony.Sms.THREAD_ID, m.thread);
values.put(Telephony.Sms.READ, 1);
values.put(Telephony.Sms.READ, m.read ? 1 : 0);
Uri id = resolver.insert(uri, values);
m.id = ContentUris.parseId(id);
return ContentUris.parseId(id);
}
public Cursor query(long date) {
public Cursor query(long date, int type, int off, int max) {
return resolver.query(Telephony.Sms.CONTENT_URI, null,
String.format("%s > ? and %s == ?", Telephony.Sms.DATE, Telephony.Sms.TYPE),
new String[]{String.valueOf(date), String.valueOf(type)}, querySort(Telephony.TextBasedSmsColumns.DATE, off, max));
}
public Cursor queryAfter(long date, int off, int max) {
return resolver.query(Telephony.Sms.CONTENT_URI, null,
String.format("%s > ?", Telephony.Sms.DATE),
new String[]{String.valueOf(date)}, null);
new String[]{String.valueOf(date)}, querySort(Telephony.TextBasedSmsColumns.DATE, off, max));
}
public Cursor queryFolder(int type, int off, int max) {
return resolver.query(Telephony.Sms.CONTENT_URI, null,
String.format("%s == ?", Telephony.Sms.TYPE),
new String[]{String.valueOf(type)}, querySort(Telephony.TextBasedSmsColumns.DATE, off, max));
}
public void delete(long id) {
......@@ -169,8 +187,7 @@ public class SmsStorage {
resolver.delete(Uri.parse(Telephony.Sms.Conversations.CONTENT_URI + "/-1"), null, null);
}
public boolean exists(long date, String phone) {
// just assume equality on date+address+type
public boolean exists(long date, String phone) { // just assume equality on date+address+type
Cursor c = resolver.query(Telephony.Sms.CONTENT_URI, new String[]{ID},
String.format("%s = ? AND %s = ?", Telephony.Sms.DATE, Telephony.Sms.ADDRESS),
new String[]{String.valueOf(date), phone}, null);
......
......@@ -320,7 +320,7 @@ public class Storage extends com.github.axet.androidlibrary.app.Storage {
SharedPreferences shared = PreferenceManager.getDefaultSharedPreferences(context);
SmsStorage storage = new SmsStorage(context);
Cursor cursor = storage.query(shared.getLong(SMSApplication.STORAGE_SMS_LAST, 0));
Cursor cursor = storage.queryAfter(shared.getLong(SMSApplication.STORAGE_SMS_LAST, 0), -1, -1);
String from = Build.DEVICE;
if (cursor != null) {
......
......@@ -1041,7 +1041,7 @@ public class FirebaseService extends Service implements FirebaseAuth.AuthStateLi
public long messages() {
SharedPreferences shared = PreferenceManager.getDefaultSharedPreferences(FirebaseService.this);
SmsStorage storage = new SmsStorage(this);
Cursor cursor = storage.query(shared.getLong(SMSApplication.SMS_LAST, 0));
Cursor cursor = storage.queryAfter(shared.getLong(SMSApplication.SMS_LAST, 0), -1, -1);
if (cursor != null) {
int count = 0;
while (cursor.moveToNext()) {
......
......@@ -46,6 +46,7 @@ import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.github.axet.androidlibrary.app.SuperUser;
import com.github.axet.androidlibrary.widgets.AboutPreferenceCompat;
import com.github.axet.smsgate.R;
import com.github.axet.smsgate.activities.MainActivity;
......@@ -101,7 +102,7 @@ public class SMSGateFragment extends PreferenceFragmentCompat implements MainAct
public static final String[] PERMISSIONS = new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.READ_SMS, Manifest.permission.READ_PHONE_STATE, Manifest.permission.RECEIVE_SMS, Manifest.permission.SEND_SMS};
public static final String[] PERMISSIONS_STORAGE = concat(PERMISSIONS, Storage.PERMISSIONS_RW);
public static final String[] PERMISSIONS_STORAGE = SuperUser.concat(PERMISSIONS, Storage.PERMISSIONS_RW);
enum Actions {
Backup,
......@@ -133,15 +134,6 @@ public class SMSGateFragment extends PreferenceFragmentCompat implements MainAct
}
};
public static String[] concat(String[] A, String[] B) {
int aLen = A.length;
int bLen = B.length;
String[] C = new String[aLen + bLen];
System.arraycopy(A, 0, C, 0, aLen);
System.arraycopy(B, 0, C, aLen, bLen);
return C;
}
@Override
public void onCreatePreferences(Bundle bundle, String s) {
Context context = getActivity();
......
......@@ -9,16 +9,14 @@ import java.util.Map;
public class ConversionResult {
public final DataType type;
private final List<Message> messages = new ArrayList<Message>();
private final List<Map<String, String>> mapList = new ArrayList<Map<String, String>>();
private long maxDate = DataType.Defaults.MAX_SYNCED_DATE;
public ConversionResult(DataType type) {
this.type = type;
}
public void add(Message message, Map<String, String> map) {
public void add(Message message) {
messages.add(message);
mapList.add(map);
String dateHeader = Headers.get(message, Headers.DATE);
if (dateHeader != null) {
......@@ -44,10 +42,6 @@ public class ConversionResult {
return maxDate;
}
public List<Map<String, String>> getMapList() {
return mapList;
}
public int size() {
return messages.size();
}
......
......@@ -4,6 +4,7 @@ import android.provider.CallLog;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.github.axet.smsgate.app.SmsStorage;
import com.zegoggles.smssync.MmsConsts;
import com.zegoggles.smssync.SmsConsts;
......@@ -35,7 +36,7 @@ class HeaderGenerator {
}
public void setHeaders(final Message message,
final Map<String, String> msgMap,
final SmsStorage.Message msgMap,
final DataType dataType,
final String address,
final @NotNull PersonRecord contact,
......@@ -55,21 +56,15 @@ class HeaderGenerator {
case SMS:
setSmsHeaders(message, msgMap);
break;
case MMS:
setMmsHeaders(message, msgMap);
break;
}
}
private void setSmsHeaders(Message message, Map<String, String> msgMap) throws MessagingException {
message.setHeader(Headers.ID, msgMap.get(SmsConsts.ID));
message.setHeader(Headers.TYPE, msgMap.get(SmsConsts.TYPE));
message.setHeader(Headers.DATE, msgMap.get(SmsConsts.DATE));
message.setHeader(Headers.THREAD_ID, msgMap.get(SmsConsts.THREAD_ID));
message.setHeader(Headers.READ, msgMap.get(SmsConsts.READ));
message.setHeader(Headers.STATUS, msgMap.get(SmsConsts.STATUS));
message.setHeader(Headers.PROTOCOL, msgMap.get(SmsConsts.PROTOCOL));
message.setHeader(Headers.SERVICE_CENTER, msgMap.get(SmsConsts.SERVICE_CENTER));
private void setSmsHeaders(Message message, SmsStorage.Message msgMap) throws MessagingException {
message.setHeader(Headers.ID, String.valueOf(msgMap.id));
message.setHeader(Headers.TYPE, String.valueOf(msgMap.type));
message.setHeader(Headers.DATE, String.valueOf(msgMap.date));
message.setHeader(Headers.THREAD_ID, msgMap.thread);
message.setHeader(Headers.READ, String.valueOf(msgMap.read ? 1 : 0));
}
private void setMmsHeaders(Message message, Map<String, String> msgMap) throws MessagingException {
......
......@@ -27,6 +27,7 @@ import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.internet.MimeUtility;
import com.github.axet.smsgate.app.SmsStorage;
import com.zegoggles.smssync.MmsConsts;
import com.zegoggles.smssync.SmsConsts;
import com.zegoggles.smssync.contacts.ContactAccessor;
......@@ -58,6 +59,8 @@ public class MessageConverter {
private final MessageGenerator mMessageGenerator;
private final boolean mMarkAsReadOnRestore;
SmsStorage storage;
public MessageConverter(Context context, Preferences preferences,
String userEmail,
PersonLookup personLookup,
......@@ -66,6 +69,7 @@ public class MessageConverter {
mMarkAsReadType = preferences.getMarkAsReadType();
mPersonLookup = personLookup;
mMarkAsReadOnRestore = preferences.getMarkAsReadOnRestore();
storage = new SmsStorage(context);
String referenceUid = preferences.getReferenceUid();
if (referenceUid == null) {
......@@ -82,14 +86,12 @@ public class MessageConverter {
new MmsSupport(mContext.getContentResolver(), mPersonLookup));
}
private boolean markAsSeen(DataType dataType, Map<String, String> msgMap) {
private boolean markAsSeen(DataType dataType, SmsStorage.Message msgMap) {
switch (mMarkAsReadType) {
case MESSAGE_STATUS:
switch (dataType) {
case SMS:
return "1".equals(msgMap.get(SmsConsts.READ));
case MMS:
return "1".equals(msgMap.get(MmsConsts.READ));
return msgMap.read;
default:
return true;
}
......@@ -105,8 +107,7 @@ public class MessageConverter {
@NotNull
ConversionResult convertMessages(final Cursor cursor, DataType dataType)
throws MessagingException {
final Map<String, String> msgMap = getMessageMap(cursor);
final SmsStorage.Message msgMap = storage.getMessage(cursor);
final Message m;
switch (dataType) {
default:
......@@ -116,7 +117,7 @@ public class MessageConverter {
final ConversionResult result = new ConversionResult(dataType);
if (m != null) {
m.setFlag(Flag.SEEN, markAsSeen(dataType, msgMap));
result.add(m, msgMap);
result.add(m);
}
return result;
......@@ -178,28 +179,6 @@ public class MessageConverter {
}
}
private Map<String, String> getMessageMap(Cursor cursor) {
final String[] columns = cursor.getColumnNames();
final Map<String, String> msgMap = new HashMap<String, String>(columns.length);
for (String column : columns) {
String value;
try {
final int index = cursor.getColumnIndex(column);
if (index != -1) {
value = cursor.getString(index);
} else {
continue;
}
} catch (SQLiteException ignored) {
// this can happen in case of BLOBS in the DB
// column type checking is API level >= 11
value = "[BLOB]";
}
msgMap.put(column, value);
}
return msgMap;
}
public static String generateReferenceValue() {
final StringBuilder sb = new StringBuilder();
final Random random = new Random();
......
......@@ -12,6 +12,7 @@ import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.internet.MimeMessage;
import com.fsck.k9.mail.internet.MimeMultipart;
import com.fsck.k9.mail.internet.TextBody;
import com.github.axet.smsgate.app.SmsStorage;
import com.zegoggles.smssync.Consts;
import com.zegoggles.smssync.MmsConsts;
import com.zegoggles.smssync.SmsConsts;
......@@ -62,12 +63,10 @@ class MessageGenerator {
public
@Nullable
Message messageForDataType(Map<String, String> msgMap, DataType dataType) throws MessagingException {
Message messageForDataType(SmsStorage.Message msgMap, DataType dataType) throws MessagingException {
switch (dataType) {
case SMS:
return messageFromMapSms(msgMap);
case MMS:
return messageFromMapMms(msgMap);
default:
return null;
}
......@@ -75,17 +74,17 @@ class MessageGenerator {
private
@Nullable
Message messageFromMapSms(Map<String, String> msgMap) throws MessagingException {
final String address = msgMap.get(SmsConsts.ADDRESS);
Message messageFromMapSms(SmsStorage.Message msgMap) throws MessagingException {
final String address = msgMap.phone;
if (TextUtils.isEmpty(address)) return null;
PersonRecord record = mPersonLookup.lookupPerson(address);
final Message msg = new MimeMessage();
msg.setSubject(getSubject(DataType.SMS, record));
setBody(msg, new TextBody(msgMap.get(SmsConsts.BODY)));
setBody(msg, new TextBody(msgMap.body));
final int messageType = toInt(msgMap.get(SmsConsts.TYPE));
final int messageType = msgMap.type;
// encode send / to address for easy reply function.
// now to replay sms you just need reply email (it will be send to your inbox)
......@@ -104,7 +103,7 @@ class MessageGenerator {
Date sentDate;
try {
sentDate = new Date(Long.valueOf(msgMap.get(SmsConsts.DATE)));
sentDate = new Date(msgMap.date);
} catch (NumberFormatException n) {
Log.e(TAG, "error parsing date", n);
sentDate = new Date();
......@@ -114,51 +113,6 @@ class MessageGenerator {
return msg;
}
private
@Nullable
Message messageFromMapMms(Map<String, String> msgMap) throws MessagingException {
if (LOCAL_LOGV) Log.v(TAG, "messageFromMapMms(" + msgMap + ")");
final Uri mmsUri = Uri.withAppendedPath(Consts.MMS_PROVIDER, msgMap.get(MmsConsts.ID));
MmsSupport.MmsDetails details = mMmsSupport.getDetails(mmsUri, mAddressStyle);
if (details.isEmpty()) {
Log.w(TAG, "no recipients found");
return null;
}
final Message msg = new MimeMessage();
msg.setSubject(getSubject(DataType.MMS, details.getRecipient()));
if (details.inbound) {
// msg_box == MmsConsts.MESSAGE_BOX_INBOX does not work
msg.setFrom(details.getRecipientAddress());
msg.setRecipient(Message.RecipientType.TO, mUserAddress);
} else {
msg.setRecipients(Message.RecipientType.TO, details.getAddresses());
msg.setFrom(mUserAddress);
}
Date sentDate;
try {
sentDate = new Date(1000 * Long.valueOf(msgMap.get(MmsConsts.DATE)));
} catch (NumberFormatException n) {
Log.e(TAG, "error parsing date", n);
sentDate = new Date();
}
final int msg_box = toInt(msgMap.get("msg_box"));
mHeaderGenerator.setHeaders(msg, msgMap, DataType.MMS, details.address, details.getRecipient(), sentDate, msg_box);
MimeMultipart body = MimeMultipart.newInstance();
for (BodyPart p : mMmsSupport.getMMSBodyParts(Uri.withAppendedPath(mmsUri, MMS_PART))) {
body.addBodyPart(p);
}
setBody(msg, body);
//msg.setUsing7bitTransport();
return msg;
}
private String getSubject(@NotNull DataType type, @NotNull PersonRecord record) {
return mPrefix ?
String.format(Locale.ENGLISH, "[%s] %s", type.getFolder(mContext), record.getName()) :
......
......@@ -4,13 +4,13 @@ import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.sqlite.SQLiteException;
import android.provider.Telephony;
import android.util.Log;
import com.github.axet.smsgate.app.SmsStorage;
import com.zegoggles.smssync.mail.DataType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.zegoggles.smssync.App.LOCAL_LOGV;
import static com.zegoggles.smssync.App.TAG;
......@@ -19,6 +19,7 @@ public class BackupItemsFetcher {
private final Context context;
private final ContentResolver resolver;
private final BackupQueryBuilder queryBuilder;
SmsStorage storage;
public BackupItemsFetcher(@NotNull Context context,
@NotNull ContentResolver resolver,
......@@ -29,30 +30,31 @@ public class BackupItemsFetcher {
this.queryBuilder = queryBuilder;
this.context = context;
this.resolver = resolver;
this.storage = new SmsStorage(context);
}
public
@NotNull
Cursor getItemsForDataType(DataType dataType, int max) {
if (LOCAL_LOGV) Log.v(TAG, "getItemsForDataType(type=" + dataType + ", max=" + max + ")");
switch (dataType) {
default:
return performQuery(queryBuilder.buildQueryForDataType(dataType, max));
}
Cursor cursor = storage.query(queryBuilder.getLastSMS(), (int) queryBuilder.getTypeSMS(), 0, max);
if (cursor == null)
return emptyCursor();
return cursor;
}
public long getMostRecentTimestamp(DataType dataType) {
switch (dataType) {
default:
return getMostRecentTimestampForQuery(queryBuilder.buildMostRecentQueryForDataType(dataType));
return getMostRecentTimestampForQuery();
}
}
private long getMostRecentTimestampForQuery(BackupQueryBuilder.Query query) {
Cursor cursor = performQuery(query);
private long getMostRecentTimestampForQuery() {
Cursor cursor = storage.queryAfter(queryBuilder.getLastSMS(), -1, -1);
try {
if (cursor.moveToFirst()) {
return cursor.getLong(0);
if (cursor.moveToLast()) {
return cursor.getLong(cursor.getColumnIndex(Telephony.TextBasedSmsColumns.DATE));
} else {
return DataType.Defaults.MAX_SYNCED_DATE;
}
......@@ -61,28 +63,6 @@ public class BackupItemsFetcher {
}
}
private
@NotNull
Cursor performQuery(@Nullable BackupQueryBuilder.Query query) {
if (query == null) return emptyCursor();
try {
final Cursor cursor = resolver.query(
query.uri,
query.projection,
query.selection,
query.selectionArgs,
query.sortOrder
);
return cursor == null ? emptyCursor() : cursor;
} catch (SQLiteException e) {
Log.w(TAG, "error querying DB", e);
return emptyCursor();
} catch (NullPointerException e) {
Log.w(TAG, "error querying DB", e);
return emptyCursor();
}
}
static Cursor emptyCursor() {
return new MatrixCursor(new String[]{});
}
......
......@@ -5,7 +5,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.1'
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.google.gms:google-services:3.1.0'
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment