Commit db30c440 authored by axet's avatar axet 🍄

Merge branch 'smsgate-1.9.0'

parents 4466ef89 7960384b
Pipeline #35854636 (#157) passed with stage
in 41 seconds
......@@ -8,8 +8,8 @@ android {
applicationId "com.github.axet.smsgate"
minSdkVersion 9
targetSdkVersion 26
versionCode 208
versionName "1.8.24"
versionCode 209
versionName "1.9.0"
}
packagingOptions {
exclude 'META-INF/LICENSE'
......@@ -39,7 +39,7 @@ android {
dependencies {
testImplementation 'junit:junit:4.12'
implementation 'com.github.axet:android-library:1.27.21' // implementation project(':android-library')
implementation 'com.github.axet:android-library:1.27.22' // 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'
......@@ -55,6 +55,7 @@ dependencies {
implementation 'com.google.firebase:firebase-database:9.8.0'
implementation 'com.google.firebase:firebase-storage:9.8.0'
implementation 'com.google.firebase:firebase-auth:9.8.0'
//noinspection GradleCompatible
implementation 'com.google.firebase:firebase-messaging:9.8.0'
}
......
package com.github.axet.smsgate.app;
import android.app.Notification;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
......@@ -18,6 +21,7 @@ import com.fsck.k9.mail.internet.TextBody;
import com.github.axet.androidlibrary.crypto.Bitcoin;
import com.github.axet.androidlibrary.services.FileProvider;
import com.github.axet.androidlibrary.services.WifiKeepService;
import com.github.axet.smsgate.BuildConfig;
import com.github.axet.smsgate.R;
import com.github.axet.smsgate.providers.SIM;
import com.github.axet.smsgate.services.FirebaseService;
......@@ -42,6 +46,7 @@ import java.util.Set;
import static com.fsck.k9.mail.internet.MimeMessageHelper.setBody;
// adb shell "am broadcast -a com.github.axet.smsgate.KEY -e data 33334412341234"
public class SMSApplication extends App {
public static final String SCHEDULER_COUNT = "SCHEDULER_COUNT";
public static final String SCHEDULER_ITEM = "SCHEDULER_";
......@@ -49,6 +54,8 @@ public class SMSApplication extends App {
public static final String SEC = "SEC";
public static final String SMS_LAST = "SMS_LAST";
public static final String MMS_LAST = "MMS_LAST";
public static final String STORAGE_SMS_LAST = "STORAGE_SMS_LAST";
public static final String STORAGE_MMS_LAST = "STORAGE_MMS_LAST";
public static final String PREF_FIREBASE = "firebase";
public static final String PREF_NOTIFICATION_LISTENER = "notification_listener";
public static final String PREF_WIFIRESTART = "wifi_restart";
......@@ -59,6 +66,7 @@ public class SMSApplication extends App {
public static final String PREF_DEFAULTSMS = "defaultsms";
public static final String PREF_FIREBASESETTINGS = "firebase_settings";
public static final String PREF_REBOOT = "reboot";
public static final String PREF_STORAGE = "storage";
public static final String PREF_APPS = "applications"; // enabled
public static final String APPS_INDEX = "APPS_";
......@@ -68,10 +76,24 @@ public class SMSApplication extends App {
public static final String APP_SUBJ = "subject";
public static final String APP_BODY = "body";
public static final String ACTION_KEY = BuildConfig.APPLICATION_ID + ".KEY";
SIM sim;
Bitcoin keyPair = null;
Handler handler = new Handler();
NotificationService.NotificationsMap<NotificationInfo> lastId = new NotificationService.NotificationsMap<>(handler);
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String a = intent.getAction();
if (a != null && a.equals(ACTION_KEY)) {
keyPair.loadSec(intent.getStringExtra("data"));
keyPair.loadPub(keyPair.sec);
save();
FirebaseService.start(context);
}
}
};
public static SMSApplication from(Context context) {
return (SMSApplication) App.from(context);
......@@ -143,6 +165,9 @@ public class SMSApplication extends App {
}
WifiKeepService.DESCRIPTION = "WifiService";
WifiKeepService.ICON = R.drawable.ic_sms;
IntentFilter ff = new IntentFilter();
ff.addAction(ACTION_KEY);
registerReceiver(receiver, ff);
}
public void save() {
......
This diff is collapsed.
......@@ -33,6 +33,7 @@ import com.github.axet.smsgate.activities.MainActivity;
import com.github.axet.smsgate.app.SMSApplication;
import com.github.axet.smsgate.app.ScheduleSMS;
import com.github.axet.smsgate.dialogs.ScheduleEditDialogFragment;
import com.github.axet.smsgate.services.ScheduleService;
import java.util.List;
......@@ -149,6 +150,7 @@ public class SchedulersFragment extends Fragment implements DialogInterface.OnDi
public void onClick(View v) {
item.enabled = holder.enabled.isChecked();
SMSApplication.save(getActivity(), items);
ScheduleService.startIfEnabled(getContext());
}
});
......@@ -307,6 +309,7 @@ public class SchedulersFragment extends Fragment implements DialogInterface.OnDi
schedulers.set(r.pos, r.schedule);
SMSApplication.save(getActivity(), schedulers.items);
}
ScheduleService.startIfEnabled(getContext());
}
this.dialog = null;
}
......
......@@ -774,14 +774,6 @@ public class SettingsFragment extends PreferenceFragmentCompat implements MainAc
@TargetApi(11)
@SuppressWarnings({"ConstantConditions", "PointlessBooleanExpression"})
private void setupStrictMode() {
if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= 11) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
// .detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyFlashScreen()
.build());
}
}
private void checkAndDisplayDroidWarning() {
......
......@@ -17,7 +17,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.BatteryManager;
......@@ -35,7 +34,6 @@ import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import android.view.View;
......@@ -50,6 +48,7 @@ import com.github.axet.androidlibrary.widgets.RemoteNotificationCompat;
import com.github.axet.smsgate.R;
import com.github.axet.smsgate.activities.MainActivity;
import com.github.axet.smsgate.app.SMSApplication;
import com.github.axet.smsgate.app.Storage;
import com.github.axet.smsgate.providers.SIM;
import com.github.axet.smsgate.providers.SMS;
import com.google.android.gms.tasks.OnFailureListener;
......@@ -71,7 +70,6 @@ import com.zegoggles.smssync.Consts;
import com.zegoggles.smssync.SmsConsts;
import com.zegoggles.smssync.mail.DataType;
import com.zegoggles.smssync.mail.PersonLookup;
import com.zegoggles.smssync.mail.PersonRecord;
import com.zegoggles.smssync.service.BackupCursors;
import com.zegoggles.smssync.service.BackupItemsFetcher;
import com.zegoggles.smssync.service.BackupQueryBuilder;
......@@ -444,88 +442,6 @@ public class FirebaseService extends Service implements FirebaseAuth.AuthStateLi
}
}
public static class SMSMessage {
String id;
long sent;
String type; // IN/OUT
String thread; // thread id
String threadPhone; // phone
String threadName; // person Full Name
String message;
public String toJSON() {
try {
JSONObject j = new JSONObject();
j.put("id", id);
j.put("sent", sent);
j.put("type", type);
j.put("thread", thread);
j.put("threadPhone", threadPhone);
j.put("threadName", threadName);
j.put("message", message);
return j.toString();
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public long getSent() {
return sent;
}
public void setSent(long sent) {
this.sent = sent;
}
public String getThread() {
return thread;
}
public String getThreadName() {
return threadName;
}
public String getThreadPhone() {
return threadPhone;
}
public void setThread(String thread) {
this.thread = thread;
}
public void setThreadName(String threadName) {
this.threadName = threadName;
}
public void setThreadPhone(String threadPhone) {
this.threadPhone = threadPhone;
}
public void setMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setType(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
public static class QueryBuilder extends BackupQueryBuilder {
SharedPreferences prefs;
......@@ -1141,28 +1057,6 @@ public class FirebaseService extends Service implements FirebaseAuth.AuthStateLi
firebase(this);
}
public static 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 void incoming() {
final Runnable run = new Runnable() {
int count = 0;
......@@ -1208,8 +1102,8 @@ public class FirebaseService extends Service implements FirebaseAuth.AuthStateLi
count++;
if (cursors.filter())
continue;
Map<String, String> map = getMessageMap(cursor.cursor);
SMSMessage sms = messageFromMapSms(map);
Map<String, String> map = Storage.getMessageMap(cursor.cursor);
Storage.SMSMessage sms = Storage.messageFromMapSms(mPersonLookup, map);
messages.child(sms.getId()).setValue(new Message(keyPair.encrypt(sms.toJSON())));
if (cursor.type == DataType.SMS) {
b.setLastSMS(sms.sent);
......@@ -1234,57 +1128,6 @@ public class FirebaseService extends Service implements FirebaseAuth.AuthStateLi
}
}
public SMSMessage messageFromMapSms(Map<String, String> msgMap) {
SMSMessage sms = new SMSMessage();
sms.id = msgMap.get(SmsConsts.ID);
sms.message = msgMap.get(SmsConsts.BODY);
final int messageType = toInt(msgMap.get(SmsConsts.TYPE));
sms.thread = msgMap.get(SmsConsts.THREAD_ID);
final String address = msgMap.get(SmsConsts.ADDRESS);
if (TextUtils.isEmpty(address)) {
sms.threadPhone = "--";
sms.threadName = "--";
} else {
PersonRecord record = mPersonLookup.lookupPerson(address);
sms.threadPhone = record.getNumber();
sms.threadName = record.getName();
}
if (sms.thread == null || sms.thread.isEmpty())
sms.thread = sms.threadPhone;
if (SmsConsts.MESSAGE_TYPE_INBOX == messageType) {
// Received message
sms.type = "IN";
} else {
// Sent message
sms.type = "OUT";
}
long sentDate;
try {
sentDate = Long.valueOf(msgMap.get(SmsConsts.DATE));
} catch (NumberFormatException n) {
Log.e(TAG, "error parsing date", n);
sentDate = System.currentTimeMillis();
}
sms.sent = sentDate;
return sms;
}
private static int toInt(String s) {
try {
return Integer.valueOf(s);
} catch (NumberFormatException e) {
return -1;
}
}
void updateAllThreads() {
// thread dates + states might be wrong, we need to force a full update
// unfortunately there's no direct way to do that in the SDK, but passing a
......
......@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.github.axet.smsgate.app.Storage;
import com.zegoggles.smssync.activity.SMSGateFragment;
import com.zegoggles.smssync.service.SmsBackupService;
......@@ -27,7 +28,7 @@ import static com.zegoggles.smssync.App.LOCAL_LOGV;
import static com.zegoggles.smssync.App.TAG;
public class IncomingPeekReceiver extends BroadcastReceiver {
private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
public static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
@Override
public void onReceive(Context context, Intent intent) {
......@@ -43,9 +44,10 @@ public class IncomingPeekReceiver extends BroadcastReceiver {
}
}
private void incomingSMS(Context context) {
public void incomingSMS(Context context) {
SMSGateFragment.checkPermissions(context);
FirebaseService.incoming(context);
SmsBackupService.scheduleIncomingBackup(context);
Storage.incoming(context);
}
}
......@@ -20,7 +20,7 @@ import java.util.List;
import java.util.Map;
@TargetApi(19) // SMS_DELIVER is API19+
public class IncomingPostReceiver extends BroadcastReceiver {
public class IncomingPostReceiver extends BroadcastReceiver { // sms deliver handler (we have to handle storage as well)
public static class Message {
public String phone;
......
......@@ -48,7 +48,7 @@ public class OnBootReceiver extends BroadcastReceiver {
SharedPreferences shared = PreferenceManager.getDefaultSharedPreferences(context);
SMSGateFragment.checkPermissions(context);
ScheduleService.start(context);
ScheduleService.startIfEnabled(context);
if (Build.VERSION.SDK_INT >= 18)
NotificationListener.startIfEnabled(context);
NotificationService.startIfEnabled(context);
......
package com.github.axet.smsgate.services;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
......@@ -36,21 +37,33 @@ public class ScheduleService extends Service implements SharedPreferences.OnShar
OptimizationPreferenceCompat.ServiceReceiver receiver;
OptimizationPreferenceCompat.NotificationIcon icon;
public static void start(Context context) {
public static void startIfEnabled(Context context) {
List<ScheduleSMS> items = SMSApplication.load(context);
TreeSet<Long> events = new TreeSet<>();
for (ScheduleSMS s : items) {
if (s.next != 0 && s.enabled)
events.add(s.next);
}
if (events.isEmpty()) {
stop(context);
} else {
Intent intent = new Intent(context, ScheduleService.class);
intent.setAction(REGISTER);
OptimizationPreferenceCompat.startService(context, intent);
}
}
public static void stop(Context context) {
Intent intent = new Intent(context, ScheduleService.class);
intent.setAction(REGISTER);
OptimizationPreferenceCompat.startService(context, intent);
context.stopService(intent);
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
receiver = new OptimizationPreferenceCompat.ServiceReceiver(this, getClass(), SMSApplication.PREF_OPTIMIZATION) {
@Override
public void check() {
}
@Override
public void register() {
super.register();
......@@ -63,6 +76,7 @@ public class ScheduleService extends Service implements SharedPreferences.OnShar
OptimizationPreferenceCompat.setKillCheck(context, 0, SMSApplication.PREF_NEXT);
}
};
receiver.create();
icon = new OptimizationPreferenceCompat.NotificationIcon(this, NOTIFICATION_ICON, "status", "ScheduleService");
icon.icon = R.drawable.ic_sms;
......@@ -76,6 +90,7 @@ public class ScheduleService extends Service implements SharedPreferences.OnShar
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
receiver.close();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.unregisterOnSharedPreferenceChangeListener(this);
......@@ -91,7 +106,8 @@ public class ScheduleService extends Service implements SharedPreferences.OnShar
if (action != null) {
if (action.equals(REGISTER)) {
SMSApplication.load(this);
registerNextAlarm();
if (registerNextAlarm() == null)
stopSelf();
}
if (action.equals(ACTION_SMS)) {
long time = intent.getLongExtra("time", 0);
......@@ -104,11 +120,10 @@ public class ScheduleService extends Service implements SharedPreferences.OnShar
return super.onStartCommand(intent, flags, startId);
}
public void registerNextAlarm() {
TreeSet<Long> events = new TreeSet<>();
public PendingIntent registerNextAlarm() {
items = SMSApplication.load(this);
TreeSet<Long> events = new TreeSet<>();
for (ScheduleSMS s : items) {
if (s.next != 0 && s.enabled)
events.add(s.next);
......@@ -120,6 +135,7 @@ public class ScheduleService extends Service implements SharedPreferences.OnShar
if (events.isEmpty()) {
AlarmManager.cancel(this, alarmIntent);
return null;
} else {
long time = events.first();
......@@ -127,7 +143,7 @@ public class ScheduleService extends Service implements SharedPreferences.OnShar
Log.d(TAG, "Current: " + ScheduleSMS.formatDate(cur.getTimeInMillis()) + " " + ScheduleSMS.formatTime(cur.getTimeInMillis()) + "; SetAlarm: " + ScheduleSMS.formatDate(time) + " " + ScheduleSMS.formatTime(time));
AlarmManager.setExact(this, time, alarmIntent);
return AlarmManager.setExact(this, time, alarmIntent);
}
}
......@@ -165,7 +181,8 @@ public class ScheduleService extends Service implements SharedPreferences.OnShar
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
registerNextAlarm();
if (registerNextAlarm() == null)
stopSelf();
}
@Nullable
......
package com.github.axet.smsgate.widgets;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.net.Uri;
import android.util.AttributeSet;
import android.widget.Toast;
import com.github.axet.androidlibrary.widgets.OpenFileDialog;
import com.github.axet.androidlibrary.widgets.OpenStorageChoicer;
import com.github.axet.smsgate.R;
import java.io.File;
public class StoragePathPreferenceCompat extends com.github.axet.androidlibrary.widgets.StoragePathPreferenceCompat {
CharSequence defSummary;
public StoragePathPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public StoragePathPreferenceCompat(Context context, AttributeSet attrs) {
super(context, attrs);
}
public StoragePathPreferenceCompat(Context context) {
super(context);
}
public void create() {
defSummary = getSummary();
choicer = new OpenStorageChoicer(storage, OpenFileDialog.DIALOG_TYPE.FOLDER_DIALOG, false) {
Uri reset;
@Override
public void onResult(Uri uri) {
if (uri.equals(reset)) {
SharedPreferences.Editor editor = getSharedPreferences().edit();
editor.remove(getKey());
editor.apply();
setSummary(defSummary);
} else {
if (callChangeListener(uri.toString())) {
setText(uri.toString());
}
}
}
@Override
public OpenFileDialog fileDialogBuild() {
final OpenFileDialog d = super.fileDialogBuild();
d.setNeutralButton("Reset", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
File path = storage.getLocalStorage();
d.setCurrentPath(path);
reset = Uri.fromFile(path);
setSummary(defSummary);
Toast.makeText(context, "SMS Storage disabled", Toast.LENGTH_SHORT).show();
}
});
return d;
}
};
choicer.setTitle(getTitle().toString());
choicer.setContext(getContext());
}
@Override
public void onClick() {
super.onClick();
}
@Override
public void onSetInitialValue(boolean restoreValue, Object defaultValue) { // allow to show null
String v = restoreValue ? getPersistedString(getText()) : (String) defaultValue;
Uri u = storage.getStoragePath(v);
if (u != null) {
setText(u.toString());
setSummary(storage.getDisplayName(u));
}
}
@Override
public Object onGetDefaultValue(TypedArray a, int index) {
super.onGetDefaultValue(a, index);
return null; // no default for books reader
}
}
......@@ -33,7 +33,6 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.StrictMode;
import android.preference.PreferenceManager;
import android.provider.Telephony;
import android.support.v7.preference.CheckBoxPreference;
......@@ -47,16 +46,16 @@ import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.github.axet.androidlibrary.app.Storage;
import com.github.axet.androidlibrary.widgets.AboutPreferenceCompat;
import com.github.axet.smsgate.BuildConfig;
import com.github.axet.smsgate.R;
import com.github.axet.smsgate.activities.MainActivity;
import com.github.axet.smsgate.app.SMSApplication;
import com.github.axet.smsgate.app.Storage;
import com.github.axet.smsgate.dialogs.FirebaseConnectDialog;
import com.github.axet.smsgate.services.FirebaseService;
import com.github.axet.smsgate.services.OnBootReceiver;
import com.github.axet.smsgate.services.SmsReplyService;
import com.github.axet.smsgate.widgets.StoragePathPreferenceCompat;
import com.squareup.otto.Subscribe;
import com.zegoggles.smssync.App;
import com.zegoggles.smssync.Consts;
......@@ -96,6 +95,11 @@ public class SMSGateFragment extends PreferenceFragmentCompat implements MainAct
private static final int REQUEST_CHANGE_DEFAULT_SMS_PACKAGE = 1;
private static final int REQUEST_PICK_ACCOUNT = 2;
private static final int REQUEST_WEB_AUTH = 3;
public static final int RESULT_PERMS_STOR = 4;
public static final int RESULT_PERMS_FIRE = 5;
public static final int RESULT_PERMS_CONN = 6;
public static final String[] PERMISSIONS = new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.READ_SMS, Manifest.permission.READ_PHONE_STATE};
enum Actions {
Backup,
......@@ -170,6 +174,12 @@ public class SMSGateFragment extends PreferenceFragmentCompat implements MainAct
SharedPreferences prefs = android.support.v7.preference.PreferenceManager.getDefaultSharedPreferences(context);
prefs.registerOnSharedPreferenceChangeListener(this);
StoragePathPreferenceCompat storage = (StoragePathPreferenceCompat) findPreference(SMSApplication.PREF_STORAGE);
storage.setStorage(new Storage(getContext()));
storage.setPermissionsDialog(this, Storage.PERMISSIONS_RW, RESULT_PERMS_STOR);
if (Build.VERSION.SDK_INT >= 21)
storage.setStorageAccessFramework(this, RESULT_PERMS_STOR);
}
@Override
......@@ -248,6 +258,11 @@ public class SMSGateFragment extends PreferenceFragmentCompat implements MainAct
}
break;
}
case RESULT_PERMS_STOR: {
StoragePathPreferenceCompat storage = (StoragePathPreferenceCompat) findPreference(SMSApplication.PREF_STORAGE);
storage.onActivityResult(resultCode, data);
break;
}
}
}
......@@ -686,7 +701,7 @@ public class SMSGateFragment extends PreferenceFragmentCompat implements MainAct
public boolean onPreferenceChange(Preference preference, Object change) {
boolean newValue = (Boolean) change;
if (newValue) {
if (Storage.permitted(SMSGateFragment.this, PERMISSIONS, 2)) {
if (Storage.permitted(SMSGateFragment.this, PERMISSIONS, RESULT_PERMS_CONN)) {
connect();
}