diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 70122a94e..729218be3 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -79,6 +79,9 @@
+
+
+
diff --git a/app/src/main/java/com/zegoggles/smssync/App.java b/app/src/main/java/com/zegoggles/smssync/App.java
index 6f2fd6eaf..71ca9b3d6 100644
--- a/app/src/main/java/com/zegoggles/smssync/App.java
+++ b/app/src/main/java/com/zegoggles/smssync/App.java
@@ -44,6 +44,8 @@
import com.zegoggles.smssync.receiver.BootReceiver;
import com.zegoggles.smssync.receiver.SmsBroadcastReceiver;
import com.zegoggles.smssync.service.BackupJobs;
+import com.zegoggles.smssync.utils.SimCard;
+import com.zegoggles.smssync.utils.SimCardHelper;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
@@ -60,6 +62,8 @@ public class App extends Application {
/** Google Play Services present on this device? */
public static boolean gcmAvailable;
+ public static SimCard[] SimCards;
+
private Preferences preferences;
private BackupJobs backupJobs;
@@ -68,6 +72,7 @@ public void onCreate() {
super.onCreate();
setupStrictMode();
gcmAvailable = GooglePlayServices.isAvailable(this);
+ SimCards = SimCardHelper.getSimCards(getApplicationContext());
preferences = new Preferences(this);
preferences.migrate();
diff --git a/app/src/main/java/com/zegoggles/smssync/activity/MainActivity.java b/app/src/main/java/com/zegoggles/smssync/activity/MainActivity.java
index 824fa4048..d0f734167 100644
--- a/app/src/main/java/com/zegoggles/smssync/activity/MainActivity.java
+++ b/app/src/main/java/com/zegoggles/smssync/activity/MainActivity.java
@@ -28,6 +28,7 @@
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
+import android.Manifest;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -116,6 +117,8 @@ public class MainActivity extends ThemeActivity implements
private static final int REQUEST_PERMISSIONS_BACKUP_MANUAL = 4;
private static final int REQUEST_PERMISSIONS_BACKUP_MANUAL_SKIP = 5;
private static final int REQUEST_PERMISSIONS_BACKUP_SERVICE = 6;
+ private static final int REQUEST_PERMISSIONS_PHONE = 7;
+
public static final String EXTRA_PERMISSIONS = "permissions";
private static final String SCREEN_TITLE_RES = "titleRes";
@@ -134,7 +137,8 @@ public void onCreate(Bundle bundle) {
setSupportActionBar(toolbar);
getSupportFragmentManager().addOnBackStackChangedListener(this);
- authPreferences = new AuthPreferences(this);
+ //XOAuth2 is legacy and therefore not considered for multi-sim (authPreferences only used in this context here)
+ authPreferences = new AuthPreferences(this, 0);
oauth2Client = new OAuth2Client(authPreferences.getOAuth2ClientId());
fallbackAuthIntent = new Intent(this, OAuth2WebAuthActivity.class).setData(oauth2Client.requestUrl());
preferenceTitles = new PreferenceTitles(getResources(), R.xml.preferences);
@@ -147,6 +151,7 @@ public void onCreate(Bundle bundle) {
}
checkDefaultSmsApp();
requestPermissionsIfNeeded();
+ requestPhoneStatePermission();
}
@Override
@@ -309,7 +314,9 @@ public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, Preferen
@Override public void onBackStackChanged() {
if (getSupportActionBar() == null) return;
- getSupportActionBar().setSubtitle(getCurrentTitle());
+ @StringRes Integer title = getCurrentTitle();
+ if (title == null) return;
+ getSupportActionBar().setSubtitle(title);
getSupportActionBar().setDisplayHomeAsUpEnabled(getSupportFragmentManager().getBackStackEntryCount() > 0);
}
@@ -318,10 +325,14 @@ public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, Preferen
onBackStackChanged();
}
- private @StringRes int getCurrentTitle() {
+ private @StringRes Integer getCurrentTitle() {
final int entryCount = getSupportFragmentManager().getBackStackEntryCount();
+ final List fragments = getSupportFragmentManager().getFragments();
if (entryCount == 0) {
return 0;
+ } else if (fragments.size() > 0 && fragments.get(fragments.size() - 1)
+ instanceof com.zegoggles.smssync.activity.fragments.AdvancedSettings.Server) {
+ return null;
} else {
final FragmentManager.BackStackEntry entry = getSupportFragmentManager().getBackStackEntryAt(entryCount - 1);
return entry.getBreadCrumbTitleRes();
@@ -329,10 +340,10 @@ public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, Preferen
}
@Subscribe public void performAction(PerformAction action) {
- if (authPreferences.isLoginInformationSet()) {
+ if (atLeastOneLoginInformationSet(this)) {
if (action.confirm) {
showDialog(CONFIRM_ACTION, new BundleBuilder().putString(ACTION, action.action.name()).build());
- } else if (preferences.isFirstBackup() && action.action == Backup) {
+ } else if (preferences.isFirstBackup(0) && action.action == Backup) {
showDialog(FIRST_SYNC);
} else {
doPerform(action.action);
@@ -463,6 +474,10 @@ private void checkDefaultSmsApp() {
}
}
+ private void requestPhoneStatePermission() {
+ ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.READ_PHONE_STATE }, REQUEST_PERMISSIONS_PHONE);
+ }
+
private void requestPermissionsIfNeeded() {
final Intent intent = getIntent();
if (intent != null && intent.hasExtra(EXTRA_PERMISSIONS)) {
@@ -472,6 +487,13 @@ private void requestPermissionsIfNeeded() {
}
}
+ private boolean atLeastOneLoginInformationSet(Context context) {
+ for (Integer settingsId = 0; settingsId < App.SimCards.length; settingsId++) {
+ if (new AuthPreferences(context, settingsId).isLoginInformationSet()) return true;
+ }
+ return false;
+ }
+
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
diff --git a/app/src/main/java/com/zegoggles/smssync/activity/StatusPreference.java b/app/src/main/java/com/zegoggles/smssync/activity/StatusPreference.java
index ce49bf81d..787f1f376 100644
--- a/app/src/main/java/com/zegoggles/smssync/activity/StatusPreference.java
+++ b/app/src/main/java/com/zegoggles/smssync/activity/StatusPreference.java
@@ -230,7 +230,8 @@ private void onRestore() {
private void onAuthFailed() {
statusLabel.setText(R.string.status_auth_failure);
- if (new AuthPreferences(getContext()).useXOAuth()) {
+ ////XOAuth2 is legacy and therefore not considered for multi-sim
+ if (new AuthPreferences(getContext(), 0).useXOAuth()) {
syncDetailsLabel.setText(R.string.status_auth_failure_details_xoauth);
} else {
syncDetailsLabel.setText(R.string.status_auth_failure_details_plain);
@@ -276,7 +277,12 @@ private void finishedRestore(RestoreState newState) {
}
private void idle() {
- syncDetailsLabel.setText(getLastSyncText(preferences.getDataTypePreferences().getMostRecentSyncedDate()));
+ long mostRecentSyncedDate = 0;
+ for (Integer settingsId = 0; settingsId < App.SimCards.length; settingsId++) {
+ long mostRecentSyncedDateForSettingsId = preferences.getDataTypePreferences().getMostRecentSyncedDate(settingsId);
+ if (mostRecentSyncedDateForSettingsId>mostRecentSyncedDate) mostRecentSyncedDate = mostRecentSyncedDateForSettingsId;
+ }
+ syncDetailsLabel.setText(getLastSyncText(mostRecentSyncedDate));
statusLabel.setText(R.string.status_idle);
statusLabel.setTextColor(idleColor);
statusIcon.setImageDrawable(idle);
diff --git a/app/src/main/java/com/zegoggles/smssync/activity/fragments/AdvancedSettings.java b/app/src/main/java/com/zegoggles/smssync/activity/fragments/AdvancedSettings.java
index 8564b8e3e..7427bf837 100644
--- a/app/src/main/java/com/zegoggles/smssync/activity/fragments/AdvancedSettings.java
+++ b/app/src/main/java/com/zegoggles/smssync/activity/fragments/AdvancedSettings.java
@@ -14,6 +14,10 @@
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.TwoStatePreference;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.EditTextPreference;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.app.ActionBar;
import com.squareup.otto.Subscribe;
import com.zegoggles.smssync.App;
@@ -31,12 +35,15 @@
import com.zegoggles.smssync.mail.DataType;
import com.zegoggles.smssync.preferences.AuthPreferences;
import com.zegoggles.smssync.preferences.DataTypePreferences;
+import com.zegoggles.smssync.utils.SimCardHelper;
import java.text.DateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import static android.Manifest.permission.READ_CONTACTS;
import static android.Manifest.permission.WRITE_CALENDAR;
@@ -83,7 +90,9 @@ public void onResume() {
@Override
public void onCreatePreferences(Bundle bundle, String rootKey) {
super.onCreatePreferences(bundle, rootKey);
- authPreferences = new AuthPreferences(getContext());
+
+ //XOAuth2 is legacy and therefore not considered for multi-sim (authPreferences only used in this context here)
+ authPreferences = new AuthPreferences(getContext(), 0);
connected = findPreference(CONNECTED.key);
assert connected != null;
connected.setSummaryProvider(new Preference.SummaryProvider() {
@@ -128,6 +137,30 @@ public static class Backup extends SMSBackupPreferenceFragment {
private static final int REQUEST_CALL_LOG_PERMISSIONS = 0;
private CheckBoxPreference callLogPreference;
+ public void onCreatePreferences(Bundle bundle, String rootKey) {
+ super.onCreatePreferences(bundle, rootKey);
+
+ PreferenceScreen mainSettings = getPreferenceScreen();
+ for (Integer settingsId = 0; settingsId < App.SimCards.length; settingsId++) {
+ if (settingsId>0) {
+ EditTextPreference imapFolder = findPreference(DataType.PreferenceKeys.IMAP_FOLDER);
+ EditTextPreference cloneImapFolder = new EditTextPreference(getContext());
+ cloneImapFolder.setTitle(SimCardHelper.addPhoneNumberIfMultiSim(getString(R.string.ui_imap_folder_label), settingsId));
+ cloneImapFolder.setKey(SimCardHelper.addSettingsId(DataType.PreferenceKeys.IMAP_FOLDER, settingsId));
+ cloneImapFolder.setDialogTitle(SimCardHelper.addPhoneNumberIfMultiSim(getString(R.string.ui_imap_folder_label), settingsId));
+ cloneImapFolder.setDialogMessage(getString(R.string.ui_imap_folder_label_dialog_msg));
+ cloneImapFolder.setDefaultValue(getString(R.string.imap_folder_default));
+ cloneImapFolder.setSummaryProvider(EditTextPreference.SimpleSummaryProvider.getInstance());
+ insertPreference(imapFolder.getOrder()+1, mainSettings, cloneImapFolder);
+ }
+ registerValidImapFolderCheck(settingsId);
+ }
+
+ EditTextPreference imapFolder = findPreference(DataType.PreferenceKeys.IMAP_FOLDER);
+ imapFolder.setTitle(SimCardHelper.addPhoneNumberIfMultiSim(imapFolder.getTitle().toString(), 0));
+ imapFolder.setDialogTitle(SimCardHelper.addPhoneNumberIfMultiSim(getString(R.string.ui_imap_folder_label), 0));
+ }
+
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
@@ -142,7 +175,7 @@ public void onResume() {
updateBackupContactGroupLabelFromPref();
updateLastBackupTimes();
initGroups();
- registerValidImapFolderCheck();
+
findPreference(MAX_ITEMS_PER_SYNC.key)
.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
@@ -204,9 +237,12 @@ private void updateMaxItemsPerSync(String newValue) {
private void updateLastBackupTimes() {
for (DataType type : DataType.values()) {
- findPreference(type.backupEnabledPreference).setSummary(
- getLastSyncText(preferences.getDataTypePreferences().getMaxSyncedDate(type))
- );
+ long maxSyncedDate = 0;
+ for (Integer settingsId = 0; settingsId < App.SimCards.length; settingsId++) {
+ long maxSyncedDateForSettingsId = preferences.getDataTypePreferences().getMaxSyncedDate(type, settingsId);
+ if (maxSyncedDateForSettingsId>maxSyncedDate) maxSyncedDate = maxSyncedDateForSettingsId;
+ }
+ findPreference(type.backupEnabledPreference).setSummary(getLastSyncText(maxSyncedDate));
}
}
@@ -224,8 +260,8 @@ private void updateBackupContactGroupLabelFromPref() {
getString(R.string.ui_backup_contact_group_label));
}
- private void registerValidImapFolderCheck() {
- findPreference(SMS.folderPreference)
+ private void registerValidImapFolderCheck(Integer settingsId) {
+ findPreference(SimCardHelper.addSettingsId(SMS.folderPreference, settingsId))
.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, final Object newValue) {
return checkValidImapFolder(getFragmentManager(), newValue.toString());
@@ -247,15 +283,51 @@ private void initGroups() {
public static class CallLog extends SMSBackupPreferenceFragment {
private static final int REQUEST_CALENDAR_ACCESS = 0;
private CheckBoxPreference enabledPreference;
- private ListPreference calendarPreference;
- private Preference folderPreference;
+
+ public void onCreatePreferences(Bundle bundle, String rootKey) {
+ super.onCreatePreferences(bundle, rootKey);
+
+ PreferenceScreen mainSettings = getPreferenceScreen();
+
+ for (Integer settingsId = 0; settingsId < App.SimCards.length; settingsId++) {
+ if (settingsId>0) {
+ addImapFolder(mainSettings, settingsId);
+ addCalendar(mainSettings, settingsId);
+ }
+ registerValidCallLogFolderCheck(settingsId);
+ }
+
+ EditTextPreference imapFolder = findPreference(DataType.PreferenceKeys.IMAP_FOLDER_CALLLOG);
+ imapFolder.setTitle(SimCardHelper.addPhoneNumberIfMultiSim(imapFolder.getTitle().toString(), 0));
+ imapFolder.setDialogTitle(SimCardHelper.addPhoneNumberIfMultiSim(getString(R.string.ui_imap_folder_calllog_label), 0));
+ }
+
+ private void addImapFolder(PreferenceScreen mainSettings, Integer settingsId) {
+ EditTextPreference imapFolder = findPreference(DataType.PreferenceKeys.IMAP_FOLDER_CALLLOG);
+ EditTextPreference cloneImapFolder = new EditTextPreference(getContext());
+ cloneImapFolder.setTitle(SimCardHelper.addPhoneNumberIfMultiSim(getString(R.string.ui_imap_folder_calllog_label), settingsId));
+ cloneImapFolder.setKey(SimCardHelper.addSettingsId(DataType.PreferenceKeys.IMAP_FOLDER_CALLLOG, settingsId));
+ cloneImapFolder.setDialogTitle(SimCardHelper.addPhoneNumberIfMultiSim(getString(R.string.ui_imap_folder_calllog_label), settingsId));
+ cloneImapFolder.setDialogMessage(getString(R.string.ui_imap_folder_calllog_label_dialog_msg));
+ cloneImapFolder.setDefaultValue(getString(R.string.imap_folder_calllog_default));
+ cloneImapFolder.setSummaryProvider(EditTextPreference.SimpleSummaryProvider.getInstance());
+ insertPreference(imapFolder.getOrder()+1, mainSettings, cloneImapFolder);
+ }
+
+ private void addCalendar(PreferenceScreen mainSettings, Integer settingsId) {
+ ListPreference calendarPreference = findPreference(CALLLOG_SYNC_CALENDAR.key);
+ ListPreference cloneCalendarPreference = new ListPreference(getContext());
+ cloneCalendarPreference.setTitle(SimCardHelper.addPhoneNumberIfMultiSim(getString(R.string.ui_backup_calllog_sync_calendar_label), settingsId));
+ cloneCalendarPreference.setSummary(calendarPreference.getSummary());
+ cloneCalendarPreference.setKey(SimCardHelper.addSettingsId(CALLLOG_SYNC_CALENDAR.key, settingsId));
+ cloneCalendarPreference.setDefaultValue("-1");
+ insertPreference(calendarPreference.getOrder()+1, mainSettings, cloneCalendarPreference);
+ }
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
enabledPreference = findPreference(CALLLOG_SYNC_CALENDAR_ENABLED.key);
- calendarPreference = findPreference(CALLLOG_SYNC_CALENDAR.key);
- folderPreference = findPreference(CALLLOG.folderPreference);
}
@Override
@@ -264,7 +336,6 @@ public void onResume() {
initCalendars();
updateCallLogCalendarLabelFromPref();
- registerValidCallLogFolderCheck();
registerCalendarSyncEnabledCallback();
addPreferenceListener(CALLLOG_BACKUP_AFTER_CALL.key);
@@ -304,19 +375,25 @@ private boolean needCalendarPermission() {
}
private void updateCallLogCalendarLabelFromPref() {
- calendarPreference.setTitle(calendarPreference.getEntry() != null ? calendarPreference.getEntry() :
- getString(R.string.ui_backup_calllog_sync_calendar_label));
+ for (Integer settingsId = 0; settingsId < App.SimCards.length; settingsId++) {
+ ListPreference calendarPreference = findPreference(SimCardHelper.addSettingsId(CALLLOG_SYNC_CALENDAR.key,settingsId));
+ calendarPreference.setTitle(calendarPreference.getEntry() != null ? calendarPreference.getEntry() : SimCardHelper.addPhoneNumberIfMultiSim(getString(R.string.ui_backup_calllog_sync_calendar_label), settingsId));
+ }
}
private void initCalendars() {
if (needCalendarPermission()) return;
CalendarAccessor calendars = CalendarAccessor.Get.instance(getContext().getContentResolver());
- initListPreference(calendarPreference, calendars.getCalendars(), false);
+ for (Integer settingsId = 0; settingsId < App.SimCards.length; settingsId++) {
+ ListPreference calendarPreference = findPreference(SimCardHelper.addSettingsId(CALLLOG_SYNC_CALENDAR.key,settingsId));
+ initListPreference(calendarPreference, calendars.getCalendars(), false);
+ }
}
- private void registerValidCallLogFolderCheck() {
- folderPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
+ private void registerValidCallLogFolderCheck(Integer settingsId) {
+ findPreference(SimCardHelper.addSettingsId(CALLLOG.folderPreference, settingsId))
+ .setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, final Object newValue) {
return checkValidImapFolder(getFragmentManager(), newValue.toString());
}
@@ -346,18 +423,77 @@ private void updateMaxItemsPerRestore(String newValue) {
public static class Server extends SMSBackupPreferenceFragment {
private AuthPreferences authPreferences;
+ private Integer settingsId;
@Override
public void onCreatePreferences(Bundle bundle, String rootKey) {
+ settingsId = 0;
+ Pattern p = Pattern.compile("^(.*)_(\\d*)$");
+ Matcher m = p.matcher(rootKey);
+ if (m.find()) {
+ settingsId=Integer.parseInt(m.group(2));
+ rootKey=m.group(1);
+ }
+
super.onCreatePreferences(bundle, rootKey);
- authPreferences = new AuthPreferences(getContext());
+
+ authPreferences = new AuthPreferences(getContext(), settingsId);
+
+ if (settingsId > 0) {
+ setPreferenceScreen(cloneSettings());
+ insertTakeOverCheckBox();
+
+ setServerState(authPreferences.getTakeOver());
+
+ findPreference(SimCardHelper.addSettingsId(AuthPreferences.SERVER_TAKEOVER, settingsId))
+ .setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ setServerState((Boolean)newValue);
+ return true;
+ }
+ });
+ }
+ }
+
+ private void setServerState(Boolean takeOver) {
+ Boolean state = !takeOver;
+ findPreference(SimCardHelper.addSettingsId(AuthPreferences.SERVER_ADDRESS, settingsId)).setEnabled(state);
+ findPreference(SimCardHelper.addSettingsId(AuthPreferences.IMAP_USER, settingsId)).setEnabled(state);
+ findPreference(SimCardHelper.addSettingsId(AuthPreferences.IMAP_PASSWORD, settingsId)).setEnabled(state);
+ findPreference(SimCardHelper.addSettingsId(AuthPreferences.SERVER_PROTOCOL, settingsId)).setEnabled(state);
+ findPreference(SimCardHelper.addSettingsId(AuthPreferences.SERVER_TRUST_ALL_CERTIFICATES, settingsId)).setEnabled(state);
+ }
+
+ private void insertTakeOverCheckBox() {
+ PreferenceScreen preferenceScreen = getPreferenceScreen();
+
+ CheckBoxPreference checkbox = new CheckBoxPreference(getContext());
+ checkbox.setTitle(R.string.ui_server_takeover);
+ checkbox.setSummary(R.string.ui_server_takeover_summary);
+ checkbox.setDefaultValue(true);
+ checkbox.setKey(SimCardHelper.addSettingsId(AuthPreferences.SERVER_TAKEOVER, settingsId));
+ insertPreference(0, preferenceScreen, checkbox);
+ }
+
+ private PreferenceScreen cloneSettings() {
+ PreferenceScreen originalScreen = getPreferenceScreen();
+ PreferenceScreen copyScreen = getPreferenceManager().createPreferenceScreen(getContext());
+
+ while(originalScreen.getPreferenceCount() > 0) {
+ Preference pref = originalScreen.getPreference(0);
+ pref.setKey(pref.getKey()+"_"+settingsId.toString());
+
+ originalScreen.removePreference(pref);
+ copyScreen.addPreference(pref);
+ }
+ return copyScreen;
}
@Override
public void onResume() {
super.onResume();
- findPreference(AuthPreferences.IMAP_PASSWORD)
+ findPreference(SimCardHelper.addSettingsId(AuthPreferences.IMAP_PASSWORD, settingsId))
.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
authPreferences.setImapPassword(newValue.toString());
@@ -365,6 +501,15 @@ public boolean onPreferenceChange(Preference preference, Object newValue) {
}
});
}
+
+ @Override public void onStart() {
+ super.onStart();
+
+ ActionBar actionBar = ((AppCompatActivity)getActivity()).getSupportActionBar();
+ if ( actionBar == null) return;
+ actionBar.setSubtitle(SimCardHelper.addPhoneNumberIfMultiSim(getString(R.string.imap_settings), settingsId));
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
}
static void updateMaxItems(Preference preference, int currentValue, String newValue) {
diff --git a/app/src/main/java/com/zegoggles/smssync/activity/fragments/MainSettings.java b/app/src/main/java/com/zegoggles/smssync/activity/fragments/MainSettings.java
index c89b73dc2..151e8e9aa 100644
--- a/app/src/main/java/com/zegoggles/smssync/activity/fragments/MainSettings.java
+++ b/app/src/main/java/com/zegoggles/smssync/activity/fragments/MainSettings.java
@@ -7,6 +7,7 @@
import androidx.preference.CheckBoxPreference;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
import com.squareup.otto.Subscribe;
import com.zegoggles.smssync.App;
@@ -19,6 +20,7 @@
import com.zegoggles.smssync.activity.events.SettingsResetEvent;
import com.zegoggles.smssync.mail.DataType;
import com.zegoggles.smssync.preferences.AuthPreferences;
+import com.zegoggles.smssync.utils.SimCardHelper;
import java.util.ArrayList;
import java.util.List;
@@ -37,10 +39,27 @@ public class MainSettings extends SMSBackupPreferenceFragment {
@Override
public void onCreatePreferences(Bundle bundle, String rootKey) {
super.onCreatePreferences(bundle, rootKey);
- authPreferences = new AuthPreferences(getContext());
- findPreference(AdvancedSettings.Server.class.getName()).setSummaryProvider(new Preference.SummaryProvider() {
+
+ //XOAuth2 is legacy and therefore not considered for multi-sim (authPreferences only used in this context here)
+ authPreferences = new AuthPreferences(getContext(), 0);
+
+ for (Integer settingsId = 0; settingsId < App.SimCards.length; settingsId++) {
+ if (settingsId>0) {
+ addAdvancedSettingsForMultipleSimCards(settingsId);
+ }
+
+ setSummaryForAdvancedServerSettings(settingsId);
+ }
+
+ PreferenceScreen advancedSettings = findPreference(AdvancedSettings.Server.class.getName());
+ advancedSettings.setTitle(SimCardHelper.addPhoneNumberIfMultiSim(advancedSettings.getTitle().toString(), 0));
+ }
+
+ private void setSummaryForAdvancedServerSettings(final Integer settingsId) {
+ findPreference(SimCardHelper.addSettingsId(AdvancedSettings.Server.class.getName(), settingsId)).setSummaryProvider(new Preference.SummaryProvider() {
@Override
public CharSequence provideSummary(Preference preference) {
+ authPreferences = new AuthPreferences(getContext(), settingsId);
if (authPreferences.usePlain() && authPreferences.isLoginInformationSet()) {
return authPreferences.toString();
} else {
@@ -54,7 +73,6 @@ public CharSequence provideSummary(Preference preference) {
public void onStart() {
super.onStart();
App.register(this);
-
}
@Override
public void onDestroy() {
@@ -170,4 +188,27 @@ public void userDonationState(State state) {
Log.w(TAG, e);
}
}
+
+ private void addAdvancedSettingsForMultipleSimCards(Integer settingsId) {
+ PreferenceScreen advancedSettings = findPreference(AdvancedSettings.Server.class.getName());
+ PreferenceScreen mainSettings = getPreferenceScreen();
+ if (advancedSettings != null) {
+ try {
+ PreferenceScreen clone = clonePreferenceScreen(advancedSettings, settingsId);
+ clone.setTitle(SimCardHelper.addPhoneNumberIfMultiSim(clone.getTitle().toString(), settingsId));
+ insertPreference(advancedSettings.getOrder()+1, mainSettings, clone);
+ } catch (Exception e) {
+ Log.w(TAG, "couldn't add advanced settings more than once");
+ }
+
+ }
+ }
+
+ private PreferenceScreen clonePreferenceScreen(PreferenceScreen advancedSettings, Integer settingsId) throws Exception {
+ PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(getContext());
+ screen.setTitle(advancedSettings.getTitle());
+ screen.setKey(SimCardHelper.addSettingsId(advancedSettings.getKey(), settingsId));
+ screen.setFragment(advancedSettings.getFragment());
+ return screen;
+ }
}
diff --git a/app/src/main/java/com/zegoggles/smssync/activity/fragments/SMSBackupPreferenceFragment.java b/app/src/main/java/com/zegoggles/smssync/activity/fragments/SMSBackupPreferenceFragment.java
index a22eb7eda..355297454 100644
--- a/app/src/main/java/com/zegoggles/smssync/activity/fragments/SMSBackupPreferenceFragment.java
+++ b/app/src/main/java/com/zegoggles/smssync/activity/fragments/SMSBackupPreferenceFragment.java
@@ -5,12 +5,15 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceGroup;
import com.zegoggles.smssync.App;
import com.zegoggles.smssync.R;
import com.zegoggles.smssync.activity.events.AutoBackupSettingsChangedEvent;
import com.zegoggles.smssync.preferences.Preferences;
+import java.util.HashMap;
+
public abstract class SMSBackupPreferenceFragment extends PreferenceFragmentCompat {
protected Preferences preferences;
private Handler handler;
@@ -42,4 +45,29 @@ public void run() {
});
}
}
+
+ protected void insertPreference(int position, PreferenceGroup preferenceList, Preference item) {
+ HashMap listItems = new HashMap();
+ int prefCount = preferenceList.getPreferenceCount();
+ for(int i = 0; i < prefCount; i++) {
+ Preference preference = preferenceList.getPreference(i);
+ listItems.put(preference.getOrder(), preference);
+ }
+
+ Integer cnt = 0;
+ for (Integer i : listItems.keySet()) {
+ Preference preference = listItems.get(i);
+ if (cnt checkPermissions(Context context) {
}
public static class PreferenceKeys {
- static final String IMAP_FOLDER = "imap_folder";
- static final String IMAP_FOLDER_CALLLOG = "imap_folder_calllog";
+ public static final String IMAP_FOLDER = "imap_folder";
+ public static final String IMAP_FOLDER_CALLLOG = "imap_folder_calllog";
static final String BACKUP_SMS = "backup_sms";
static final String BACKUP_MMS = "backup_mms";
diff --git a/app/src/main/java/com/zegoggles/smssync/mail/MessageConverter.java b/app/src/main/java/com/zegoggles/smssync/mail/MessageConverter.java
index 678201f9a..e69a2a627 100644
--- a/app/src/main/java/com/zegoggles/smssync/mail/MessageConverter.java
+++ b/app/src/main/java/com/zegoggles/smssync/mail/MessageConverter.java
@@ -50,6 +50,7 @@
public class MessageConverter {
private final Context context;
+ private final Preferences preferences;
private final ThreadHelper threadHelper = new ThreadHelper();
private final MarkAsReadTypes markAsReadType;
@@ -63,6 +64,7 @@ public MessageConverter(Context context,
PersonLookup personLookup,
ContactAccessor contactAccessor) {
this.context = context;
+ this.preferences = preferences;
markAsReadType = preferences.getMarkAsReadType();
this.personLookup = personLookup;
markAsReadOnRestore = preferences.getMarkAsReadOnRestore();
@@ -108,11 +110,11 @@ private boolean markAsSeen(DataType dataType, Map msgMap) {
}
}
- public @NonNull ConversionResult convertMessages(final Cursor cursor, DataType dataType)
+ public @NonNull ConversionResult convertMessages(final Cursor cursor, DataType dataType, Integer settingsId)
throws MessagingException {
final Map msgMap = getMessageMap(cursor);
- final Message m = messageGenerator.messageForDataType(msgMap, dataType);
+ final Message m = messageGenerator.messageForDataType(msgMap, dataType, settingsId);
final ConversionResult result = new ConversionResult(dataType);
if (m != null) {
m.setFlag(Flag.SEEN, markAsSeen(dataType, msgMap));
@@ -123,7 +125,7 @@ private boolean markAsSeen(DataType dataType, Map msgMap) {
}
- public @NonNull ContentValues messageToContentValues(final Message message)
+ public @NonNull ContentValues messageToContentValues(Integer settingsId, final Message message)
throws IOException, MessagingException {
if (message == null) throw new MessagingException("message is null");
@@ -148,6 +150,9 @@ private boolean markAsSeen(DataType dataType, Map msgMap) {
values.put(Telephony.TextBasedSmsColumns.THREAD_ID, threadHelper.getThreadId(context, address));
values.put(Telephony.TextBasedSmsColumns.READ,
markAsReadOnRestore ? "1" : Headers.get(message, Headers.READ));
+ if (App.SimCards.length > 1) {
+ values.put(Telephony.TextBasedSmsColumns.SUBSCRIPTION_ID, settingsId);
+ }
break;
case CALLLOG:
values.put(CallLog.Calls.NUMBER, Headers.get(message, Headers.ADDRESS));
@@ -155,6 +160,9 @@ private boolean markAsSeen(DataType dataType, Map msgMap) {
values.put(CallLog.Calls.DATE, Headers.get(message, Headers.DATE));
values.put(CallLog.Calls.DURATION, Long.valueOf(Headers.get(message, Headers.DURATION)));
values.put(CallLog.Calls.NEW, 0);
+ if (App.SimCards.length > 1) {
+ values.put(CallLog.Calls.PHONE_ACCOUNT_ID, getSimCardNumberOrIccId(settingsId));
+ }
PersonRecord record = personLookup.lookupPerson(Headers.get(message, Headers.ADDRESS));
if (!record.isUnknown()) {
@@ -170,6 +178,15 @@ private boolean markAsSeen(DataType dataType, Map msgMap) {
return values;
}
+ private String getSimCardNumberOrIccId(Integer settingsId) {
+ if (preferences.getUseIccIdForRestore()) {
+ return App.SimCards[settingsId].IccId;
+ } else {
+ Integer simCardNumber = settingsId + 1;
+ return simCardNumber.toString();
+ }
+ }
+
public DataType getDataType(Message message) throws MessagingException {
final String dataTypeHeader = Headers.get(message, Headers.DATATYPE);
if (dataTypeHeader == null) {
diff --git a/app/src/main/java/com/zegoggles/smssync/mail/MessageGenerator.java b/app/src/main/java/com/zegoggles/smssync/mail/MessageGenerator.java
index 5250a03ab..01528d7c0 100644
--- a/app/src/main/java/com/zegoggles/smssync/mail/MessageGenerator.java
+++ b/app/src/main/java/com/zegoggles/smssync/mail/MessageGenerator.java
@@ -67,16 +67,16 @@ class MessageGenerator {
this.callLogTypes = callLogTypes;
}
- public @Nullable Message messageForDataType(Map msgMap, DataType dataType) throws MessagingException {
+ public @Nullable Message messageForDataType(Map msgMap, DataType dataType, Integer settingsId) throws MessagingException {
switch (dataType) {
- case SMS: return messageFromMapSms(msgMap);
- case MMS: return messageFromMapMms(msgMap);
- case CALLLOG: return messageFromMapCallLog(msgMap);
+ case SMS: return messageFromMapSms(msgMap, settingsId);
+ case MMS: return messageFromMapMms(msgMap, settingsId);
+ case CALLLOG: return messageFromMapCallLog(msgMap, settingsId);
default: return null;
}
}
- private @Nullable Message messageFromMapSms(Map msgMap) throws MessagingException {
+ private @Nullable Message messageFromMapSms(Map msgMap, Integer settingsId) throws MessagingException {
final String address = msgMap.get(Telephony.TextBasedSmsColumns.ADDRESS);
if (TextUtils.isEmpty(address)) return null;
@@ -84,7 +84,7 @@ class MessageGenerator {
if (!includePersonInBackup(record, DataType.SMS)) return null;
final Message msg = new MimeMessage();
- msg.setSubject(getSubject(DataType.SMS, record));
+ msg.setSubject(getSubject(DataType.SMS, record, settingsId));
setBody(msg, new TextBody(msgMap.get(Telephony.TextBasedSmsColumns.BODY)));
final int messageType = toInt(msgMap.get(Telephony.TextBasedSmsColumns.TYPE));
@@ -110,7 +110,7 @@ class MessageGenerator {
return msg;
}
- private @Nullable Message messageFromMapMms(Map msgMap) throws MessagingException {
+ private @Nullable Message messageFromMapMms(Map msgMap, Integer settingsId) throws MessagingException {
if (LOCAL_LOGV) Log.v(TAG, "messageFromMapMms(" + msgMap + ")");
final Uri mmsUri = Uri.withAppendedPath(Consts.MMS_PROVIDER, msgMap.get(Telephony.BaseMmsColumns._ID));
@@ -125,7 +125,7 @@ class MessageGenerator {
}
final Message msg = new MimeMessage();
- msg.setSubject(getSubject(DataType.MMS, details.getRecipient()));
+ msg.setSubject(getSubject(DataType.MMS, details.getRecipient(), settingsId));
if (details.inbound) {
// msg_box == MmsConsts.MESSAGE_BOX_INBOX does not work
@@ -155,7 +155,7 @@ class MessageGenerator {
return msg;
}
- private @Nullable Message messageFromMapCallLog(Map msgMap) throws MessagingException {
+ private @Nullable Message messageFromMapCallLog(Map msgMap, Integer settingsId) throws MessagingException {
final String address = msgMap.get(CallLog.Calls.NUMBER);
final int callType = toInt(msgMap.get(CallLog.Calls.TYPE));
@@ -167,7 +167,7 @@ class MessageGenerator {
if (!includePersonInBackup(record, DataType.CALLLOG)) return null;
final Message msg = new MimeMessage();
- msg.setSubject(getSubject(DataType.CALLLOG, record));
+ msg.setSubject(getSubject(DataType.CALLLOG, record, settingsId));
switch (callType) {
case CallLog.Calls.OUTGOING_TYPE:
@@ -204,9 +204,9 @@ class MessageGenerator {
return msg;
}
- private String getSubject(@NonNull DataType type, @NonNull PersonRecord record) {
+ private String getSubject(@NonNull DataType type, @NonNull PersonRecord record, Integer settingsId) {
return prefix ?
- String.format(Locale.ENGLISH, "[%s] %s", dataTypePreferences.getFolder(type), record.getName()) :
+ String.format(Locale.ENGLISH, "[%s] %s", dataTypePreferences.getFolder(type, settingsId), record.getName()) :
context.getString(type.withField, record.getName());
}
diff --git a/app/src/main/java/com/zegoggles/smssync/preferences/AuthPreferences.java b/app/src/main/java/com/zegoggles/smssync/preferences/AuthPreferences.java
index afb020228..09cd12083 100644
--- a/app/src/main/java/com/zegoggles/smssync/preferences/AuthPreferences.java
+++ b/app/src/main/java/com/zegoggles/smssync/preferences/AuthPreferences.java
@@ -12,6 +12,7 @@
import com.zegoggles.smssync.R;
import com.zegoggles.smssync.auth.OAuth2Client;
import com.zegoggles.smssync.auth.TokenRefresher;
+import com.zegoggles.smssync.utils.SimCardHelper;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
@@ -25,8 +26,11 @@ public class AuthPreferences {
private static final String UTF_8 = "UTF-8";
private final Context context;
private final SharedPreferences preferences;
+ private final Integer settingsId;
private SharedPreferences credentials;
+ public static final String SERVER_TAKEOVER = "server_takeover";
+
public static final String SERVER_AUTHENTICATION = "server_authentication";
private static final String OAUTH2_USER = "oauth2_user";
@@ -43,9 +47,9 @@ public class AuthPreferences {
/**
* Preference key containing the server protocol
*/
- private static final String SERVER_PROTOCOL = "server_protocol";
+ public static final String SERVER_PROTOCOL = "server_protocol";
- private static final String SERVER_TRUST_ALL_CERTIFICATES = "server_trust_all_certificates";
+ public static final String SERVER_TRUST_ALL_CERTIFICATES = "server_trust_all_certificates";
/**
* IMAP URI.
@@ -64,9 +68,11 @@ public class AuthPreferences {
private static final String DEFAULT_SERVER_ADDRESS = "imap.gmail.com:993";
private static final String DEFAULT_SERVER_PROTOCOL = "+ssl+";
- public AuthPreferences(Context context) {
+ public AuthPreferences(Context context, Integer settingsId) {
this.context = context.getApplicationContext();
this.preferences = PreferenceManager.getDefaultSharedPreferences(context);
+ //settingsId has no effect on XOAuth2 (deprecated)
+ this.settingsId = settingsId;
}
public String getOauth2Token() {
@@ -117,11 +123,11 @@ public String getOAuth2ClientId() {
}
public void setImapPassword(String s) {
- getCredentials().edit().putString(IMAP_PASSWORD, s).commit();
+ getCredentials().edit().putString(addSettingsId(IMAP_PASSWORD), s).commit();
}
public void setImapUser(String s) {
- preferences.edit().putString(IMAP_USER, s).commit();
+ preferences.edit().putString(addSettingsId(IMAP_USER), s).commit();
}
@SuppressWarnings("deprecation")
@@ -185,16 +191,32 @@ public String getStoreUri() {
}
}
+ private String addSettingsId(String stringWithoutSettingsId) {
+ if (getTakeOver()) {
+ return SimCardHelper.addSettingsId(stringWithoutSettingsId, 0);
+ } else {
+ return SimCardHelper.addSettingsId(stringWithoutSettingsId, settingsId);
+ }
+ }
+
+ public Boolean getTakeOver() {
+ if (settingsId == 0) {
+ return false;
+ } else {
+ return preferences.getBoolean(SimCardHelper.addSettingsId(SERVER_TAKEOVER, settingsId), true);
+ }
+ }
+
private String getServerAddress() {
- return preferences.getString(SERVER_ADDRESS, DEFAULT_SERVER_ADDRESS);
+ return preferences.getString(addSettingsId(SERVER_ADDRESS), DEFAULT_SERVER_ADDRESS);
}
private String getServerProtocol() {
- return preferences.getString(SERVER_PROTOCOL, DEFAULT_SERVER_PROTOCOL);
+ return preferences.getString(addSettingsId(SERVER_PROTOCOL), DEFAULT_SERVER_PROTOCOL);
}
public boolean isTrustAllCertificates() {
- return preferences.getBoolean(SERVER_TRUST_ALL_CERTIFICATES, false);
+ return preferences.getBoolean(addSettingsId(SERVER_TRUST_ALL_CERTIFICATES), false);
}
private String formatUri(AuthType authType, String serverProtocol, String username, String password, String serverAddress) {
@@ -226,15 +248,15 @@ private SharedPreferences getCredentials() {
}
public String getServername() {
- return preferences.getString(SERVER_ADDRESS, null);
+ return preferences.getString(addSettingsId(SERVER_ADDRESS), null);
}
public String getImapUsername() {
- return preferences.getString(IMAP_USER, null);
+ return preferences.getString(addSettingsId(IMAP_USER), null);
}
private String getImapPassword() {
- return getCredentials().getString(IMAP_PASSWORD, null);
+ return getCredentials().getString(addSettingsId(IMAP_PASSWORD), null);
}
/**
@@ -281,8 +303,8 @@ void migrate() {
if ("+ssl".equals(getServerProtocol()) ||
"+tls".equals(getServerProtocol())) {
preferences.edit()
- .putBoolean(SERVER_TRUST_ALL_CERTIFICATES, true)
- .putString(SERVER_PROTOCOL, getServerProtocol()+"+")
+ .putBoolean(addSettingsId(SERVER_TRUST_ALL_CERTIFICATES), true)
+ .putString(addSettingsId(SERVER_PROTOCOL), getServerProtocol()+"+")
.commit();
}
}
diff --git a/app/src/main/java/com/zegoggles/smssync/preferences/DataTypePreferences.java b/app/src/main/java/com/zegoggles/smssync/preferences/DataTypePreferences.java
index 67440bf54..e9da277fe 100644
--- a/app/src/main/java/com/zegoggles/smssync/preferences/DataTypePreferences.java
+++ b/app/src/main/java/com/zegoggles/smssync/preferences/DataTypePreferences.java
@@ -2,6 +2,8 @@
import android.content.SharedPreferences;
import com.zegoggles.smssync.mail.DataType;
+import com.zegoggles.smssync.utils.SimCardHelper;
+import com.zegoggles.smssync.App;
import java.util.ArrayList;
import java.util.EnumSet;
@@ -49,15 +51,15 @@ public EnumSet enabled() {
return enabledTypes.isEmpty() ? EnumSet.noneOf(DataType.class) : EnumSet.copyOf(enabledTypes);
}
- public String getFolder(DataType dataType) {
- return sharedPreferences.getString(dataType.folderPreference, dataType.defaultFolder);
+ public String getFolder(DataType dataType, Integer settingsId) {
+ return sharedPreferences.getString(SimCardHelper.addSettingsId(dataType.folderPreference, settingsId), dataType.defaultFolder);
}
/**
* @return returns the last synced date in milliseconds (epoch)
*/
- public long getMaxSyncedDate(DataType dataType) {
- final long maxSynced = sharedPreferences.getLong(dataType.maxSyncedPreference, MAX_SYNCED_DATE);
+ public long getMaxSyncedDate(DataType dataType, Integer settingsId) {
+ final long maxSynced = sharedPreferences.getLong(SimCardHelper.addSettingsId(dataType.maxSyncedPreference, settingsId), MAX_SYNCED_DATE);
if (dataType == MMS && maxSynced > 0) {
return maxSynced * 1000L;
} else {
@@ -65,21 +67,23 @@ public long getMaxSyncedDate(DataType dataType) {
}
}
- public boolean setMaxSyncedDate(DataType dataType, long max) {
- return sharedPreferences.edit().putLong(dataType.maxSyncedPreference, max).commit();
+ public boolean setMaxSyncedDate(DataType dataType, long max, Integer settingsId) {
+ return sharedPreferences.edit().putLong(SimCardHelper.addSettingsId(dataType.maxSyncedPreference, settingsId), max).commit();
}
- public long getMostRecentSyncedDate() {
+ public long getMostRecentSyncedDate(Integer settingsId) {
return Math.max(Math.max(
- getMaxSyncedDate(DataType.SMS),
- getMaxSyncedDate(DataType.CALLLOG)),
- getMaxSyncedDate(DataType.MMS));
+ getMaxSyncedDate(DataType.SMS, settingsId),
+ getMaxSyncedDate(DataType.CALLLOG, settingsId)),
+ getMaxSyncedDate(DataType.MMS, settingsId));
}
public void clearLastSyncData() {
SharedPreferences.Editor editor = sharedPreferences.edit();
- for (DataType type : DataType.values()) {
- editor.remove(type.maxSyncedPreference);
+ for (Integer settingsId = 0; settingsId < App.SimCards.length; settingsId++) {
+ for (DataType type : DataType.values()) {
+ editor.remove(SimCardHelper.addSettingsId(type.maxSyncedPreference, settingsId));
+ }
}
editor.commit();
}
diff --git a/app/src/main/java/com/zegoggles/smssync/preferences/Defaults.java b/app/src/main/java/com/zegoggles/smssync/preferences/Defaults.java
index aab9b0046..4417a44e8 100644
--- a/app/src/main/java/com/zegoggles/smssync/preferences/Defaults.java
+++ b/app/src/main/java/com/zegoggles/smssync/preferences/Defaults.java
@@ -23,6 +23,7 @@ class Defaults {
public static final int MAX_ITEMS_PER_SYNC = -1;
public static final int MAX_ITEMS_PER_RESTORE = -1;
public static final boolean MARK_AS_READ_ON_RESTORE = true;
+ public static final boolean USE_ICC_ID_FOR_RESTORE = true;
private Defaults() {}
}
diff --git a/app/src/main/java/com/zegoggles/smssync/preferences/Preferences.java b/app/src/main/java/com/zegoggles/smssync/preferences/Preferences.java
index a57518738..9bf650d5b 100644
--- a/app/src/main/java/com/zegoggles/smssync/preferences/Preferences.java
+++ b/app/src/main/java/com/zegoggles/smssync/preferences/Preferences.java
@@ -26,6 +26,7 @@
import com.zegoggles.smssync.R;
import com.zegoggles.smssync.contacts.ContactGroup;
import com.zegoggles.smssync.mail.DataType;
+import com.zegoggles.smssync.utils.SimCardHelper;
import java.util.Locale;
@@ -44,6 +45,7 @@
import static com.zegoggles.smssync.preferences.Preferences.Keys.LAST_VERSION_CODE;
import static com.zegoggles.smssync.preferences.Preferences.Keys.MAIL_SUBJECT_PREFIX;
import static com.zegoggles.smssync.preferences.Preferences.Keys.MARK_AS_READ_ON_RESTORE;
+import static com.zegoggles.smssync.preferences.Preferences.Keys.USE_ICC_ID_FOR_RESTORE;
import static com.zegoggles.smssync.preferences.Preferences.Keys.MARK_AS_READ_TYPES;
import static com.zegoggles.smssync.preferences.Preferences.Keys.MAX_ITEMS_PER_RESTORE;
import static com.zegoggles.smssync.preferences.Preferences.Keys.MAX_ITEMS_PER_SYNC;
@@ -90,6 +92,7 @@ public enum Keys {
RESTORE_STARRED_ONLY("restore_starred_only"),
MARK_AS_READ_TYPES("mark_as_read_types"),
MARK_AS_READ_ON_RESTORE("mark_as_read_on_restore"),
+ USE_ICC_ID_FOR_RESTORE("use_icc_id_for_restore"),
THIRD_PARTY_INTEGRATION("third_party_integration"),
APP_LOG("app_log"),
APP_LOG_DEBUG("app_log_debug"),
@@ -128,8 +131,8 @@ public ContactGroup getBackupContactGroup() {
return new ContactGroup(getStringAsInt(BACKUP_CONTACT_GROUP, -1));
}
- public boolean isCallLogCalendarSyncEnabled() {
- return getCallLogCalendarId() >= 0 &&
+ public boolean isCallLogCalendarSyncEnabled(Integer settingsId) {
+ return getCallLogCalendarId(settingsId) >= 0 &&
preferences.getBoolean(CALLLOG_SYNC_CALENDAR_ENABLED.key, false);
}
@@ -137,8 +140,8 @@ public boolean isCallLogBackupAfterCallEnabled() {
return preferences.getBoolean(CALLLOG_BACKUP_AFTER_CALL.key, false);
}
- public int getCallLogCalendarId() {
- return getStringAsInt(CALLLOG_SYNC_CALENDAR, -1);
+ public int getCallLogCalendarId(Integer settingsId) {
+ return getStringAsInt(SimCardHelper.addSettingsId(CALLLOG_SYNC_CALENDAR.key, settingsId), -1);
}
public CallLogTypes getCallLogType() {
@@ -214,13 +217,17 @@ public boolean getMarkAsReadOnRestore() {
return preferences.getBoolean(MARK_AS_READ_ON_RESTORE.key, Defaults.MARK_AS_READ_ON_RESTORE);
}
+ public boolean getUseIccIdForRestore() {
+ return preferences.getBoolean(USE_ICC_ID_FOR_RESTORE.key, Defaults.USE_ICC_ID_FOR_RESTORE);
+ }
+
public AddressStyle getEmailAddressStyle() {
return getDefaultType(preferences, Keys.EMAIL_ADDRESS_STYLE.key, AddressStyle.class, AddressStyle.NAME);
}
- public boolean isFirstBackup() {
+ public boolean isFirstBackup(Integer settingsId) {
for (DataType type : DataType.values()) {
- if (preferences.contains(type.maxSyncedPreference)) {
+ if (preferences.contains(SimCardHelper.addSettingsId(type.maxSyncedPreference, settingsId))) {
return false;
}
}
@@ -228,7 +235,7 @@ public boolean isFirstBackup() {
}
public boolean isFirstUse() {
- if (isFirstBackup() && !preferences.contains(FIRST_USE.key)) {
+ if (isFirstBackup(0) && !preferences.contains(FIRST_USE.key)) {
preferences.edit().putBoolean(FIRST_USE.key, false).commit();
return true;
} else {
@@ -292,7 +299,9 @@ public void setUseOldScheduler(boolean enabled) {
}
public void migrate() {
- new AuthPreferences(context).migrate();
+ for (Integer settingsId = 0; settingsId < App.SimCards.length; settingsId++) {
+ new AuthPreferences(context, settingsId).migrate();
+ }
}
static > T getDefaultType(SharedPreferences preferences, String pref, Class tClazz, T defaultType) {
diff --git a/app/src/main/java/com/zegoggles/smssync/receiver/SmsBroadcastReceiver.java b/app/src/main/java/com/zegoggles/smssync/receiver/SmsBroadcastReceiver.java
index 6a176a6b8..46fc97e58 100644
--- a/app/src/main/java/com/zegoggles/smssync/receiver/SmsBroadcastReceiver.java
+++ b/app/src/main/java/com/zegoggles/smssync/receiver/SmsBroadcastReceiver.java
@@ -26,6 +26,7 @@
import com.zegoggles.smssync.preferences.Preferences;
import com.zegoggles.smssync.service.BackupJobs;
import com.zegoggles.smssync.utils.AppLog;
+import com.zegoggles.smssync.App;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
@@ -59,8 +60,8 @@ private boolean shouldSchedule(Context context) {
final Preferences preferences = getPreferences(context);
final boolean autoBackupEnabled = preferences.isAutoBackupEnabled();
- final boolean loginInformationSet = getAuthPreferences(context).isLoginInformationSet();
- final boolean firstBackup = preferences.isFirstBackup();
+ final boolean loginInformationSet = atLeastOneLoginInformationSet(context);
+ final boolean firstBackup = preferences.isFirstBackup(0);
final boolean schedule = (autoBackupEnabled && loginInformationSet && !firstBackup);
@@ -90,7 +91,10 @@ protected Preferences getPreferences(Context context) {
return new Preferences(context);
}
- protected AuthPreferences getAuthPreferences(Context context) {
- return new AuthPreferences(context);
+ protected boolean atLeastOneLoginInformationSet(Context context) {
+ for (Integer settingsId = 0; settingsId < App.SimCards.length; settingsId++) {
+ if (new AuthPreferences(context, settingsId).isLoginInformationSet()) return true;
+ }
+ return false;
}
}
diff --git a/app/src/main/java/com/zegoggles/smssync/service/BackupConfig.java b/app/src/main/java/com/zegoggles/smssync/service/BackupConfig.java
index 6e98e83a0..bba632e7a 100644
--- a/app/src/main/java/com/zegoggles/smssync/service/BackupConfig.java
+++ b/app/src/main/java/com/zegoggles/smssync/service/BackupConfig.java
@@ -6,9 +6,10 @@
import com.zegoggles.smssync.mail.DataType;
import java.util.EnumSet;
+import java.util.List;
public class BackupConfig {
- public final BackupImapStore imapStore;
+ public final List imapStores;
public final int currentTry;
public final int maxItemsPerSync;
public final ContactGroup groupToBackup;
@@ -16,18 +17,18 @@ public class BackupConfig {
public final boolean debug;
public final EnumSet typesToBackup;
- BackupConfig(@NonNull BackupImapStore imapStore,
+ BackupConfig(@NonNull List imapStores,
int currentTry,
int maxItemsPerSync,
@NonNull ContactGroup groupToBackup,
@NonNull BackupType backupType,
@NonNull EnumSet typesToBackup,
boolean debug) {
- if (imapStore == null) throw new IllegalArgumentException("need imapstore");
+ if (imapStores == null) throw new IllegalArgumentException("need imapstores");
if (typesToBackup == null || typesToBackup.isEmpty()) throw new IllegalArgumentException("need to specify types to backup");
if (currentTry < 0) throw new IllegalArgumentException("currentTry < 0");
- this.imapStore = imapStore;
+ this.imapStores = imapStores;
this.currentTry = currentTry;
this.maxItemsPerSync = maxItemsPerSync;
this.groupToBackup = groupToBackup;
@@ -36,8 +37,8 @@ public class BackupConfig {
this.typesToBackup = typesToBackup;
}
- public BackupConfig retryWithStore(BackupImapStore store) {
- return new BackupConfig(store, currentTry + 1,
+ public BackupConfig retryWithStore(List stores) {
+ return new BackupConfig(stores, currentTry + 1,
maxItemsPerSync,
groupToBackup,
backupType,
@@ -47,8 +48,8 @@ public BackupConfig retryWithStore(BackupImapStore store) {
@Override public String toString() {
return "BackupConfig{" +
- "imap=" + imapStore +
- ", currentTry=" + currentTry +
+ GetImapString() +
+ "currentTry=" + currentTry +
", maxItemsPerSync=" + maxItemsPerSync +
", groupToBackup=" + groupToBackup +
", backupType=" + backupType +
@@ -56,4 +57,14 @@ public BackupConfig retryWithStore(BackupImapStore store) {
", typesToBackup=" + typesToBackup +
'}';
}
+
+ private String GetImapString() {
+ String imapStoreString = "";
+ Integer cnt = 0;
+ for(BackupImapStore store: imapStores) {
+ imapStoreString += "imap" + cnt.toString() + "=" + store.toString() + ", ";
+ cnt++;
+ }
+ return imapStoreString;
+ }
}
diff --git a/app/src/main/java/com/zegoggles/smssync/service/BackupItemsFetcher.java b/app/src/main/java/com/zegoggles/smssync/service/BackupItemsFetcher.java
index 8c1fc2cf1..7c99cdbe4 100644
--- a/app/src/main/java/com/zegoggles/smssync/service/BackupItemsFetcher.java
+++ b/app/src/main/java/com/zegoggles/smssync/service/BackupItemsFetcher.java
@@ -27,9 +27,9 @@ public class BackupItemsFetcher {
this.resolver = resolver;
}
- public @NonNull Cursor getItemsForDataType(DataType dataType, ContactGroupIds group, int max) {
+ public @NonNull Cursor getItemsForDataType(DataType dataType, ContactGroupIds group, Integer settingsId, int max) {
if (LOCAL_LOGV) Log.v(TAG, "getItemsForDataType(type=" + dataType + ", max=" + max + ")");
- return performQuery(queryBuilder.buildQueryForDataType(dataType, group, max));
+ return performQuery(queryBuilder.buildQueryForDataType(dataType, group, settingsId, max));
}
/**
diff --git a/app/src/main/java/com/zegoggles/smssync/service/BackupQueryBuilder.java b/app/src/main/java/com/zegoggles/smssync/service/BackupQueryBuilder.java
index 88b31fb98..a5e4f8b59 100644
--- a/app/src/main/java/com/zegoggles/smssync/service/BackupQueryBuilder.java
+++ b/app/src/main/java/com/zegoggles/smssync/service/BackupQueryBuilder.java
@@ -11,6 +11,7 @@
import com.zegoggles.smssync.contacts.ContactGroupIds;
import com.zegoggles.smssync.mail.DataType;
import com.zegoggles.smssync.preferences.DataTypePreferences;
+import com.zegoggles.smssync.App;
import java.util.Locale;
import java.util.Set;
@@ -60,15 +61,33 @@ static class Query {
}
}
- public @Nullable Query buildQueryForDataType(DataType type, @Nullable ContactGroupIds groupIds, int max) {
+ public @Nullable Query buildQueryForDataType(DataType type, @Nullable ContactGroupIds groupIds, Integer settingsId, int max) {
switch (type) {
- case SMS: return getQueryForSMS(groupIds, max);
- case MMS: return getQueryForMMS(groupIds, max);
- case CALLLOG: return getQueryForCallLog(max);
+ case SMS: return getQueryForSMS(groupIds, settingsId, max);
+ case MMS: return getQueryForMMS(groupIds, settingsId, max);
+ case CALLLOG: return getQueryForCallLog(settingsId, max);
default: return null;
}
}
+ private String getSimCardNumber(Integer settingsId) {
+ //simCardNumber starts with 1, whereas settingsId is 0-based
+ Integer simCardNumber = settingsId + 1;
+ return simCardNumber.toString();
+ }
+
+ private String getIccId(Integer settingsId) {
+ return App.SimCards[settingsId].IccId;
+ }
+
+ private String singleSIMCondition() {
+ if (App.SimCards.length == 1)
+ {
+ return "1 OR";
+ }
+ return "";
+ }
+
public @Nullable Query buildMostRecentQueryForDataType(DataType type) {
switch (type) {
case MMS:
@@ -97,23 +116,27 @@ static class Query {
}
}
- private Query getQueryForSMS(@Nullable ContactGroupIds groupIds, int max) {
+ private Query getQueryForSMS(@Nullable ContactGroupIds groupIds, Integer settingsId, int max) {
return new Query(Consts.SMS_PROVIDER,
null,
String.format(Locale.ENGLISH,
- "%s > ? AND %s <> ? %s",
+ "%s > ? AND %s <> ? %s AND (%s %s = ?)",
Telephony.TextBasedSmsColumns.DATE,
Telephony.TextBasedSmsColumns.TYPE,
- groupSelection(SMS, groupIds)).trim(),
+ groupSelection(SMS, groupIds),
+ singleSIMCondition(),
+ Telephony.TextBasedSmsColumns.SUBSCRIPTION_ID
+ ).trim(),
new String[] {
- String.valueOf(preferences.getMaxSyncedDate(SMS)),
- String.valueOf(Telephony.TextBasedSmsColumns.MESSAGE_TYPE_DRAFT)
+ String.valueOf(preferences.getMaxSyncedDate(SMS, settingsId)),
+ String.valueOf(Telephony.TextBasedSmsColumns.MESSAGE_TYPE_DRAFT),
+ getSimCardNumber(settingsId)
},
max);
}
- private Query getQueryForMMS(@Nullable ContactGroupIds group, int max) {
- long maxSynced = preferences.getMaxSyncedDate(MMS);
+ private Query getQueryForMMS(@Nullable ContactGroupIds group, Integer settingsId, int max) {
+ long maxSynced = preferences.getMaxSyncedDate(MMS, settingsId);
if (maxSynced > 0) {
// NB: max synced date is stored in seconds since epoch in database
maxSynced = (long) (maxSynced / 1000d);
@@ -121,24 +144,30 @@ private Query getQueryForMMS(@Nullable ContactGroupIds group, int max) {
return new Query(
Consts.MMS_PROVIDER,
null,
- String.format(Locale.ENGLISH, "%s > ? AND %s <> ? %s",
+ String.format(Locale.ENGLISH, "%s > ? AND %s <> ? %s AND (%s %s = ?)",
Telephony.BaseMmsColumns.DATE,
Telephony.BaseMmsColumns.MESSAGE_TYPE,
- groupSelection(DataType.MMS, group)).trim(),
+ groupSelection(DataType.MMS, group),
+ singleSIMCondition(),
+ Telephony.BaseMmsColumns.SUBSCRIPTION_ID
+ ).trim(),
new String[] {
String.valueOf(maxSynced),
- MmsConsts.DELIVERY_REPORT
+ MmsConsts.DELIVERY_REPORT,
+ getSimCardNumber(settingsId)
},
max);
}
- private Query getQueryForCallLog(int max) {
+ private Query getQueryForCallLog(Integer settingsId, int max) {
return new Query(
Consts.CALLLOG_PROVIDER,
CALLLOG_PROJECTION,
- String.format(Locale.ENGLISH, "%s > ?", CallLog.Calls.DATE),
+ String.format(Locale.ENGLISH, "%s > ? AND (%s %s = ? OR %s = ?)", CallLog.Calls.DATE, singleSIMCondition(), CallLog.Calls.PHONE_ACCOUNT_ID, CallLog.Calls.PHONE_ACCOUNT_ID),
new String[] {
- String.valueOf(preferences.getMaxSyncedDate(CALLLOG))
+ String.valueOf(preferences.getMaxSyncedDate(CALLLOG, settingsId)),
+ getSimCardNumber(settingsId),
+ getIccId(settingsId)
},
max);
}
diff --git a/app/src/main/java/com/zegoggles/smssync/service/BackupTask.java b/app/src/main/java/com/zegoggles/smssync/service/BackupTask.java
index 72eb0dd4a..5400bb36e 100644
--- a/app/src/main/java/com/zegoggles/smssync/service/BackupTask.java
+++ b/app/src/main/java/com/zegoggles/smssync/service/BackupTask.java
@@ -14,6 +14,7 @@
import com.zegoggles.smssync.R;
import com.zegoggles.smssync.auth.OAuth2Client;
import com.zegoggles.smssync.auth.TokenRefreshException;
+import com.zegoggles.smssync.service.exception.RequiresLoginException;
import com.zegoggles.smssync.auth.TokenRefresher;
import com.zegoggles.smssync.calendar.CalendarAccessor;
import com.zegoggles.smssync.contacts.ContactAccessor;
@@ -31,6 +32,8 @@
import java.util.List;
import java.util.Locale;
+import java.util.HashMap;
+
import static com.zegoggles.smssync.App.LOCAL_LOGV;
import static com.zegoggles.smssync.App.TAG;
@@ -52,7 +55,7 @@ class BackupTask extends AsyncTask {
private final SmsBackupService service;
private final BackupItemsFetcher fetcher;
private final MessageConverter converter;
- private final CalendarSyncer calendarSyncer;
+ private final HashMap calendarSyncerList = new HashMap();
private final AuthPreferences authPreferences;
private final Preferences preferences;
private final ContactAccessor contactAccessor;
@@ -73,16 +76,15 @@ class BackupTask extends AsyncTask {
this.contactAccessor = new ContactAccessor();
this.converter = new MessageConverter(context, service.getPreferences(), authPreferences.getUserEmail(), personLookup, contactAccessor);
- if (preferences.isCallLogCalendarSyncEnabled()) {
- calendarSyncer = new CalendarSyncer(
- CalendarAccessor.Get.instance(service.getContentResolver()),
- preferences.getCallLogCalendarId(),
- personLookup,
- new CallFormatter(context.getResources())
- );
-
- } else {
- calendarSyncer = null;
+ for (Integer settingsId = 0; settingsId < App.SimCards.length; settingsId++) {
+ if (preferences.isCallLogCalendarSyncEnabled(settingsId)) {
+ calendarSyncerList.put(settingsId, new CalendarSyncer(
+ CalendarAccessor.Get.instance(service.getContentResolver()),
+ preferences.getCallLogCalendarId(settingsId),
+ personLookup,
+ new CallFormatter(context.getResources())
+ ));
+ }
}
this.tokenRefresher = new TokenRefresher(service, new OAuth2Client(authPreferences.getOAuth2ClientId()), authPreferences);
}
@@ -98,7 +100,7 @@ class BackupTask extends AsyncTask {
this.service = service;
this.fetcher = fetcher;
this.converter = messageConverter;
- this.calendarSyncer = syncer;
+ this.calendarSyncerList.put(0, syncer);
this.authPreferences = authPreferences;
this.preferences = preferences;
this.contactAccessor = accessor;
@@ -139,48 +141,80 @@ private BackupState acquireLocksAndBackup(BackupConfig config) {
}
private BackupState fetchAndBackupItems(BackupConfig config) {
- BackupCursors cursors = null;
+ HashMap cursorList = new HashMap();
+ Exception lastException = null;
try {
final ContactGroupIds groupIds = contactAccessor.getGroupContactIds(service.getContentResolver(), config.groupToBackup);
+ final Context context = service.getApplicationContext();
- cursors = new BulkFetcher(fetcher).fetch(config.typesToBackup, groupIds, config.maxItemsPerSync);
- final int itemsToSync = cursors.count();
-
- if (itemsToSync > 0) {
- appLog(R.string.app_log_backup_messages, cursors.count(SMS), cursors.count(MMS), cursors.count(CALLLOG));
- if (config.debug) {
- appLog(R.string.app_log_backup_messages_with_config, config);
+ int itemsToSync = 0;
+ for (Integer settingsId = 0; settingsId < App.SimCards.length; settingsId++) {
+ if (!(new AuthPreferences(context, settingsId).isLoginInformationSet())) {
+ lastException = new RequiresLoginException();
+ continue;
}
- return backupCursors(cursors, config.imapStore, config.backupType, itemsToSync);
- } else {
- appLog(R.string.app_log_skip_backup_no_items);
+ BackupCursors cursors = new BulkFetcher(fetcher).fetch(config.typesToBackup, groupIds, settingsId, config.maxItemsPerSync);
+ itemsToSync += cursors.count();
+ if (cursors.count() > 0) {
+ appLog(R.string.app_log_backup_messages, cursors.count(SMS), cursors.count(MMS), cursors.count(CALLLOG), App.SimCards[settingsId]);
+ if (config.debug) {
+ appLog(R.string.app_log_backup_messages_with_config, config);
+ }
+ cursorList.put(settingsId, cursors);
+ }
+ }
- if (preferences.isFirstBackup()) {
- // If this is the first backup we need to write something to MAX_SYNCED_DATE
- // such that we know that we've performed a backup before.
- preferences.getDataTypePreferences().setMaxSyncedDate(SMS, MAX_SYNCED_DATE);
- preferences.getDataTypePreferences().setMaxSyncedDate(MMS, MAX_SYNCED_DATE);
+ int[] result = new int[2];
+ result[0] = itemsToSync;
+ result[1] = 0;
+ for (int settingsId : cursorList.keySet()) {
+ BackupCursors cursors = cursorList.get(settingsId);
+ try {
+ if (cursors.count() > 0) {
+ result = backupCursors(settingsId, cursors, config.imapStores.get(settingsId), config.backupType, result[0], result[1]);
+ } else {
+ appLog(R.string.app_log_skip_backup_no_items);
+
+ if (preferences.isFirstBackup(settingsId)) {
+ // If this is the first backup we need to write something to MAX_SYNCED_DATE
+ // such that we know that we've performed a backup before.
+ preferences.getDataTypePreferences().setMaxSyncedDate(SMS, MAX_SYNCED_DATE, settingsId);
+ preferences.getDataTypePreferences().setMaxSyncedDate(MMS, MAX_SYNCED_DATE, settingsId);
+ }
+ }
+ } catch (XOAuth2AuthenticationFailedException e) {
+ try {
+ return handleAuthError(config, e);
+ } catch (XOAuth2AuthenticationFailedException eInner) {
+ lastException = eInner;
+ }
+ } catch (AuthenticationFailedException e) {
+ lastException = e;
+ } catch (MessagingException e) {
+ lastException = e;
+ } catch (SecurityException e) {
+ lastException = e;
}
+ }
+ if (lastException != null) return transition(ERROR, lastException);
+
+ if (itemsToSync > 0) {
+ return new BackupState(FINISHED_BACKUP, result[1], result[0], config.backupType, null, null);
+ } else {
Log.i(TAG, "Nothing to do.");
return transition(FINISHED_BACKUP, null);
}
- } catch (XOAuth2AuthenticationFailedException e) {
- return handleAuthError(config, e);
- } catch (AuthenticationFailedException e) {
- return transition(ERROR, e);
- } catch (MessagingException e) {
- return transition(ERROR, e);
- } catch (SecurityException e) {
- return transition(ERROR, e);
} finally {
- if (cursors != null) {
- cursors.close();
+ if (cursorList != null) {
+ for (BackupCursors cursors : cursorList.values()) {
+ cursors.close();
+ }
}
}
}
- private BackupState handleAuthError(BackupConfig config, XOAuth2AuthenticationFailedException e) {
+ private BackupState handleAuthError(BackupConfig config, XOAuth2AuthenticationFailedException e) throws XOAuth2AuthenticationFailedException {
if (e.getStatus() == 400) {
appLogDebug("need to perform xoauth2 token refresh");
if (config.currentTry < 1) {
@@ -189,7 +223,7 @@ private BackupState handleAuthError(BackupConfig config, XOAuth2AuthenticationFa
// we got a new token, let's handleAuthError one more time - we need to pass in a new store object
// since the auth params on it are immutable
appLogDebug("token refreshed, retrying");
- return fetchAndBackupItems(config.retryWithStore(service.getBackupImapStore()));
+ return fetchAndBackupItems(config.retryWithStore(service.getBackupImapStores()));
} catch (MessagingException ignored) {
Log.w(TAG, ignored);
} catch (TokenRefreshException refreshException) {
@@ -201,14 +235,16 @@ private BackupState handleAuthError(BackupConfig config, XOAuth2AuthenticationFa
} else {
appLogDebug("unexpected xoauth status code " + e.getStatus());
}
- return transition(ERROR, e);
+ throw e;
}
private BackupState skip(Iterable types) {
appLog(R.string.app_log_skip_backup_skip_messages);
for (DataType type : types) {
try {
- preferences.getDataTypePreferences().setMaxSyncedDate(type, fetcher.getMostRecentTimestamp(type));
+ for (Integer settingsId = 0; settingsId < App.SimCards.length; settingsId++) {
+ preferences.getDataTypePreferences().setMaxSyncedDate(type, fetcher.getMostRecentTimestamp(type), settingsId);
+ }
} catch (SecurityException e ) {
return new BackupState(ERROR, 0, 0, MANUAL, type, e);
}
@@ -255,7 +291,7 @@ private void post(BackupState state) {
App.post(state);
}
- private BackupState backupCursors(BackupCursors cursors, BackupImapStore store, BackupType backupType, int itemsToSync)
+ private int[] backupCursors(Integer settingsId, BackupCursors cursors, BackupImapStore store, BackupType backupType, int itemsToSync, int backedUpItems)
throws MessagingException {
Log.i(TAG, String.format(Locale.ENGLISH, "Starting backup (%d messages)", itemsToSync));
publish(LOGIN);
@@ -263,12 +299,11 @@ private BackupState backupCursors(BackupCursors cursors, BackupImapStore store,
try {
publish(CALC);
- int backedUpItems = 0;
while (!isCancelled() && cursors.hasNext()) {
BackupCursors.CursorAndType cursor = cursors.next();
if (LOCAL_LOGV) Log.v(TAG, "backing up: " + cursor);
- ConversionResult result = converter.convertMessages(cursor.cursor, cursor.type);
+ ConversionResult result = converter.convertMessages(cursor.cursor, cursor.type, settingsId);
if (!result.isEmpty()) {
List messages = result.getMessages();
@@ -277,12 +312,13 @@ private BackupState backupCursors(BackupCursors cursors, BackupImapStore store,
messages.size(), cursor.type));
}
- store.getFolder(cursor.type, preferences.getDataTypePreferences()).appendMessages(messages);
+ store.getFolder(cursor.type, preferences.getDataTypePreferences(), settingsId).appendMessages(messages);
- if (cursor.type == CALLLOG && calendarSyncer != null) {
- calendarSyncer.syncCalendar(result);
+ if (cursor.type == CALLLOG && calendarSyncerList.containsKey(settingsId)) {
+ calendarSyncerList.get(settingsId).syncCalendar(result);
}
- preferences.getDataTypePreferences().setMaxSyncedDate(cursor.type, result.getMaxDate());
+
+ preferences.getDataTypePreferences().setMaxSyncedDate(cursor.type, result.getMaxDate(), settingsId);
backedUpItems += messages.size();
} else {
Log.w(TAG, "no messages converted");
@@ -292,10 +328,11 @@ private BackupState backupCursors(BackupCursors cursors, BackupImapStore store,
publishProgress(new BackupState(BACKUP, backedUpItems, itemsToSync, backupType, cursor.type, null));
}
- return new BackupState(FINISHED_BACKUP,
- backedUpItems,
- itemsToSync,
- backupType, null, null);
+ int[] retVal = new int[2];
+
+ retVal[0] = itemsToSync;
+ retVal[1] = backedUpItems;
+ return retVal;
} finally {
store.closeFolders();
}
diff --git a/app/src/main/java/com/zegoggles/smssync/service/BulkFetcher.java b/app/src/main/java/com/zegoggles/smssync/service/BulkFetcher.java
index 4ab8cc23f..3d2e22f79 100644
--- a/app/src/main/java/com/zegoggles/smssync/service/BulkFetcher.java
+++ b/app/src/main/java/com/zegoggles/smssync/service/BulkFetcher.java
@@ -18,12 +18,13 @@ public BulkFetcher(BackupItemsFetcher itemsFetcher) {
public @NonNull BackupCursors fetch(final @NonNull EnumSet types,
final @Nullable ContactGroupIds groups,
+ final Integer settingsId,
final int maxItems) {
int max = maxItems;
BackupCursors cursors = new BackupCursors();
for (DataType type : types) {
- Cursor cursor = itemsFetcher.getItemsForDataType(type, groups, max);
+ Cursor cursor = itemsFetcher.getItemsForDataType(type, groups, settingsId, max);
cursors.add(type, cursor);
if (max > 0) {
diff --git a/app/src/main/java/com/zegoggles/smssync/service/RestoreConfig.java b/app/src/main/java/com/zegoggles/smssync/service/RestoreConfig.java
index 8834741cb..79b4edeb9 100644
--- a/app/src/main/java/com/zegoggles/smssync/service/RestoreConfig.java
+++ b/app/src/main/java/com/zegoggles/smssync/service/RestoreConfig.java
@@ -1,6 +1,7 @@
package com.zegoggles.smssync.service;
import com.zegoggles.smssync.mail.BackupImapStore;
+import java.util.List;
public class RestoreConfig {
final int tries;
@@ -9,9 +10,9 @@ public class RestoreConfig {
final boolean restoreOnlyStarred;
final int maxRestore;
final int currentRestoredItem;
- final BackupImapStore imapStore;
+ final List imapStores;
- public RestoreConfig(BackupImapStore imapStore,
+ public RestoreConfig(List imapStores,
int tries,
boolean restoreSms,
boolean restoreCallLog,
@@ -20,7 +21,7 @@ public RestoreConfig(BackupImapStore imapStore,
int currentRestoredItem) {
this.tries = tries;
- this.imapStore = imapStore;
+ this.imapStores = imapStores;
this.restoreSms = restoreSms;
this.restoreCallLog = restoreCallLog;
this.restoreOnlyStarred = restoreOnlyStarred;
@@ -28,9 +29,9 @@ public RestoreConfig(BackupImapStore imapStore,
this.currentRestoredItem = currentRestoredItem;
}
- public RestoreConfig retryWithStore(int currentItem, BackupImapStore backupImapStore) {
+ public RestoreConfig retryWithStore(int currentItem, List backupImapStores) {
return new RestoreConfig(
- backupImapStore,
+ backupImapStores,
tries + 1,
restoreSms,
restoreCallLog,
@@ -48,7 +49,17 @@ public RestoreConfig retryWithStore(int currentItem, BackupImapStore backupImapS
", restoreOnlyStarred=" + restoreOnlyStarred +
", maxRestore=" + maxRestore +
", currentRestoredItem=" + currentRestoredItem +
- ", imapStore=" + imapStore +
+ GetImapString() +
'}';
}
+
+ private String GetImapString() {
+ String imapStoreString = "";
+ Integer cnt = 0;
+ for(BackupImapStore store: imapStores) {
+ imapStoreString += ", imapStore" + cnt.toString() + "=" + store.toString();
+ cnt++;
+ }
+ return imapStoreString;
+ }
}
diff --git a/app/src/main/java/com/zegoggles/smssync/service/RestoreTask.java b/app/src/main/java/com/zegoggles/smssync/service/RestoreTask.java
index 37dc492c2..810b8133c 100644
--- a/app/src/main/java/com/zegoggles/smssync/service/RestoreTask.java
+++ b/app/src/main/java/com/zegoggles/smssync/service/RestoreTask.java
@@ -20,17 +20,20 @@
import com.zegoggles.smssync.Consts;
import com.zegoggles.smssync.auth.TokenRefreshException;
import com.zegoggles.smssync.auth.TokenRefresher;
+import com.zegoggles.smssync.auth.OAuth2Client;
import com.zegoggles.smssync.mail.BackupImapStore;
import com.zegoggles.smssync.mail.DataType;
import com.zegoggles.smssync.mail.MessageConverter;
import com.zegoggles.smssync.preferences.Preferences;
import com.zegoggles.smssync.service.state.RestoreState;
import com.zegoggles.smssync.service.state.SmsSyncState;
+import com.zegoggles.smssync.preferences.AuthPreferences;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
+import java.util.HashMap;
import java.util.List;
import java.util.Set;
@@ -57,16 +60,18 @@ class RestoreTask extends AsyncTask {
private final MessageConverter converter;
private final TokenRefresher tokenRefresher;
private final Preferences preferences;
+ private final AuthPreferences authPreferences;
RestoreTask(SmsRestoreService service,
MessageConverter converter,
- ContentResolver resolver,
- TokenRefresher tokenRefresher) {
+ ContentResolver resolver) {
this.service = service;
+ this.authPreferences = service.getAuthPreferences();
this.converter = converter;
this.resolver = resolver;
- this.tokenRefresher = tokenRefresher;
this.preferences = service.getPreferences();
+
+ this.tokenRefresher = new TokenRefresher(service, new OAuth2Client(authPreferences.getOAuth2ClientId()), authPreferences);
}
@Override
@@ -95,66 +100,93 @@ protected void onPreExecute() {
}
private RestoreState restore(RestoreConfig config) {
- final BackupImapStore imapStore = config.imapStore;
-
+ int itemsToRestoreCount = 0;
+ Exception lastException = null;
+ HashMap> msgList = new HashMap>();
int currentRestoredItem = config.currentRestoredItem;
- try {
- publishProgress(LOGIN);
- imapStore.checkSettings();
- publishProgress(CALC);
+ for (Integer settingsId = 0; settingsId < App.SimCards.length; settingsId++) {
+ final BackupImapStore imapStore = config.imapStores.get(settingsId);
- final List msgs = new ArrayList();
-
- if (config.restoreSms) {
- msgs.addAll(imapStore.getFolder(SMS, preferences.getDataTypePreferences()).getMessages(config.maxRestore, config.restoreOnlyStarred, null));
- }
- if (config.restoreCallLog) {
- msgs.addAll(imapStore.getFolder(CALLLOG, preferences.getDataTypePreferences()).getMessages(config.maxRestore, config.restoreOnlyStarred, null));
- }
+ try {
+ publishProgress(LOGIN);
+ imapStore.checkSettings();
- final int itemsToRestoreCount = config.maxRestore <= 0 ? msgs.size() : Math.min(msgs.size(), config.maxRestore);
+ publishProgress(CALC);
- if (itemsToRestoreCount > 0) {
- for (; currentRestoredItem < itemsToRestoreCount && !isCancelled(); currentRestoredItem++) {
- DataType dataType = importMessage(msgs.get(currentRestoredItem));
+ if (config.restoreSms) {
+ List msgs = new ArrayList();
+ msgs.addAll(imapStore.getFolder(SMS, preferences.getDataTypePreferences(), settingsId).getMessages(config.maxRestore, config.restoreOnlyStarred, null));
+ msgList.put(settingsId, msgs);
+ }
+ if (config.restoreCallLog) {
+ List msgs = new ArrayList();
+ msgs.addAll(imapStore.getFolder(CALLLOG, preferences.getDataTypePreferences(), settingsId).getMessages(config.maxRestore, config.restoreOnlyStarred, null));
+ msgList.put(settingsId, msgs);
+ }
- msgs.set(currentRestoredItem, null); // help gc
- publishProgress(new RestoreState(RESTORE, currentRestoredItem, itemsToRestoreCount, 0, 0, dataType, null));
- if (currentRestoredItem % 50 == 0) {
- //clear cache periodically otherwise SD card fills up
- service.clearCache();
- }
+ itemsToRestoreCount += config.maxRestore <= 0 ? msgList.get(settingsId).size() : Math.min(msgList.get(settingsId).size(), config.maxRestore);
+ } catch (XOAuth2AuthenticationFailedException e) {
+ try {
+ return handleAuthError(config, currentRestoredItem, e);
+ } catch (XOAuth2AuthenticationFailedException eInner) {
+ lastException = eInner;
}
+ } catch (AuthenticationFailedException e) {
+ lastException = e;
+ } catch (MessagingException e) {
+ Log.e(TAG, ERROR, e);
updateAllThreadsIfAnySmsRestored();
- } else {
- Log.d(TAG, "nothing to restore");
+ lastException = e;
+ } catch (IllegalStateException e) {
+ // usually memory problems (Couldn't init cursor window)
+ lastException = e;
+ } finally {
+ imapStore.closeFolders();
}
+ }
+
+ int restoredCount = 0;
+ for (int settingsId : msgList.keySet()) {
+ List msgs = msgList.get(settingsId);
+ try {
+ if (msgs.size() > 0) {
+ for (; currentRestoredItem < msgs.size() && !isCancelled(); currentRestoredItem++) {
+ DataType dataType = importMessage(settingsId, msgs.get(currentRestoredItem));
+
+ msgs.set(currentRestoredItem, null); // help gc
+ publishProgress(new RestoreState(RESTORE, restoredCount + currentRestoredItem, itemsToRestoreCount, 0, 0, dataType, null));
+ if (currentRestoredItem % 50 == 0) {
+ //clear cache periodically otherwise SD card fills up
+ service.clearCache();
+ }
+ }
+ updateAllThreadsIfAnySmsRestored();
+
+ restoredCount += smsIds.size() + callLogIds.size();
+ }
+ } catch (IllegalStateException e) {
+ // usually memory problems (Couldn't init cursor window)
+ lastException = e;
+ }
+ }
+
+ if (lastException != null) return transition(SmsSyncState.ERROR, lastException);
- final int restoredCount = smsIds.size() + callLogIds.size();
+ if (itemsToRestoreCount > 0) {
return new RestoreState(isCancelled() ? CANCELED_RESTORE : FINISHED_RESTORE,
- currentRestoredItem,
+ restoredCount,
itemsToRestoreCount,
restoredCount,
Math.max(0, uids.size() - restoredCount),
null, null);
- } catch (XOAuth2AuthenticationFailedException e) {
- return handleAuthError(config, currentRestoredItem, e);
- } catch (AuthenticationFailedException e) {
- return transition(SmsSyncState.ERROR, e);
- } catch (MessagingException e) {
- Log.e(TAG, ERROR, e);
- updateAllThreadsIfAnySmsRestored();
- return transition(SmsSyncState.ERROR, e);
- } catch (IllegalStateException e) {
- // usually memory problems (Couldn't init cursor window)
- return transition(SmsSyncState.ERROR, e);
- } finally {
- imapStore.closeFolders();
+ } else {
+ Log.d(TAG, "nothing to restore");
+ return new RestoreState(FINISHED_RESTORE, 0, 0, 0, 0, null, null);
}
}
- private RestoreState handleAuthError(RestoreConfig config, int currentRestoredItem, XOAuth2AuthenticationFailedException e) {
+ private RestoreState handleAuthError(RestoreConfig config, int currentRestoredItem, XOAuth2AuthenticationFailedException e) throws XOAuth2AuthenticationFailedException {
if (e.getStatus() == 400) {
Log.d(TAG, "need to perform xoauth2 token refresh");
if (config.tries < 1) {
@@ -162,7 +194,7 @@ private RestoreState handleAuthError(RestoreConfig config, int currentRestoredIt
tokenRefresher.refreshOAuth2Token();
// we got a new token, let's retry one more time - we need to pass in a new store object
// since the auth params on it are immutable
- return restore(config.retryWithStore(currentRestoredItem, service.getBackupImapStore()));
+ return restore(config.retryWithStore(currentRestoredItem, service.getBackupImapStores()));
} catch (MessagingException ignored) {
Log.w(TAG, ignored);
} catch (TokenRefreshException refreshException) {
@@ -174,7 +206,7 @@ private RestoreState handleAuthError(RestoreConfig config, int currentRestoredIt
} else {
Log.w(TAG, "unexpected xoauth status code " + e.getStatus());
}
- return transition(SmsSyncState.ERROR, e);
+ throw e;
}
private void publishProgress(SmsSyncState smsSyncState) {
@@ -214,7 +246,7 @@ private void post(RestoreState changed) {
}
@SuppressWarnings("unchecked")
- private DataType importMessage(Message message) {
+ private DataType importMessage(Integer settingsId, Message message) {
uids.add(message.getUid());
FetchProfile fp = new FetchProfile();
@@ -227,10 +259,10 @@ private DataType importMessage(Message message) {
//only restore sms+call log for now
switch (dataType) {
case CALLLOG:
- importCallLog(message);
+ importCallLog(settingsId, message);
break;
case SMS:
- importSms(message);
+ importSms(settingsId, message);
break;
default:
if (LOCAL_LOGV) Log.d(TAG, "ignoring restore of type: " + dataType);
@@ -247,9 +279,9 @@ private DataType importMessage(Message message) {
return dataType;
}
- private void importSms(final Message message) throws IOException, MessagingException {
+ private void importSms(Integer settingsId, final Message message) throws IOException, MessagingException {
if (LOCAL_LOGV) Log.v(TAG, "importSms(" + message + ")");
- final ContentValues values = converter.messageToContentValues(message);
+ final ContentValues values = converter.messageToContentValues(settingsId, message);
final Integer type = values.getAsInteger(Telephony.TextBasedSmsColumns.TYPE);
// only restore inbox messages and sent messages - otherwise sms might get sent on restore
@@ -263,8 +295,8 @@ private void importSms(final Message message) throws IOException, MessagingExcep
smsIds.add(uri.getLastPathSegment());
Long timestamp = values.getAsLong(Telephony.TextBasedSmsColumns.DATE);
- if (timestamp != null && preferences.getDataTypePreferences().getMaxSyncedDate(SMS) < timestamp) {
- preferences.getDataTypePreferences().setMaxSyncedDate(SMS, timestamp);
+ if (timestamp != null && preferences.getDataTypePreferences().getMaxSyncedDate(SMS, settingsId) < timestamp) {
+ preferences.getDataTypePreferences().setMaxSyncedDate(SMS, timestamp, settingsId);
}
if (LOCAL_LOGV) Log.v(TAG, "inserted " + uri);
@@ -274,9 +306,9 @@ private void importSms(final Message message) throws IOException, MessagingExcep
}
}
- private void importCallLog(final Message message) throws MessagingException, IOException {
+ private void importCallLog(Integer settingsId, final Message message) throws MessagingException, IOException {
if (LOCAL_LOGV) Log.v(TAG, "importCallLog(" + message + ")");
- final ContentValues values = converter.messageToContentValues(message);
+ final ContentValues values = converter.messageToContentValues(settingsId, message);
if (!callLogExists(values)) {
final Uri uri = resolver.insert(Consts.CALLLOG_PROVIDER, values);
if (uri != null) callLogIds.add(uri.getLastPathSegment());
diff --git a/app/src/main/java/com/zegoggles/smssync/service/ServiceBase.java b/app/src/main/java/com/zegoggles/smssync/service/ServiceBase.java
index 2ea3da9d8..8cf8f4a6e 100644
--- a/app/src/main/java/com/zegoggles/smssync/service/ServiceBase.java
+++ b/app/src/main/java/com/zegoggles/smssync/service/ServiceBase.java
@@ -49,6 +49,8 @@
import static com.zegoggles.smssync.App.LOCAL_LOGV;
import static com.zegoggles.smssync.App.TAG;
import static java.util.Locale.ENGLISH;
+import java.util.ArrayList;
+import java.util.List;
public abstract class ServiceBase extends Service {
@Nullable private PowerManager.WakeLock wakeLock;
@@ -96,16 +98,25 @@ public boolean isWorking() {
return getState().isRunning();
}
- protected BackupImapStore getBackupImapStore() throws MessagingException {
- final String uri = getAuthPreferences().getStoreUri();
- if (!BackupImapStore.isValidUri(uri)) {
- throw new MessagingException("No valid IMAP URI: "+uri);
+ protected List getBackupImapStores() throws MessagingException {
+ List backupImapStores = new ArrayList();
+
+ for (Integer settingsId = 0; settingsId < App.SimCards.length; settingsId++) {
+ final String uri = getAuthPreferences(settingsId).getStoreUri();
+ if (!BackupImapStore.isValidUri(uri)) {
+ throw new MessagingException("No valid IMAP URI: "+uri);
+ }
+ backupImapStores.add(new BackupImapStore(getApplicationContext(), uri, getAuthPreferences(settingsId).isTrustAllCertificates()));
}
- return new BackupImapStore(getApplicationContext(), uri, getAuthPreferences().isTrustAllCertificates());
+ return backupImapStores;
}
protected AuthPreferences getAuthPreferences() {
- return new AuthPreferences(this);
+ return getAuthPreferences(0);
+ }
+
+ protected AuthPreferences getAuthPreferences(Integer settingsId) {
+ return new AuthPreferences(this, settingsId);
}
protected Preferences getPreferences() {
diff --git a/app/src/main/java/com/zegoggles/smssync/service/SmsBackupService.java b/app/src/main/java/com/zegoggles/smssync/service/SmsBackupService.java
index dc9a6b1e9..8381a002a 100644
--- a/app/src/main/java/com/zegoggles/smssync/service/SmsBackupService.java
+++ b/app/src/main/java/com/zegoggles/smssync/service/SmsBackupService.java
@@ -37,7 +37,6 @@
import com.zegoggles.smssync.service.exception.ConnectivityException;
import com.zegoggles.smssync.service.exception.MissingPermissionException;
import com.zegoggles.smssync.service.exception.NoConnectionException;
-import com.zegoggles.smssync.service.exception.RequiresLoginException;
import com.zegoggles.smssync.service.exception.RequiresWifiException;
import com.zegoggles.smssync.service.state.BackupState;
import com.zegoggles.smssync.service.state.SmsSyncState;
@@ -46,6 +45,7 @@
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
+import java.util.List;
import static android.R.drawable.stat_sys_warning;
import static com.zegoggles.smssync.App.CHANNEL_ID;
@@ -114,21 +114,17 @@ private void backup(BackupType backupType) {
EnumSet enabledTypes = getEnabledBackupTypes();
checkPermissions(enabledTypes);
if (backupType != SKIP) {
- checkCredentials();
if (getPreferences().isUseOldScheduler()) {
legacyCheckConnectivity();
}
}
appLog(R.string.app_log_start_backup, backupType);
- getBackupTask().execute(getBackupConfig(backupType, enabledTypes, getBackupImapStore()));
+ getBackupTask().execute(getBackupConfig(backupType, enabledTypes, getBackupImapStores()));
} catch (MessagingException e) {
Log.w(TAG, e);
moveToState(state.transition(ERROR, e));
} catch (ConnectivityException e) {
moveToState(state.transition(ERROR, e));
- } catch (RequiresLoginException e) {
- appLog(R.string.app_log_missing_credentials);
- moveToState(state.transition(ERROR, e));
} catch (BackupDisabledException e) {
moveToState(state.transition(FINISHED_BACKUP, e));
} catch (MissingPermissionException e) {
@@ -148,9 +144,9 @@ private void checkPermissions(EnumSet enabledTypes) throws MissingPerm
private BackupConfig getBackupConfig(BackupType backupType,
EnumSet enabledTypes,
- BackupImapStore imapStore) {
+ List imapStores) {
return new BackupConfig(
- imapStore,
+ imapStores,
0,
getPreferences().getMaxItemsPerSync(),
getPreferences().getBackupContactGroup(),
@@ -168,12 +164,6 @@ private EnumSet getEnabledBackupTypes() throws BackupDisabledException
return dataTypes;
}
- private void checkCredentials() throws RequiresLoginException {
- if (!getAuthPreferences().isLoginInformationSet()) {
- throw new RequiresLoginException();
- }
- }
-
@SuppressWarnings("deprecation")
private void legacyCheckConnectivity() throws ConnectivityException {
android.net.NetworkInfo active = getConnectivityManager().getActiveNetworkInfo();
diff --git a/app/src/main/java/com/zegoggles/smssync/service/SmsRestoreService.java b/app/src/main/java/com/zegoggles/smssync/service/SmsRestoreService.java
index f90130c90..179d91576 100644
--- a/app/src/main/java/com/zegoggles/smssync/service/SmsRestoreService.java
+++ b/app/src/main/java/com/zegoggles/smssync/service/SmsRestoreService.java
@@ -12,12 +12,10 @@
import com.squareup.otto.Subscribe;
import com.zegoggles.smssync.App;
import com.zegoggles.smssync.R;
-import com.zegoggles.smssync.auth.OAuth2Client;
import com.zegoggles.smssync.auth.TokenRefresher;
import com.zegoggles.smssync.contacts.ContactAccessor;
import com.zegoggles.smssync.mail.MessageConverter;
import com.zegoggles.smssync.mail.PersonLookup;
-import com.zegoggles.smssync.preferences.AuthPreferences;
import com.zegoggles.smssync.service.exception.SmsProviderNotWritableException;
import com.zegoggles.smssync.service.state.RestoreState;
@@ -89,7 +87,7 @@ protected void handleIntent(final Intent intent) {
);
RestoreConfig config = new RestoreConfig(
- getBackupImapStore(),
+ getBackupImapStores(),
0,
restoreSms,
restoreCallLog,
@@ -98,9 +96,7 @@ protected void handleIntent(final Intent intent) {
0
);
- final AuthPreferences authPreferences = new AuthPreferences(this);
- new RestoreTask(this, converter, getContentResolver(),
- new TokenRefresher(service, new OAuth2Client(authPreferences.getOAuth2ClientId()), authPreferences)).execute(config);
+ new RestoreTask(this, converter, getContentResolver()).execute(config);
} catch (MessagingException e) {
postError(e);
diff --git a/app/src/main/java/com/zegoggles/smssync/utils/SimCard.java b/app/src/main/java/com/zegoggles/smssync/utils/SimCard.java
new file mode 100644
index 000000000..7b3bcb616
--- /dev/null
+++ b/app/src/main/java/com/zegoggles/smssync/utils/SimCard.java
@@ -0,0 +1,12 @@
+package com.zegoggles.smssync.utils;
+
+public class SimCard
+{
+ public SimCard(String phoneNumber, String iccId) {
+ PhoneNumber = phoneNumber;
+ IccId = iccId;
+ }
+
+ public String PhoneNumber;
+ public String IccId;
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/zegoggles/smssync/utils/SimCardHelper.java b/app/src/main/java/com/zegoggles/smssync/utils/SimCardHelper.java
new file mode 100644
index 000000000..a2d585199
--- /dev/null
+++ b/app/src/main/java/com/zegoggles/smssync/utils/SimCardHelper.java
@@ -0,0 +1,87 @@
+package com.zegoggles.smssync.utils;
+
+import android.content.Context;
+import android.os.Build;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import androidx.annotation.RequiresApi;
+
+import com.zegoggles.smssync.App;
+
+public class SimCardHelper {
+ public static SimCard[] getSimCards(Context context) {
+ if (Build.VERSION.SDK_INT >= 29) return getSimCardsInternal(context); else {
+ SimCard[] simCards = new SimCard[1];
+ simCards[0] = new SimCard("1", "1");
+ return simCards;
+ }
+ }
+
+ @RequiresApi(29)
+ private static SimCard[] getSimCardsInternal(Context context) {
+ int simMaxCount = 1;
+ try {
+ SubscriptionManager subscriptionManager = (SubscriptionManager) context.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE
+ );
+ simMaxCount = subscriptionManager.getActiveSubscriptionInfoCountMax();
+ } catch (Exception e) {
+ simMaxCount = 1;
+ }
+
+ SimCard[] simCards = new SimCard[simMaxCount];
+
+ try {
+ SubscriptionManager subscriptionManager = (SubscriptionManager) context.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE
+ );
+
+ int simCount = 0;
+ for (SubscriptionInfo subscriptionInfo : subscriptionManager.getActiveSubscriptionInfoList()) {
+ String phoneNumber = transformPhoneNumber(subscriptionInfo.getNumber());
+ String iccId = subscriptionInfo.getIccId();
+ simCards[simCount] = new SimCard(phoneNumber, iccId);
+ simCount++;
+ }
+
+ simCards = fillWithFallBack(simCards, simMaxCount);
+ } catch (Exception e) {
+ simCards = fillWithFallBack(simCards, simMaxCount);
+ if (simCards.length == 0) {
+ simCards[0] = new SimCard("1", "1");
+ }
+ }
+
+ return simCards;
+ }
+
+ private static String transformPhoneNumber(String number) {
+ return number.replaceAll("[^\\d]", "");
+ }
+
+ private static SimCard[] fillWithFallBack(SimCard[] simCards, int simMaxCount) {
+ int simCount = 0;
+ for (int i = 0; i < simMaxCount; i++) {
+ if (simCards[simCount] == null) {
+ String cnt = String.valueOf(i + 1);
+ simCards[simCount] = new SimCard(cnt, cnt);
+ }
+ simCount++;
+ }
+ return simCards;
+ }
+
+ public static String addSettingsId(String stringWithoutSettingsId, Integer settingsId) {
+ if (settingsId == 0) {
+ return stringWithoutSettingsId;
+ } else {
+ return stringWithoutSettingsId + "_" + settingsId;
+ }
+ }
+
+ public static String addPhoneNumberIfMultiSim(String plain, Integer settingsId) {
+ SimCard[] simCards = App.SimCards;
+ if (simCards.length < 2) return plain;
+ return plain + " ("+simCards[settingsId].PhoneNumber+")";
+ }
+}
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 14b370cf3..d1c5067c1 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -114,6 +114,9 @@
Gelesen markieren (SMS)
Ob alle wiederhergestellten SMS als gelesen markiert werden sollen oder nicht.
+ IccId für MultiSim verwenden
+ Ob IccId für das Anrufprotokoll verwendet werden soll, um zwischen Sim-Karten zu unterscheiden (geräteabhängig und nur verwendet auf MultiSim-Geräten)
+
E-Mail Adress-Stil
Format der E-Mail-Adressen
@@ -252,7 +255,7 @@
Übersprungen (Anmeldedaten fehlen)
Sicherung angefordert (%1$s)
Starte Sicherung (%1$s)
- Sichere (%1$d SMS, %2$d MMS, %3$d call log)
+ Sichere (%1$d SMS, %2$d MMS, %3$d call log) von Telefonnummer %4$s
Sicherung abgebrochen
Sicherung fertiggestellt
@@ -317,6 +320,8 @@
Allen Zertifikaten trauen
Benuzte Konfiguration: %1$s
+ Einstellungen von SIM 1
+ Einstellungen von SIM 1 übernehmen
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 714b69c4d..651f7cf6c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -138,6 +138,9 @@
Mark as read (SMS)
Whether to mark all restored SMS as read or not.
+ Use IccId for multisim
+ Whether to use IccId for Calllog to differentiate between multiple sim-cards (device dependant and only used on mulitsim devices)
+
Email address style
Format of email addresses
@@ -279,7 +282,7 @@
Skipped (missing credentials)
Backup requested (%1$s)
Starting backup (%1$s)
- Backing up (%1$d SMS, %2$d MMS, %3$d call log)
+ Backing up (%1$d SMS, %2$d MMS, %3$d call log) from number %4$s
Using config: %1$s
Backup canceled
Backup finished
@@ -349,4 +352,6 @@
Dark theme
Legacy settings
XOAuth2 is no longer supported.
+ Settings from SIM 1
+ Takeover settings from SIM 1
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 6232305e4..829f66e7c 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -258,6 +258,12 @@
android:summary="@string/ui_mark_as_read_restore_desc"
android:defaultValue="true"/>
+
+
map = new HashMap();
- Message msg = generator.messageForDataType(map, DataType.SMS);
+ Message msg = generator.messageForDataType(map, DataType.SMS, 0);
assertThat(msg).isNull();
}
@Test public void testShouldGenerateSubjectWithNameForSMS() throws Exception {
PersonRecord record = new PersonRecord(1, "Test Testor", null, null);
- Message msg = generator.messageForDataType(mockMessage("1234", record), DataType.SMS);
+ Message msg = generator.messageForDataType(mockMessage("1234", record), DataType.SMS, 0);
assertThat(msg).isNotNull();
assertThat(msg.getSubject()).isEqualTo("SMS with Test Testor");
}
@Test public void testShouldGenerateSMSMessageWithCorrectEncoding() throws Exception {
PersonRecord record = new PersonRecord(1, "Test Testor", null, null);
- Message msg = generator.messageForDataType(mockMessage("1234", record), DataType.SMS);
+ Message msg = generator.messageForDataType(mockMessage("1234", record), DataType.SMS, 0);
assertThat(msg.getHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)).isEqualTo(new String[] {
MimeUtil.ENC_QUOTED_PRINTABLE
});
@@ -91,7 +91,7 @@ public class MessageGeneratorTest {
new Address("foo@bar.com"));
when(mmsSupport.getDetails(any(Uri.class), any(AddressStyle.class))).thenReturn(details);
- Message msg = generator.messageForDataType(mockMessage("1234", personRecord), DataType.MMS);
+ Message msg = generator.messageForDataType(mockMessage("1234", personRecord), DataType.MMS, 0);
assertThat(msg).isNotNull();
assertThat(msg.getSubject()).isEqualTo("SMS with Foo Bar");
@@ -104,7 +104,7 @@ public class MessageGeneratorTest {
new Address("foo@bar.com"));
when(mmsSupport.getDetails(any(Uri.class), any(AddressStyle.class))).thenReturn(details);
- Message msg = generator.messageForDataType(mockMessage("1234", personRecord), DataType.MMS);
+ Message msg = generator.messageForDataType(mockMessage("1234", personRecord), DataType.MMS, 0);
assertThat(msg.getHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)).isEqualTo(new String[] {
MimeUtil.ENC_7BIT
});
@@ -112,7 +112,7 @@ public class MessageGeneratorTest {
@Test public void testShouldGenerateMessageForCallLogOutgoing() throws Exception {
PersonRecord record = new PersonRecord(-1, "Test Testor", null, null);
- Message msg = generator.messageForDataType(mockCalllogMessage("1234", OUTGOING_TYPE, record), CALLLOG);
+ Message msg = generator.messageForDataType(mockCalllogMessage("1234", OUTGOING_TYPE, record), CALLLOG, 0);
assertThat(msg).isNotNull();
assertThat(msg.getSubject()).isEqualTo("Call with Test Testor");
assertThat(msg.getFrom()[0]).isEqualTo(me);
@@ -121,13 +121,13 @@ public class MessageGeneratorTest {
@Test public void testShouldGenerateMessageForCallLogIncoming() throws Exception {
PersonRecord record = new PersonRecord(-1, "Test Testor", null, null);
- Message message = generator.messageForDataType(mockCalllogMessage("1234", INCOMING_TYPE, record), CALLLOG);
+ Message message = generator.messageForDataType(mockCalllogMessage("1234", INCOMING_TYPE, record), CALLLOG, 0);
assertMessage(message);
}
@Test public void testShouldGenerateMessageForCallLogMissed() throws Exception {
PersonRecord record = new PersonRecord(-1, "Test Testor", null, null);
- Message message = generator.messageForDataType(mockCalllogMessage("1234", MISSED_TYPE, record), CALLLOG);
+ Message message = generator.messageForDataType(mockCalllogMessage("1234", MISSED_TYPE, record), CALLLOG, 0);
assertMessage(message);
}
@@ -140,7 +140,7 @@ private void assertMessage(Message message) {
@Test public void testShouldGenerateMessageForCallLogIncomingUnknown() throws Exception {
PersonRecord record = new PersonRecord(0, null, null, "-1");
- Message msg = generator.messageForDataType(mockCalllogMessage("", INCOMING_TYPE, record), CALLLOG);
+ Message msg = generator.messageForDataType(mockCalllogMessage("", INCOMING_TYPE, record), CALLLOG, 0);
assertThat(msg).isNotNull();
assertThat(msg.getSubject()).isEqualTo("Call with Unknown");
assertThat(msg.getFrom()[0].toString()).isEqualTo("Unknown ");
@@ -149,7 +149,7 @@ private void assertMessage(Message message) {
@Test public void testShouldGenerateCallLogMessageWithCorrectEncoding() throws Exception {
PersonRecord record = new PersonRecord(-1, "Test Testor", null, null);
- Message msg = generator.messageForDataType(mockCalllogMessage("1234", OUTGOING_TYPE, record), CALLLOG);
+ Message msg = generator.messageForDataType(mockCalllogMessage("1234", OUTGOING_TYPE, record), CALLLOG, 0);
assertThat(msg.getHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)).isEqualTo(new String[] {
MimeUtil.ENC_QUOTED_PRINTABLE
});
@@ -157,21 +157,21 @@ private void assertMessage(Message message) {
@Test public void testShouldGenerateSubjectWithNameAndNumberForSMS() throws Exception {
PersonRecord record = new PersonRecord(1, "Test Testor", "test@test.com", "1234");
- Message msg = generator.messageForDataType(mockMessage("1234", record), DataType.SMS);
+ Message msg = generator.messageForDataType(mockMessage("1234", record), DataType.SMS, 0);
assertThat(msg).isNotNull();
assertThat(msg.getSubject()).isEqualTo("SMS with Test Testor");
}
@Test public void shouldGenerateCorrectFromHeaderWithUsersEmailAddress() throws Exception {
PersonRecord record = new PersonRecord(1, "Test Testor", "test@test.com", "1234");
- Message msg = generator.messageForDataType(mockMessage("1234", record), DataType.SMS);
+ Message msg = generator.messageForDataType(mockMessage("1234", record), DataType.SMS, 0);
assertThat(msg).isNotNull();
assertThat(msg.getFrom()[0]).isEqualTo(me);
}
@Test public void shouldGenerateCorrectToHeader() throws Exception {
PersonRecord record = new PersonRecord(1, "Test Testor", "test@test.com", "1234");
- Message msg = generator.messageForDataType(mockMessage("1234", record), DataType.SMS);
+ Message msg = generator.messageForDataType(mockMessage("1234", record), DataType.SMS, 0);
assertThat(msg).isNotNull();
assertThat(msg.getRecipients(Message.RecipientType.TO)[0].toString())
@@ -186,7 +186,7 @@ private void assertMessage(Message message) {
map.put(Telephony.TextBasedSmsColumns.DATE, String.valueOf(date.getTime()));
map.put(Telephony.TextBasedSmsColumns.TYPE, "0");
- Message msg = generator.messageForDataType(map, DataType.SMS);
+ Message msg = generator.messageForDataType(map, DataType.SMS, 0);
assertThat(msg).isNotNull();
verify(headerGenerator).setHeaders(any(Message.class),
@@ -203,7 +203,7 @@ private void assertMessage(Message message) {
Map map = mockMessage("1234", record);
map.put(Telephony.TextBasedSmsColumns.TYPE, "1");
- Message msg = generator.messageForDataType(map, DataType.SMS);
+ Message msg = generator.messageForDataType(map, DataType.SMS, 0);
assertThat(msg).isNotNull();
assertThat(msg.getFrom()[0].toString())
@@ -214,7 +214,7 @@ private void assertMessage(Message message) {
@Test public void testShouldUseNumberIfNameIsUnknown() throws Exception {
PersonRecord record = new PersonRecord(-1, null, null, "1234");
- Message msg = generator.messageForDataType(mockMessage("1234", record), DataType.SMS);
+ Message msg = generator.messageForDataType(mockMessage("1234", record), DataType.SMS, 0);
assertThat(msg).isNotNull();
assertThat(msg.getSubject()).isEqualTo("SMS with 1234");
}
@@ -236,9 +236,9 @@ private void assertMessage(Message message) {
map.put(Telephony.TextBasedSmsColumns.TYPE, "1");
when(groupIds.contains(record)).thenReturn(false);
- assertThat(generator.messageForDataType(map, DataType.SMS)).isNull();
+ assertThat(generator.messageForDataType(map, DataType.SMS, 0)).isNull();
when(groupIds.contains(record)).thenReturn(true);
- assertThat(generator.messageForDataType(map, DataType.SMS)).isNotNull();
+ assertThat(generator.messageForDataType(map, DataType.SMS, 0)).isNotNull();
}
private Map mockMessage(String address, PersonRecord record) {
diff --git a/app/src/test/java/com/zegoggles/smssync/preferences/AuthPreferencesTest.java b/app/src/test/java/com/zegoggles/smssync/preferences/AuthPreferencesTest.java
index 2713f78a9..349accc34 100644
--- a/app/src/test/java/com/zegoggles/smssync/preferences/AuthPreferencesTest.java
+++ b/app/src/test/java/com/zegoggles/smssync/preferences/AuthPreferencesTest.java
@@ -17,7 +17,7 @@ public class AuthPreferencesTest {
@Before public void before() {
initMocks(this);
- authPreferences = new AuthPreferences(RuntimeEnvironment.application);
+ authPreferences = new AuthPreferences(RuntimeEnvironment.application, 0);
}
@Test public void testStoreUri() throws Exception {
diff --git a/app/src/test/java/com/zegoggles/smssync/preferences/PreferencesTest.java b/app/src/test/java/com/zegoggles/smssync/preferences/PreferencesTest.java
index 9f25b0ee3..695ac0419 100644
--- a/app/src/test/java/com/zegoggles/smssync/preferences/PreferencesTest.java
+++ b/app/src/test/java/com/zegoggles/smssync/preferences/PreferencesTest.java
@@ -24,21 +24,26 @@ public class PreferencesTest {
assertThat(preferences.isFirstUse()).isFalse();
}
@Test public void shouldTestForFirstBackup() throws Exception {
- assertThat(preferences.isFirstBackup()).isTrue();
+ assertThat(preferences.isFirstBackup(0)).isTrue();
}
@Test public void shouldTestForFirstBackupSMS() throws Exception {
- preferences.getDataTypePreferences().setMaxSyncedDate(SMS, 1234);
- assertThat(preferences.isFirstBackup()).isFalse();
+ preferences.getDataTypePreferences().setMaxSyncedDate(SMS, 1234, 0);
+ assertThat(preferences.isFirstBackup(0)).isFalse();
+ }
+
+ @Test public void shouldTestForFirstBackupSMSForSecondSIM() throws Exception {
+ preferences.getDataTypePreferences().setMaxSyncedDate(SMS, 1234, 1);
+ assertThat(preferences.isFirstBackup(1)).isFalse();
}
@Test public void shouldTestForFirstBackupMMS() throws Exception {
- preferences.getDataTypePreferences().setMaxSyncedDate(MMS, 1234);
- assertThat(preferences.isFirstBackup()).isFalse();
+ preferences.getDataTypePreferences().setMaxSyncedDate(MMS, 1234, 0);
+ assertThat(preferences.isFirstBackup(0)).isFalse();
}
@Test public void shouldTestForFirstBackupCallLog() throws Exception {
- preferences.getDataTypePreferences().setMaxSyncedDate(CALLLOG, 1234);
- assertThat(preferences.isFirstBackup()).isFalse();
+ preferences.getDataTypePreferences().setMaxSyncedDate(CALLLOG, 1234, 0);
+ assertThat(preferences.isFirstBackup(0)).isFalse();
}
}
diff --git a/app/src/test/java/com/zegoggles/smssync/receiver/SmsBroadcastReceiverTest.java b/app/src/test/java/com/zegoggles/smssync/receiver/SmsBroadcastReceiverTest.java
index 7e7d67ae6..0d567491f 100644
--- a/app/src/test/java/com/zegoggles/smssync/receiver/SmsBroadcastReceiverTest.java
+++ b/app/src/test/java/com/zegoggles/smssync/receiver/SmsBroadcastReceiverTest.java
@@ -17,28 +17,36 @@
@RunWith(RobolectricTestRunner.class)
public class SmsBroadcastReceiverTest {
+
+ public class TestSmsBroadcastReceiver extends SmsBroadcastReceiver {
+ private boolean atLeastOneLoginInformationSet;
+
+ public void SetAtLeastOneLoginInformationSet(boolean value) {
+ atLeastOneLoginInformationSet = value;
+ }
+
+ @Override protected BackupJobs getBackupJobs(Context context) {
+ return backupJobs;
+ }
+
+ @Override protected Preferences getPreferences(Context context) {
+ return preferences;
+ }
+
+ @Override protected boolean atLeastOneLoginInformationSet(Context context) {
+ return atLeastOneLoginInformationSet;
+ }
+ }
+
Context context;
@Mock BackupJobs backupJobs;
@Mock Preferences preferences;
- @Mock AuthPreferences authPreferences;
- SmsBroadcastReceiver receiver;
+ TestSmsBroadcastReceiver receiver;
@Before public void before() {
initMocks(this);
context = RuntimeEnvironment.application;
- receiver = new SmsBroadcastReceiver() {
- @Override protected BackupJobs getBackupJobs(Context context) {
- return backupJobs;
- }
-
- @Override protected Preferences getPreferences(Context context) {
- return preferences;
- }
-
- @Override protected AuthPreferences getAuthPreferences(Context context) {
- return authPreferences;
- }
- };
+ receiver = new TestSmsBroadcastReceiver();
}
@Test public void shouldScheduleIncomingBackupAfterIncomingMessage() throws Exception {
@@ -56,22 +64,22 @@ public class SmsBroadcastReceiverTest {
@Test public void shouldNotScheduleIfLoginInformationIsNotSet() throws Exception {
mockScheduled();
- when(authPreferences.isLoginInformationSet()).thenReturn(false);
+ receiver.SetAtLeastOneLoginInformationSet(false);
receiver.onReceive(context, new Intent().setAction("android.provider.Telephony.SMS_RECEIVED"));
verifyZeroInteractions(backupJobs);
}
@Test public void shouldNotScheduleIfFirstBackupHasNotBeenRun() throws Exception {
mockScheduled();
- when(preferences.isFirstBackup()).thenReturn(true);
+ when(preferences.isFirstBackup(0)).thenReturn(true);
receiver.onReceive(context, new Intent().setAction("android.provider.Telephony.SMS_RECEIVED"));
verifyZeroInteractions(backupJobs);
}
private void mockScheduled() {
- when(authPreferences.isLoginInformationSet()).thenReturn(true);
+ receiver.SetAtLeastOneLoginInformationSet(true);
when(preferences.isAutoBackupEnabled()).thenReturn(true);
- when(preferences.isFirstBackup()).thenReturn(false);
+ when(preferences.isFirstBackup(0)).thenReturn(false);
when(preferences.isUseOldScheduler()).thenReturn(true);
}
}
diff --git a/app/src/test/java/com/zegoggles/smssync/service/BackupConfigTest.java b/app/src/test/java/com/zegoggles/smssync/service/BackupConfigTest.java
index da29cef37..019eddd9f 100644
--- a/app/src/test/java/com/zegoggles/smssync/service/BackupConfigTest.java
+++ b/app/src/test/java/com/zegoggles/smssync/service/BackupConfigTest.java
@@ -6,6 +6,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
+import java.util.ArrayList;
+import java.util.List;
import java.util.EnumSet;
@@ -16,7 +18,9 @@ public class BackupConfigTest {
@Test(expected = IllegalArgumentException.class)
public void shouldCheckForDataTypesEmpty() throws Exception {
- new BackupConfig(mock(BackupImapStore.class),
+ List imapStores = new ArrayList();
+ imapStores.add(mock(BackupImapStore.class));
+ new BackupConfig(imapStores,
0,
-1,
ContactGroup.EVERYBODY,
@@ -29,7 +33,9 @@ public void shouldCheckForDataTypesEmpty() throws Exception {
@SuppressWarnings("ConstantConditions")
@Test(expected = IllegalArgumentException.class)
public void shouldCheckForDataTypesNull() throws Exception {
- new BackupConfig(mock(BackupImapStore.class),
+ List imapStores = new ArrayList();
+ imapStores.add(mock(BackupImapStore.class));
+ new BackupConfig(imapStores,
0,
-1,
ContactGroup.EVERYBODY,
@@ -41,7 +47,9 @@ public void shouldCheckForDataTypesNull() throws Exception {
@Test(expected = IllegalArgumentException.class)
public void shouldCheckForPositiveTry() throws Exception {
- new BackupConfig(mock(BackupImapStore.class),
+ List imapStores = new ArrayList();
+ imapStores.add(mock(BackupImapStore.class));
+ new BackupConfig(imapStores,
-1,
-1,
ContactGroup.EVERYBODY,
diff --git a/app/src/test/java/com/zegoggles/smssync/service/BackupItemsFetcherTest.java b/app/src/test/java/com/zegoggles/smssync/service/BackupItemsFetcherTest.java
index 604db13af..588af7bdd 100644
--- a/app/src/test/java/com/zegoggles/smssync/service/BackupItemsFetcherTest.java
+++ b/app/src/test/java/com/zegoggles/smssync/service/BackupItemsFetcherTest.java
@@ -44,7 +44,7 @@ public class BackupItemsFetcherTest {
@Test public void shouldGetItemsForDataType() throws Exception {
preferences.getDataTypePreferences().setBackupEnabled(true, SMS);
- assertThat(fetcher.getItemsForDataType(SMS, null, -1).getCount()).isEqualTo(0);
+ assertThat(fetcher.getItemsForDataType(SMS, null, 0, -1).getCount()).isEqualTo(0);
verifyZeroInteractions(resolver);
}
@@ -55,7 +55,7 @@ public class BackupItemsFetcherTest {
mockEmptyQuery();
- assertThat(fetcher.getItemsForDataType(SMS, null, -1).getCount()).isEqualTo(0);
+ assertThat(fetcher.getItemsForDataType(SMS, null, 0, -1).getCount()).isEqualTo(0);
}
@Test public void shouldCatchNullPointerExceptions() throws Exception {
@@ -65,7 +65,7 @@ public class BackupItemsFetcherTest {
mockEmptyQuery();
- assertThat(fetcher.getItemsForDataType(SMS, null, -1).getCount()).isEqualTo(0);
+ assertThat(fetcher.getItemsForDataType(SMS, null, 0, -1).getCount()).isEqualTo(0);
}
@Test public void shouldReturnDefaultIfDataTypeCannotBeRead() throws Exception {
@@ -105,6 +105,6 @@ private void mockMostRecentTimestampForType(DataType type, long max) {
private void mockEmptyQuery() {
BackupQueryBuilder.Query query = mock(BackupQueryBuilder.Query.class);
- when(queryBuilder.buildQueryForDataType(SMS, null, -1)).thenReturn(query);
+ when(queryBuilder.buildQueryForDataType(SMS, null, 0, -1)).thenReturn(query);
}
}
diff --git a/app/src/test/java/com/zegoggles/smssync/service/BackupQueryBuilderTest.java b/app/src/test/java/com/zegoggles/smssync/service/BackupQueryBuilderTest.java
index 7cffc7588..f0a65a011 100644
--- a/app/src/test/java/com/zegoggles/smssync/service/BackupQueryBuilderTest.java
+++ b/app/src/test/java/com/zegoggles/smssync/service/BackupQueryBuilderTest.java
@@ -9,12 +9,15 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.robolectric.RobolectricTestRunner;
+import com.zegoggles.smssync.App;
+import com.zegoggles.smssync.utils.SimCard;
import static com.google.common.truth.Truth.assertThat;
import static com.zegoggles.smssync.mail.DataType.CALLLOG;
import static com.zegoggles.smssync.mail.DataType.MMS;
import static com.zegoggles.smssync.mail.DataType.SMS;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
@@ -26,17 +29,17 @@ public class BackupQueryBuilderTest {
@Before public void before() {
initMocks(this);
- when(dataTypePreferences.getMaxSyncedDate(any(DataType.class))).thenReturn(-1L);
+ when(dataTypePreferences.getMaxSyncedDate(any(DataType.class), anyInt())).thenReturn(-1L);
builder = new BackupQueryBuilder(dataTypePreferences);
}
@Test public void shouldBuildQueryForSMS() throws Exception {
- BackupQueryBuilder.Query query = builder.buildQueryForDataType(SMS, null, 200);
+ BackupQueryBuilder.Query query = builder.buildQueryForDataType(SMS, null, 0, 200);
assertThat(query.uri).isEqualTo(Uri.parse("content://sms"));
assertThat(query.projection).isNull();
- assertThat(query.selection).isEqualTo("date > ? AND type <> ?");
- assertThat(query.selectionArgs).asList().containsExactly("-1", "3");
+ assertThat(query.selection).isEqualTo("date > ? AND type <> ? AND (1 OR sub_id = ?)");
+ assertThat(query.selectionArgs).asList().containsExactly("-1", "3", "1");
assertThat(query.sortOrder).isEqualTo("date LIMIT 200");
}
@@ -44,45 +47,45 @@ public class BackupQueryBuilderTest {
ContactGroupIds ids = new ContactGroupIds();
ids.add(1L, 20L);
- BackupQueryBuilder.Query query = builder.buildQueryForDataType(SMS, ids, 200);
+ BackupQueryBuilder.Query query = builder.buildQueryForDataType(SMS, ids, 0, 200);
assertThat(query.uri).isEqualTo(Uri.parse("content://sms"));
assertThat(query.projection).isNull();
- assertThat(query.selection).isEqualTo("date > ? AND type <> ? AND (type = 2 OR person IN (20))");
- assertThat(query.selectionArgs).asList().containsExactly("-1", "3");
+ assertThat(query.selection).isEqualTo("date > ? AND type <> ? AND (type = 2 OR person IN (20)) AND (1 OR sub_id = ?)");
+ assertThat(query.selectionArgs).asList().containsExactly("-1", "3", "1");
assertThat(query.sortOrder).isEqualTo("date LIMIT 200");
}
@Test public void shouldBuildQueryForMMS() throws Exception {
- BackupQueryBuilder.Query query = builder.buildQueryForDataType(MMS, null, 200);
+ BackupQueryBuilder.Query query = builder.buildQueryForDataType(MMS, null, 0, 200);
assertThat(query.uri).isEqualTo(Uri.parse("content://mms"));
assertThat(query.projection).isNull();
- assertThat(query.selection).isEqualTo("date > ? AND m_type <> ?");
- assertThat(query.selectionArgs).asList().containsExactly("-1", "134");
+ assertThat(query.selection).isEqualTo("date > ? AND m_type <> ? AND (1 OR sub_id = ?)");
+ assertThat(query.selectionArgs).asList().containsExactly("-1", "134", "1");
assertThat(query.sortOrder).isEqualTo("date LIMIT 200");
}
@Test public void shouldBuildQueryForMMSWithSyncedDate() throws Exception {
long nowInSecs = System.currentTimeMillis();
- when(dataTypePreferences.getMaxSyncedDate(MMS)).thenReturn(nowInSecs);
- BackupQueryBuilder.Query query = builder.buildQueryForDataType(MMS, null, 200);
+ when(dataTypePreferences.getMaxSyncedDate(MMS, 0)).thenReturn(nowInSecs);
+ BackupQueryBuilder.Query query = builder.buildQueryForDataType(MMS, null, 0, 200);
assertThat(query.uri).isEqualTo(Uri.parse("content://mms"));
assertThat(query.projection).isNull();
- assertThat(query.selection).isEqualTo("date > ? AND m_type <> ?");
- assertThat(query.selectionArgs).asList().containsExactly(String.valueOf(nowInSecs / 1000L), "134");
+ assertThat(query.selection).isEqualTo("date > ? AND m_type <> ? AND (1 OR sub_id = ?)");
+ assertThat(query.selectionArgs).asList().containsExactly(String.valueOf(nowInSecs / 1000L), "134", "1");
assertThat(query.sortOrder).isEqualTo("date LIMIT 200");
}
@Test public void shouldBuildQueryForCallLog() throws Exception {
- BackupQueryBuilder.Query query = builder.buildQueryForDataType(CALLLOG, null, 200);
+ BackupQueryBuilder.Query query = builder.buildQueryForDataType(CALLLOG, null, 0, 200);
assertThat(query.uri).isEqualTo(Uri.parse("content://call_log/calls"));
assertThat(query.projection).asList().containsExactly("_id", "number", "duration", "date", "type");
- assertThat(query.selection).isEqualTo("date > ?");
- assertThat(query.selectionArgs).asList().containsExactly("-1");
+ assertThat(query.selection).isEqualTo("date > ? AND (1 OR subscription_id = ? OR subscription_id = ?)");
+ assertThat(query.selectionArgs).asList().containsExactly("-1", "1", "1");
assertThat(query.sortOrder).isEqualTo("date LIMIT 200");
}
@@ -112,4 +115,46 @@ public class BackupQueryBuilderTest {
assertThat(query.selectionArgs).isNull();
assertThat(query.sortOrder).isEqualTo("date DESC LIMIT 1");
}
+
+ @Test public void shouldBuildQueryForMultiSimSMS() throws Exception {
+ setMultipleSimCards();
+ BackupQueryBuilder.Query query = builder.buildQueryForDataType(SMS, null, 0, 200);
+
+ assertThat(query.uri).isEqualTo(Uri.parse("content://sms"));
+ assertThat(query.projection).isNull();
+ assertThat(query.selection).isEqualTo("date > ? AND type <> ? AND ( sub_id = ?)");
+ assertThat(query.selectionArgs).asList().containsExactly("-1", "3", "1");
+ assertThat(query.sortOrder).isEqualTo("date LIMIT 200");
+ }
+
+ @Test public void shouldBuildQueryForMultiSimMMS() throws Exception {
+ setMultipleSimCards();
+ BackupQueryBuilder.Query query = builder.buildQueryForDataType(MMS, null, 0, 200);
+
+ assertThat(query.uri).isEqualTo(Uri.parse("content://mms"));
+ assertThat(query.projection).isNull();
+ assertThat(query.selection).isEqualTo("date > ? AND m_type <> ? AND ( sub_id = ?)");
+ assertThat(query.selectionArgs).asList().containsExactly("-1", "134", "1");
+ assertThat(query.sortOrder).isEqualTo("date LIMIT 200");
+ }
+
+ @Test public void shouldBuildQueryForMultiSimCallLog() throws Exception {
+ setMultipleSimCards();
+ BackupQueryBuilder.Query query = builder.buildQueryForDataType(CALLLOG, null, 0, 200);
+
+ assertThat(query.uri).isEqualTo(Uri.parse("content://call_log/calls"));
+ assertThat(query.projection).asList().containsExactly("_id", "number", "duration", "date", "type");
+ assertThat(query.selection).isEqualTo("date > ? AND ( subscription_id = ? OR subscription_id = ?)");
+ assertThat(query.selectionArgs).asList().containsExactly("-1", "1", "1");
+ assertThat(query.sortOrder).isEqualTo("date LIMIT 200");
+ }
+
+
+ private void setMultipleSimCards() {
+ SimCard[] simCards = new SimCard[2];
+ simCards[0] = new SimCard("0", "0");
+ simCards[1] = new SimCard("1", "1");
+ simCards[0].IccId = "1";
+ App.SimCards = simCards;
+ }
}
diff --git a/app/src/test/java/com/zegoggles/smssync/service/BackupTaskTest.java b/app/src/test/java/com/zegoggles/smssync/service/BackupTaskTest.java
index eeaee5be7..69f05f9cd 100644
--- a/app/src/test/java/com/zegoggles/smssync/service/BackupTaskTest.java
+++ b/app/src/test/java/com/zegoggles/smssync/service/BackupTaskTest.java
@@ -7,6 +7,7 @@
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.internet.MimeMessage;
import com.fsck.k9.mail.store.imap.XOAuth2AuthenticationFailedException;
+import com.zegoggles.smssync.service.exception.RequiresLoginException;
import com.zegoggles.smssync.auth.TokenRefreshException;
import com.zegoggles.smssync.auth.TokenRefresher;
import com.zegoggles.smssync.contacts.ContactAccessor;
@@ -53,6 +54,9 @@
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
+import java.util.ArrayList;
+import java.util.List;
+
@RunWith(RobolectricTestRunner.class)
public class BackupTaskTest {
BackupTask task;
@@ -80,10 +84,15 @@ public class BackupTaskTest {
task = new BackupTask(service, fetcher, converter, syncer, authPreferences, preferences, accessor, tokenRefresher);
context = RuntimeEnvironment.application;
+
+ new AuthPreferences(context, 0).setImapPassword("a");
+ new AuthPreferences(context, 0).setImapUser("a");
}
private BackupConfig getBackupConfig(EnumSet types) {
- return new BackupConfig(store, 0, 100, new ContactGroup(-1), BackupType.MANUAL, types,
+ List imapStores = new ArrayList();
+ imapStores.add(store);
+ return new BackupConfig(imapStores, 0, 100, new ContactGroup(-1), BackupType.MANUAL, types,
false
);
}
@@ -100,8 +109,8 @@ private BackupConfig getBackupConfig(EnumSet types) {
@Test public void shouldVerifyStoreSettings() throws Exception {
mockFetch(SMS, 1);
- when(converter.convertMessages(any(Cursor.class), eq(SMS))).thenReturn(result(SMS, 1));
- when(store.getFolder(SMS, dataTypePreferences)).thenReturn(folder);
+ when(converter.convertMessages(any(Cursor.class), eq(SMS), anyInt())).thenReturn(result(SMS, 1));
+ when(store.getFolder(SMS, dataTypePreferences, 0)).thenReturn(folder);
task.doInBackground(config);
verify(store).checkSettings();
}
@@ -109,8 +118,8 @@ private BackupConfig getBackupConfig(EnumSet types) {
@Test public void shouldBackupItems() throws Exception {
mockFetch(SMS, 1);
- when(converter.convertMessages(any(Cursor.class), eq(SMS))).thenReturn(result(SMS, 1));
- when(store.getFolder(notNull(DataType.class), same(dataTypePreferences))).thenReturn(folder);
+ when(converter.convertMessages(any(Cursor.class), eq(SMS), anyInt())).thenReturn(result(SMS, 1));
+ when(store.getFolder(notNull(DataType.class), same(dataTypePreferences), anyInt())).thenReturn(folder);
BackupState finalState = task.doInBackground(config);
@@ -130,8 +139,8 @@ private BackupConfig getBackupConfig(EnumSet types) {
public void shouldBackupMultipleTypes() throws Exception {
mockFetch(SMS, 1);
mockFetch(MMS, 2);
- when(store.getFolder(notNull(DataType.class), same(dataTypePreferences))).thenReturn(folder);
- when(converter.convertMessages(any(Cursor.class), any(DataType.class))).thenReturn(result(SMS, 1));
+ when(store.getFolder(notNull(DataType.class), same(dataTypePreferences), anyInt())).thenReturn(folder);
+ when(converter.convertMessages(any(Cursor.class), any(DataType.class), anyInt())).thenReturn(result(SMS, 1));
BackupState finalState = task.doInBackground(getBackupConfig(EnumSet.of(SMS, MMS)));
@@ -143,20 +152,20 @@ public void shouldBackupMultipleTypes() throws Exception {
@Test public void shouldCreateFoldersLazilyOnlyForNeededTypes() throws Exception {
mockFetch(SMS, 1);
- when(converter.convertMessages(any(Cursor.class), eq(SMS))).thenReturn(result(SMS, 1));
- when(store.getFolder(notNull(DataType.class), same(dataTypePreferences))).thenReturn(folder);
+ when(converter.convertMessages(any(Cursor.class), eq(SMS), anyInt())).thenReturn(result(SMS, 1));
+ when(store.getFolder(notNull(DataType.class), same(dataTypePreferences), anyInt())).thenReturn(folder);
task.doInBackground(config);
- verify(store).getFolder(SMS, dataTypePreferences);
- verify(store, never()).getFolder(MMS, dataTypePreferences);
- verify(store, never()).getFolder(CALLLOG, dataTypePreferences);
+ verify(store).getFolder(SMS, dataTypePreferences, 0);
+ verify(store, never()).getFolder(MMS, dataTypePreferences, 0);
+ verify(store, never()).getFolder(CALLLOG, dataTypePreferences, 0);
}
@Test public void shouldCloseImapFolderAfterBackup() throws Exception {
mockFetch(SMS, 1);
- when(converter.convertMessages(any(Cursor.class), eq(SMS))).thenReturn(result(SMS, 1));
- when(store.getFolder(notNull(DataType.class), same(dataTypePreferences))).thenReturn(folder);
+ when(converter.convertMessages(any(Cursor.class), eq(SMS), anyInt())).thenReturn(result(SMS, 1));
+ when(store.getFolder(notNull(DataType.class), same(dataTypePreferences), anyInt())).thenReturn(folder);
task.doInBackground(config);
@@ -172,11 +181,13 @@ public void shouldBackupMultipleTypes() throws Exception {
@Test public void shouldSkipItems() throws Exception {
when(fetcher.getMostRecentTimestamp(any(DataType.class))).thenReturn(-23L);
+ List imapStores = new ArrayList();
+ imapStores.add(store);
BackupState finalState = task.doInBackground(new BackupConfig(
- store, 0, 100, new ContactGroup(-1), BackupType.SKIP, EnumSet.of(SMS), false
+ imapStores, 0, 100, new ContactGroup(-1), BackupType.SKIP, EnumSet.of(SMS), false
)
);
- verify(dataTypePreferences).setMaxSyncedDate(DataType.SMS, -23);
+ verify(dataTypePreferences).setMaxSyncedDate(DataType.SMS, -23, 0);
verifyZeroInteractions(dataTypePreferences);
assertThat(finalState).isNotNull();
@@ -185,12 +196,12 @@ public void shouldBackupMultipleTypes() throws Exception {
@Test public void shouldHandleAuthErrorAndTokenCannotBeRefreshed() throws Exception {
mockFetch(SMS, 1);
- when(converter.convertMessages(any(Cursor.class), notNull(DataType.class))).thenReturn(result(SMS, 1));
+ when(converter.convertMessages(any(Cursor.class), notNull(DataType.class), anyInt())).thenReturn(result(SMS, 1));
XOAuth2AuthenticationFailedException exception = mock(XOAuth2AuthenticationFailedException.class);
when(exception.getStatus()).thenReturn(400);
- when(store.getFolder(notNull(DataType.class), same(dataTypePreferences))).thenThrow(exception);
+ when(store.getFolder(notNull(DataType.class), same(dataTypePreferences), anyInt())).thenThrow(exception);
doThrow(new TokenRefreshException("failed")).when(tokenRefresher).refreshOAuth2Token();
@@ -206,13 +217,15 @@ public void shouldBackupMultipleTypes() throws Exception {
@Test public void shouldHandleAuthErrorAndTokenCouldBeRefreshed() throws Exception {
mockFetch(SMS, 1);
- when(converter.convertMessages(any(Cursor.class), notNull(DataType.class))).thenReturn(result(SMS, 1));
+ when(converter.convertMessages(any(Cursor.class), notNull(DataType.class), anyInt())).thenReturn(result(SMS, 1));
XOAuth2AuthenticationFailedException exception = mock(XOAuth2AuthenticationFailedException.class);
when(exception.getStatus()).thenReturn(400);
- when(store.getFolder(notNull(DataType.class), same(dataTypePreferences))).thenThrow(exception);
- when(service.getBackupImapStore()).thenReturn(store);
+ when(store.getFolder(notNull(DataType.class), same(dataTypePreferences), anyInt())).thenThrow(exception);
+ List imapStores = new ArrayList();
+ imapStores.add(store);
+ when(service.getBackupImapStores()).thenReturn(imapStores);
task.doInBackground(config);
@@ -227,6 +240,17 @@ public void shouldBackupMultipleTypes() throws Exception {
verify(service).releaseLocks();
}
+ @Test public void shouldCheckForLoginCredentials() throws Exception {
+ mockFetch(SMS, 1);
+ when(converter.convertMessages(any(Cursor.class), eq(SMS), anyInt())).thenReturn(result(SMS, 1));
+ when(store.getFolder(SMS, dataTypePreferences, 0)).thenReturn(folder);
+ new AuthPreferences(context, 0).setImapPassword("");
+
+ task.doInBackground(config);
+
+ verify(service).transition(eq(SmsSyncState.ERROR), any(RequiresLoginException.class));
+ }
+
private ConversionResult result(DataType type, int n) {
ConversionResult result = new ConversionResult(type);
@@ -237,7 +261,7 @@ private ConversionResult result(DataType type, int n) {
}
private void mockFetch(DataType type, final int n) {
- when(fetcher.getItemsForDataType(eq(type), any(ContactGroupIds.class), anyInt())).then(new Answer