mirror of
https://github.com/etesync/android
synced 2025-01-23 14:10:54 +00:00
Merge: Add import from local accounts
This set of commits add import from local accounts. It's a bit rough around the edges, but it's good enough to go in, so work can continue collaboratively.
This commit is contained in:
commit
0d848f0573
@ -184,6 +184,7 @@
|
|||||||
android:parentActivityName=".ui.AccountsActivity">
|
android:parentActivityName=".ui.AccountsActivity">
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".ui.ViewCollectionActivity"/>
|
<activity android:name=".ui.ViewCollectionActivity"/>
|
||||||
|
<activity android:name=".ui.importlocal.ImportActivity"/>
|
||||||
<activity android:name=".ui.AccountSettingsActivity"/>
|
<activity android:name=".ui.AccountSettingsActivity"/>
|
||||||
<activity android:name=".ui.CreateCollectionActivity"/>
|
<activity android:name=".ui.CreateCollectionActivity"/>
|
||||||
<activity android:name=".ui.EditCollectionActivity"/>
|
<activity android:name=".ui.EditCollectionActivity"/>
|
||||||
|
@ -181,6 +181,11 @@ public class LocalAddressBook extends AndroidAddressBook implements LocalCollect
|
|||||||
return (LocalContact[])queryContacts(RawContacts.DIRTY + "!= 0 AND " + RawContacts.DELETED + "== 0", null);
|
return (LocalContact[])queryContacts(RawContacts.DIRTY + "!= 0 AND " + RawContacts.DELETED + "== 0", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public LocalContact[] getAll() throws ContactsStorageException {
|
||||||
|
return (LocalContact[])queryContacts(RawContacts.DELETED + "== 0", null);
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public LocalGroup[] getDeletedGroups() throws ContactsStorageException {
|
public LocalGroup[] getDeletedGroups() throws ContactsStorageException {
|
||||||
return (LocalGroup[])queryGroups(Groups.DELETED + "!= 0", null);
|
return (LocalGroup[])queryGroups(Groups.DELETED + "!= 0", null);
|
||||||
|
@ -130,6 +130,11 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {
|
|||||||
return (LocalEvent[])queryEvents(Events._SYNC_ID + " IS NULL AND " + Events.ORIGINAL_ID + " IS NULL", null);
|
return (LocalEvent[])queryEvents(Events._SYNC_ID + " IS NULL AND " + Events.ORIGINAL_ID + " IS NULL", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public LocalEvent[] getAll() throws CalendarStorageException {
|
||||||
|
return (LocalEvent[])queryEvents(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LocalEvent getByUid(String uid) throws CalendarStorageException {
|
public LocalEvent getByUid(String uid) throws CalendarStorageException {
|
||||||
LocalEvent[] ret = (LocalEvent[]) queryEvents(Events._SYNC_ID + " =? ", new String[]{uid});
|
LocalEvent[] ret = (LocalEvent[]) queryEvents(Events._SYNC_ID + " =? ", new String[]{uid});
|
||||||
|
@ -8,7 +8,6 @@ import android.app.Dialog;
|
|||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.ContentProviderClient;
|
import android.content.ContentProviderClient;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@ -17,10 +16,7 @@ import android.os.Bundle;
|
|||||||
import android.provider.CalendarContract;
|
import android.provider.CalendarContract;
|
||||||
import android.provider.ContactsContract;
|
import android.provider.ContactsContract;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v4.app.DialogFragment;
|
import android.support.v4.app.DialogFragment;
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.etesync.syncadapter.App;
|
import com.etesync.syncadapter.App;
|
||||||
import com.etesync.syncadapter.R;
|
import com.etesync.syncadapter.R;
|
||||||
@ -29,6 +25,7 @@ import com.etesync.syncadapter.resource.LocalAddressBook;
|
|||||||
import com.etesync.syncadapter.resource.LocalCalendar;
|
import com.etesync.syncadapter.resource.LocalCalendar;
|
||||||
import com.etesync.syncadapter.resource.LocalContact;
|
import com.etesync.syncadapter.resource.LocalContact;
|
||||||
import com.etesync.syncadapter.resource.LocalEvent;
|
import com.etesync.syncadapter.resource.LocalEvent;
|
||||||
|
import com.etesync.syncadapter.ui.importlocal.ResultFragment;
|
||||||
|
|
||||||
import org.apache.commons.codec.Charsets;
|
import org.apache.commons.codec.Charsets;
|
||||||
|
|
||||||
@ -36,7 +33,6 @@ import java.io.File;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import at.bitfire.ical4android.CalendarStorageException;
|
import at.bitfire.ical4android.CalendarStorageException;
|
||||||
import at.bitfire.ical4android.Event;
|
import at.bitfire.ical4android.Event;
|
||||||
@ -44,10 +40,10 @@ import at.bitfire.ical4android.InvalidCalendarException;
|
|||||||
import at.bitfire.vcard4android.Contact;
|
import at.bitfire.vcard4android.Contact;
|
||||||
import at.bitfire.vcard4android.ContactsStorageException;
|
import at.bitfire.vcard4android.ContactsStorageException;
|
||||||
import lombok.Cleanup;
|
import lombok.Cleanup;
|
||||||
import lombok.ToString;
|
|
||||||
|
|
||||||
import static com.etesync.syncadapter.Constants.KEY_ACCOUNT;
|
import static com.etesync.syncadapter.Constants.KEY_ACCOUNT;
|
||||||
import static com.etesync.syncadapter.Constants.KEY_COLLECTION_INFO;
|
import static com.etesync.syncadapter.Constants.KEY_COLLECTION_INFO;
|
||||||
|
import static com.etesync.syncadapter.ui.importlocal.ResultFragment.*;
|
||||||
|
|
||||||
public class ImportFragment extends DialogFragment {
|
public class ImportFragment extends DialogFragment {
|
||||||
private static final int REQUEST_CODE = 6384; // onActivityResult request
|
private static final int REQUEST_CODE = 6384; // onActivityResult request
|
||||||
@ -85,9 +81,7 @@ public class ImportFragment extends DialogFragment {
|
|||||||
} else {
|
} else {
|
||||||
ImportResult data = new ImportResult();
|
ImportResult data = new ImportResult();
|
||||||
data.e = new Exception(getString(R.string.import_permission_required));
|
data.e = new Exception(getString(R.string.import_permission_required));
|
||||||
getFragmentManager().beginTransaction()
|
((ResultFragment.OnImportCallback) getActivity()).onImportResult(data);
|
||||||
.add(ResultFragment.newInstance(data), null)
|
|
||||||
.commitAllowingStateLoss();
|
|
||||||
|
|
||||||
dismissAllowingStateLoss();
|
dismissAllowingStateLoss();
|
||||||
}
|
}
|
||||||
@ -166,9 +160,8 @@ public class ImportFragment extends DialogFragment {
|
|||||||
} catch (ActivityNotFoundException e) {
|
} catch (ActivityNotFoundException e) {
|
||||||
ImportResult data = new ImportResult();
|
ImportResult data = new ImportResult();
|
||||||
data.e = new Exception("Failed to open file chooser.\nPlease install one.");
|
data.e = new Exception("Failed to open file chooser.\nPlease install one.");
|
||||||
getFragmentManager().beginTransaction()
|
|
||||||
.add(ResultFragment.newInstance(data), null)
|
((ResultFragment.OnImportCallback) getActivity()).onImportResult(data);
|
||||||
.commitAllowingStateLoss();
|
|
||||||
|
|
||||||
dismissAllowingStateLoss();
|
dismissAllowingStateLoss();
|
||||||
}
|
}
|
||||||
@ -200,9 +193,7 @@ public class ImportFragment extends DialogFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void loadFinished(ImportResult data) {
|
public void loadFinished(ImportResult data) {
|
||||||
getFragmentManager().beginTransaction()
|
((ResultFragment.OnImportCallback) getActivity()).onImportResult(data);
|
||||||
.add(ResultFragment.newInstance(data), null)
|
|
||||||
.commitAllowingStateLoss();
|
|
||||||
|
|
||||||
dismissAllowingStateLoss();
|
dismissAllowingStateLoss();
|
||||||
|
|
||||||
@ -347,68 +338,4 @@ public class ImportFragment extends DialogFragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ToString
|
|
||||||
static class ImportResult implements Serializable {
|
|
||||||
long total;
|
|
||||||
long added;
|
|
||||||
long updated;
|
|
||||||
Exception e;
|
|
||||||
|
|
||||||
boolean isFailed() {
|
|
||||||
return (e != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
long getSkipped() {
|
|
||||||
return total - (added + updated);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ResultFragment extends DialogFragment {
|
|
||||||
private static final String KEY_RESULT = "result";
|
|
||||||
private ImportResult result;
|
|
||||||
|
|
||||||
private static ResultFragment newInstance(ImportResult result) {
|
|
||||||
Bundle args = new Bundle();
|
|
||||||
args.putSerializable(KEY_RESULT, result);
|
|
||||||
ResultFragment fragment = new ResultFragment();
|
|
||||||
fragment.setArguments(args);
|
|
||||||
return fragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
result = (ImportResult) getArguments().getSerializable(KEY_RESULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@NonNull
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
int icon;
|
|
||||||
int title;
|
|
||||||
String msg;
|
|
||||||
if (result.isFailed()) {
|
|
||||||
icon = R.drawable.ic_error_dark;
|
|
||||||
title = R.string.import_dialog_failed_title;
|
|
||||||
msg = result.e.getLocalizedMessage();
|
|
||||||
} else {
|
|
||||||
icon = R.drawable.ic_import_export_black;
|
|
||||||
title = R.string.import_dialog_title;
|
|
||||||
msg = getString(R.string.import_dialog_success, result.total, result.added, result.updated, result.getSkipped());
|
|
||||||
}
|
|
||||||
return new AlertDialog.Builder(getActivity())
|
|
||||||
.setTitle(title)
|
|
||||||
.setIcon(icon)
|
|
||||||
.setMessage(msg)
|
|
||||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
// dismiss
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.create();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import com.etesync.syncadapter.model.EntryEntity;
|
|||||||
import com.etesync.syncadapter.model.JournalEntity;
|
import com.etesync.syncadapter.model.JournalEntity;
|
||||||
import com.etesync.syncadapter.resource.LocalAddressBook;
|
import com.etesync.syncadapter.resource.LocalAddressBook;
|
||||||
import com.etesync.syncadapter.resource.LocalCalendar;
|
import com.etesync.syncadapter.resource.LocalCalendar;
|
||||||
|
import com.etesync.syncadapter.ui.importlocal.ImportActivity;
|
||||||
import com.etesync.syncadapter.ui.journalviewer.ListEntriesFragment;
|
import com.etesync.syncadapter.ui.journalviewer.ListEntriesFragment;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
@ -167,8 +168,6 @@ public class ViewCollectionActivity extends AppCompatActivity implements Refresh
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onImport(MenuItem item) {
|
public void onImport(MenuItem item) {
|
||||||
getSupportFragmentManager().beginTransaction()
|
startActivity(ImportActivity.newIntent(ViewCollectionActivity.this, account, info));
|
||||||
.add(ImportFragment.newInstance(account, info), null)
|
|
||||||
.commit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,120 @@
|
|||||||
|
package com.etesync.syncadapter.ui.importlocal;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.content.ContentProviderClient;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.provider.CalendarContract;
|
||||||
|
import android.provider.CalendarContract.Calendars;
|
||||||
|
import android.provider.CalendarContract.Events;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.etesync.syncadapter.App;
|
||||||
|
import com.etesync.syncadapter.resource.LocalCalendar;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by tal on 27/03/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class CalendarAccount {
|
||||||
|
public String accountName;
|
||||||
|
public List<LocalCalendar> calendars = new ArrayList<>();
|
||||||
|
|
||||||
|
private static final String[] CAL_COLS = new String[] {
|
||||||
|
Calendars._ID, Calendars.DELETED, Calendars.NAME, Calendars.CALENDAR_DISPLAY_NAME,
|
||||||
|
Calendars.ACCOUNT_NAME, Calendars.ACCOUNT_TYPE, Calendars.OWNER_ACCOUNT,
|
||||||
|
Calendars.VISIBLE, Calendars.CALENDAR_TIME_ZONE };
|
||||||
|
|
||||||
|
private static final String[] CAL_ID_COLS = new String[] { Events._ID };
|
||||||
|
private static final String CAL_ID_WHERE = Events.CALENDAR_ID + "=?";
|
||||||
|
|
||||||
|
protected CalendarAccount(String accountName) {
|
||||||
|
this.accountName = accountName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load all available calendars.
|
||||||
|
// If an empty list is returned the caller probably needs to enable calendar
|
||||||
|
// read permissions in App Ops/XPrivacy etc.
|
||||||
|
public static List<CalendarAccount> loadAll(ContentResolver resolver) {
|
||||||
|
|
||||||
|
if (missing(resolver, Calendars.CONTENT_URI) || missing(resolver, Events.CONTENT_URI))
|
||||||
|
return new ArrayList<>();
|
||||||
|
|
||||||
|
Cursor cur;
|
||||||
|
try {
|
||||||
|
cur = resolver.query(Calendars.CONTENT_URI,
|
||||||
|
CAL_COLS, null, null, Calendars.ACCOUNT_NAME + " ASC");
|
||||||
|
} catch (Exception except) {
|
||||||
|
App.log.warning("Calendar provider is missing columns, continuing anyway");
|
||||||
|
cur = resolver.query(Calendars.CONTENT_URI, null, null, null, null);
|
||||||
|
except.printStackTrace();
|
||||||
|
}
|
||||||
|
List<CalendarAccount> calendarAccounts = new ArrayList<>(cur.getCount());
|
||||||
|
|
||||||
|
CalendarAccount calendarAccount = null;
|
||||||
|
ContentProviderClient contentProviderClient = resolver.acquireContentProviderClient(CalendarContract.CONTENT_URI);
|
||||||
|
while (cur.moveToNext()) {
|
||||||
|
if (getLong(cur, Calendars.DELETED) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String accountName = getString(cur, Calendars.ACCOUNT_NAME);
|
||||||
|
if (calendarAccount == null || !calendarAccount.accountName.equals(accountName)) {
|
||||||
|
calendarAccount = new CalendarAccount(accountName);
|
||||||
|
calendarAccounts.add(calendarAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
long id = getLong(cur, Calendars._ID);
|
||||||
|
if (id == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String[] args = new String[] { String.valueOf(id) };
|
||||||
|
Cursor eventsCur = resolver.query(Events.CONTENT_URI, CAL_ID_COLS, CAL_ID_WHERE, args, null);
|
||||||
|
Account account = new Account(accountName, getString(cur, Calendars.ACCOUNT_TYPE));
|
||||||
|
|
||||||
|
try {
|
||||||
|
LocalCalendar localCalendar = LocalCalendar.findByName(account, contentProviderClient,
|
||||||
|
LocalCalendar.Factory.INSTANCE, getString(cur, Calendars.NAME));
|
||||||
|
if (localCalendar != null) calendarAccount.calendars.add(localCalendar);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
eventsCur.close();
|
||||||
|
}
|
||||||
|
contentProviderClient.release();
|
||||||
|
cur.close();
|
||||||
|
return calendarAccounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getColumnIndex(Cursor cur, String dbName) {
|
||||||
|
return dbName == null ? -1 : cur.getColumnIndex(dbName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long getLong(Cursor cur, String dbName) {
|
||||||
|
int i = getColumnIndex(cur, dbName);
|
||||||
|
return i == -1 ? -1 : cur.getLong(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getString(Cursor cur, String dbName) {
|
||||||
|
int i = getColumnIndex(cur, dbName);
|
||||||
|
return i == -1 ? null : cur.getString(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean missing(ContentResolver resolver, Uri uri) {
|
||||||
|
// Determine if a provider is missing
|
||||||
|
ContentProviderClient provider = resolver.acquireContentProviderClient(uri);
|
||||||
|
if (provider != null)
|
||||||
|
provider.release();
|
||||||
|
return provider == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return accountName;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,192 @@
|
|||||||
|
package com.etesync.syncadapter.ui.importlocal;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import com.etesync.syncadapter.App;
|
||||||
|
import com.etesync.syncadapter.R;
|
||||||
|
import com.etesync.syncadapter.model.CollectionInfo;
|
||||||
|
import com.etesync.syncadapter.ui.ImportFragment;
|
||||||
|
|
||||||
|
public class ImportActivity extends AppCompatActivity implements SelectImportMethod, ResultFragment.OnImportCallback, DialogInterface {
|
||||||
|
public final static String EXTRA_ACCOUNT = "account",
|
||||||
|
EXTRA_COLLECTION_INFO = "collectionInfo";
|
||||||
|
|
||||||
|
private Account account;
|
||||||
|
protected CollectionInfo info;
|
||||||
|
|
||||||
|
public static Intent newIntent(Context context, Account account, CollectionInfo info) {
|
||||||
|
Intent intent = new Intent(context, ImportActivity.class);
|
||||||
|
intent.putExtra(ImportActivity.EXTRA_ACCOUNT, account);
|
||||||
|
intent.putExtra(ImportActivity.EXTRA_COLLECTION_INFO, info);
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_import);
|
||||||
|
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
setTitle(getString(R.string.import_dialog_title));
|
||||||
|
|
||||||
|
account = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);
|
||||||
|
info = (CollectionInfo) getIntent().getExtras().getSerializable(EXTRA_COLLECTION_INFO);
|
||||||
|
|
||||||
|
if (savedInstanceState == null)
|
||||||
|
getSupportFragmentManager().beginTransaction()
|
||||||
|
.add(R.id.fragment_container, new ImportActivity.SelectImportFragment())
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void importFile() {
|
||||||
|
getSupportFragmentManager().beginTransaction()
|
||||||
|
.add(ImportFragment.newInstance(account, info), null)
|
||||||
|
.commit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void importAccount() {
|
||||||
|
if (info.type == CollectionInfo.Type.CALENDAR) {
|
||||||
|
getSupportFragmentManager().beginTransaction()
|
||||||
|
.replace(R.id.fragment_container,
|
||||||
|
LocalCalendarImportFragment.newInstance(account, info))
|
||||||
|
.addToBackStack(LocalCalendarImportFragment.class.getName())
|
||||||
|
.commit();
|
||||||
|
} else if (info.type == CollectionInfo.Type.ADDRESS_BOOK) {
|
||||||
|
getSupportFragmentManager().beginTransaction()
|
||||||
|
.replace(R.id.fragment_container,
|
||||||
|
LocalContactImportFragment.newInstance(account, info))
|
||||||
|
.addToBackStack(LocalContactImportFragment.class.getName())
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
setTitle(getString(R.string.import_select_account));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void popBackStack() {
|
||||||
|
if (!getSupportFragmentManager().popBackStackImmediate()) {
|
||||||
|
finish();
|
||||||
|
} else {
|
||||||
|
setTitle(getString(R.string.import_dialog_title));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
popBackStack();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
|
popBackStack();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onKeyDown(keyCode, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
App app = (App) getApplicationContext();
|
||||||
|
if (app.getCertManager() != null)
|
||||||
|
app.getCertManager().appInForeground = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
|
||||||
|
App app = (App) getApplicationContext();
|
||||||
|
if (app.getCertManager() != null)
|
||||||
|
app.getCertManager().appInForeground = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onImportResult(ResultFragment.ImportResult importResult) {
|
||||||
|
ResultFragment fragment = ResultFragment.newInstance(importResult);
|
||||||
|
fragment.show(getSupportFragmentManager(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel() {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dismiss() {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SelectImportFragment extends Fragment {
|
||||||
|
|
||||||
|
private SelectImportMethod mSelectImportMethod;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
// This makes sure that the container activity has implemented
|
||||||
|
// the callback interface. If not, it throws an exception
|
||||||
|
try {
|
||||||
|
mSelectImportMethod = (SelectImportMethod) getActivity();
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new ClassCastException(getActivity().toString()
|
||||||
|
+ " must implement MyInterface ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Override
|
||||||
|
public void onAttach(Activity activity) {
|
||||||
|
super.onAttach(activity);
|
||||||
|
// This makes sure that the container activity has implemented
|
||||||
|
// the callback interface. If not, it throws an exception
|
||||||
|
try {
|
||||||
|
mSelectImportMethod = (SelectImportMethod) activity;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new ClassCastException(activity.toString()
|
||||||
|
+ " must implement MyInterface ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View v = inflater.inflate(R.layout.fragment_import, container, false);
|
||||||
|
v.findViewById(R.id.import_button_account).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View aView) {
|
||||||
|
mSelectImportMethod.importAccount();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
v.findViewById(R.id.import_button_file).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View aView) {
|
||||||
|
mSelectImportMethod.importFile();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,263 @@
|
|||||||
|
package com.etesync.syncadapter.ui.importlocal;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.provider.CalendarContract;
|
||||||
|
import android.support.v4.app.ListFragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.BaseExpandableListAdapter;
|
||||||
|
import android.widget.ExpandableListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.etesync.syncadapter.R;
|
||||||
|
import com.etesync.syncadapter.model.CollectionInfo;
|
||||||
|
import com.etesync.syncadapter.resource.LocalCalendar;
|
||||||
|
import com.etesync.syncadapter.resource.LocalEvent;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import at.bitfire.ical4android.CalendarStorageException;
|
||||||
|
import at.bitfire.ical4android.Event;
|
||||||
|
|
||||||
|
import static com.etesync.syncadapter.Constants.KEY_ACCOUNT;
|
||||||
|
import static com.etesync.syncadapter.Constants.KEY_COLLECTION_INFO;
|
||||||
|
|
||||||
|
public class LocalCalendarImportFragment extends ListFragment {
|
||||||
|
|
||||||
|
private Account account;
|
||||||
|
private CollectionInfo info;
|
||||||
|
|
||||||
|
public static LocalCalendarImportFragment newInstance(Account account, CollectionInfo info) {
|
||||||
|
LocalCalendarImportFragment frag = new LocalCalendarImportFragment();
|
||||||
|
Bundle args = new Bundle(1);
|
||||||
|
args.putParcelable(KEY_ACCOUNT, account);
|
||||||
|
args.putSerializable(KEY_COLLECTION_INFO, info);
|
||||||
|
frag.setArguments(args);
|
||||||
|
return frag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setRetainInstance(true);
|
||||||
|
|
||||||
|
account = getArguments().getParcelable(KEY_ACCOUNT);
|
||||||
|
info = (CollectionInfo) getArguments().getSerializable(KEY_COLLECTION_INFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.fragment_local_calendar_import, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
importAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void importAccount() {
|
||||||
|
final List<CalendarAccount> calendarAccountList = CalendarAccount.loadAll(getContext().getContentResolver());
|
||||||
|
|
||||||
|
ExpandableListView listCalendar = (ExpandableListView) getListView();
|
||||||
|
|
||||||
|
final LocalCalendarImportFragment.ExpandableListAdapter adapter =
|
||||||
|
new LocalCalendarImportFragment.ExpandableListAdapter(getContext(), calendarAccountList);
|
||||||
|
listCalendar.setAdapter(adapter);
|
||||||
|
|
||||||
|
listCalendar.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onChildClick(ExpandableListView aExpandableListView, View aView, int groupPosition, int childPosition, long aL) {
|
||||||
|
new ImportEvents().execute(calendarAccountList.get(groupPosition).calendars.get(childPosition));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class ExpandableListAdapter extends BaseExpandableListAdapter {
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private List<CalendarAccount> calendarAccounts;
|
||||||
|
|
||||||
|
public ExpandableListAdapter(Context context, List<CalendarAccount> calendarAccounts) {
|
||||||
|
this.context = context;
|
||||||
|
this.calendarAccounts = calendarAccounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ChildViewHolder {
|
||||||
|
TextView textView;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class GroupViewHolder {
|
||||||
|
TextView titleTextView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getChild(int groupPosition, int childPosititon) {
|
||||||
|
return calendarAccounts.get(groupPosition).calendars
|
||||||
|
.get(childPosititon).getDisplayName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getChildId(int groupPosition, int childPosition) {
|
||||||
|
return childPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getChildView(int groupPosition, final int childPosition,
|
||||||
|
boolean isLastChild, View convertView, ViewGroup parent) {
|
||||||
|
|
||||||
|
final String childText = (String) getChild(groupPosition, childPosition);
|
||||||
|
ChildViewHolder viewHolder;
|
||||||
|
if (convertView == null) {
|
||||||
|
LayoutInflater inflater = (LayoutInflater) context
|
||||||
|
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
convertView = inflater.inflate(R.layout.import_calendars_list_item, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (convertView.getTag() != null) {
|
||||||
|
viewHolder = (ChildViewHolder) convertView.getTag();
|
||||||
|
} else {
|
||||||
|
viewHolder = new ChildViewHolder();
|
||||||
|
viewHolder.textView = (TextView) convertView
|
||||||
|
.findViewById(R.id.listItemText);
|
||||||
|
convertView.setTag(viewHolder);
|
||||||
|
}
|
||||||
|
viewHolder.textView.setText(childText);
|
||||||
|
return convertView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getChildrenCount(int groupPosition) {
|
||||||
|
return calendarAccounts.get(groupPosition).calendars
|
||||||
|
.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getGroup(int groupPosition) {
|
||||||
|
return calendarAccounts.get(groupPosition).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getGroupCount() {
|
||||||
|
return calendarAccounts.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getGroupId(int groupPosition) {
|
||||||
|
return groupPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getGroupView(int groupPosition, boolean isExpanded,
|
||||||
|
View convertView, ViewGroup parent) {
|
||||||
|
String headerTitle = (String) getGroup(groupPosition);
|
||||||
|
GroupViewHolder viewHolder;
|
||||||
|
if (convertView == null) {
|
||||||
|
LayoutInflater inflater = (LayoutInflater) context
|
||||||
|
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
convertView = inflater.inflate(R.layout.import_calendars_list_group, null);
|
||||||
|
}
|
||||||
|
if (convertView.getTag() != null) {
|
||||||
|
viewHolder = (GroupViewHolder) convertView.getTag();
|
||||||
|
} else {
|
||||||
|
viewHolder = new GroupViewHolder();
|
||||||
|
viewHolder.titleTextView = (TextView) convertView
|
||||||
|
.findViewById(R.id.title);
|
||||||
|
convertView.setTag(viewHolder);
|
||||||
|
}
|
||||||
|
viewHolder.titleTextView.setText(headerTitle);
|
||||||
|
|
||||||
|
return convertView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasStableIds() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChildSelectable(int groupPosition, int childPosition) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class ImportEvents extends AsyncTask<LocalCalendar, Integer, ResultFragment.ImportResult> {
|
||||||
|
ProgressDialog progressDialog;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
progressDialog = new ProgressDialog(getActivity());
|
||||||
|
progressDialog.setTitle(R.string.import_dialog_title);
|
||||||
|
progressDialog.setMessage(getString(R.string.import_dialog_adding_entries));
|
||||||
|
progressDialog.setCanceledOnTouchOutside(false);
|
||||||
|
progressDialog.setCancelable(false);
|
||||||
|
progressDialog.setIndeterminate(false);
|
||||||
|
progressDialog.setIcon(R.drawable.ic_import_export_black);
|
||||||
|
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||||
|
progressDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResultFragment.ImportResult doInBackground(LocalCalendar... calendars) {
|
||||||
|
return importEvents(calendars[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onProgressUpdate(Integer... progress) {
|
||||||
|
if (progressDialog != null)
|
||||||
|
progressDialog.setProgress(progress[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(ResultFragment.ImportResult result) {
|
||||||
|
progressDialog.dismiss();
|
||||||
|
((ResultFragment.OnImportCallback) getActivity()).onImportResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResultFragment.ImportResult importEvents(LocalCalendar fromCalendar) {
|
||||||
|
ResultFragment.ImportResult result = new ResultFragment.ImportResult();
|
||||||
|
try {
|
||||||
|
LocalCalendar localCalendar = LocalCalendar.findByName(account,
|
||||||
|
getContext().getContentResolver().acquireContentProviderClient(CalendarContract.CONTENT_URI),
|
||||||
|
LocalCalendar.Factory.INSTANCE, info.url);
|
||||||
|
LocalEvent[] localEvents = fromCalendar.getAll();
|
||||||
|
int total = localEvents.length;
|
||||||
|
progressDialog.setMax(total);
|
||||||
|
result.total = total;
|
||||||
|
int progress = 0;
|
||||||
|
for (LocalEvent currentLocalEvent : localEvents) {
|
||||||
|
Event event = currentLocalEvent.getEvent();
|
||||||
|
try {
|
||||||
|
LocalEvent localEvent = event.uid == null ? null :
|
||||||
|
localCalendar.getByUid(event.uid);
|
||||||
|
|
||||||
|
if (localEvent != null) {
|
||||||
|
localEvent.updateAsDirty(event);
|
||||||
|
result.updated++;
|
||||||
|
} else {
|
||||||
|
localEvent = new LocalEvent(localCalendar, event, event.uid, null);
|
||||||
|
localEvent.addAsDirty();
|
||||||
|
result.added++;
|
||||||
|
}
|
||||||
|
} catch (CalendarStorageException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
}
|
||||||
|
publishProgress(++progress);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
result.e = e;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,299 @@
|
|||||||
|
package com.etesync.syncadapter.ui.importlocal;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.ContentProviderClient;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.provider.ContactsContract;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.etesync.syncadapter.R;
|
||||||
|
import com.etesync.syncadapter.model.CollectionInfo;
|
||||||
|
import com.etesync.syncadapter.resource.LocalAddressBook;
|
||||||
|
import com.etesync.syncadapter.resource.LocalContact;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import at.bitfire.vcard4android.Contact;
|
||||||
|
import at.bitfire.vcard4android.ContactsStorageException;
|
||||||
|
|
||||||
|
import static android.content.ContentValues.TAG;
|
||||||
|
import static com.etesync.syncadapter.Constants.KEY_ACCOUNT;
|
||||||
|
import static com.etesync.syncadapter.Constants.KEY_COLLECTION_INFO;
|
||||||
|
|
||||||
|
public class LocalContactImportFragment extends Fragment {
|
||||||
|
|
||||||
|
private Account account;
|
||||||
|
private CollectionInfo info;
|
||||||
|
private RecyclerView recyclerView;
|
||||||
|
|
||||||
|
public static LocalContactImportFragment newInstance(Account account, CollectionInfo info) {
|
||||||
|
LocalContactImportFragment frag = new LocalContactImportFragment();
|
||||||
|
Bundle args = new Bundle(1);
|
||||||
|
args.putParcelable(KEY_ACCOUNT, account);
|
||||||
|
args.putSerializable(KEY_COLLECTION_INFO, info);
|
||||||
|
frag.setArguments(args);
|
||||||
|
|
||||||
|
return frag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setRetainInstance(true);
|
||||||
|
|
||||||
|
account = getArguments().getParcelable(KEY_ACCOUNT);
|
||||||
|
info = (CollectionInfo) getArguments().getSerializable(KEY_COLLECTION_INFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_local_contact_import, container, false);
|
||||||
|
|
||||||
|
recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
|
||||||
|
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||||
|
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity()));
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
importAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void importAccount() {
|
||||||
|
ContentProviderClient provider = getContext().getContentResolver().acquireContentProviderClient(ContactsContract.RawContacts.CONTENT_URI);
|
||||||
|
Cursor cursor;
|
||||||
|
try {
|
||||||
|
cursor = provider.query(ContactsContract.RawContacts.CONTENT_URI,
|
||||||
|
new String[]{ContactsContract.RawContacts.ACCOUNT_NAME, ContactsContract.RawContacts.ACCOUNT_TYPE}
|
||||||
|
, null, null, ContactsContract.RawContacts.ACCOUNT_NAME + " ASC");
|
||||||
|
} catch (Exception except) {
|
||||||
|
Log.w(TAG, "Calendar provider is missing columns, continuing anyway");
|
||||||
|
|
||||||
|
except.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<LocalAddressBook> localAddressBooks = new ArrayList<>();
|
||||||
|
Account account = null;
|
||||||
|
int accountNameIndex = cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_NAME);
|
||||||
|
int accountTypeIndex = cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_TYPE);
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
String accountName = cursor.getString(accountNameIndex);
|
||||||
|
String accountType = cursor.getString(accountTypeIndex);
|
||||||
|
if (account == null || (!account.name.equals(accountName) || !account.type.equals(accountType))) {
|
||||||
|
account = new Account(accountName, accountType);
|
||||||
|
localAddressBooks.add(new LocalAddressBook(account, provider));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recyclerView.setAdapter(new ImportContactAdapter(localAddressBooks, new OnAccountSelected() {
|
||||||
|
@Override
|
||||||
|
public void accountSelected(int index) {
|
||||||
|
new ImportContacts().execute(localAddressBooks.get(index));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class ImportContacts extends AsyncTask<LocalAddressBook, Integer, ResultFragment.ImportResult> {
|
||||||
|
ProgressDialog progressDialog;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
progressDialog = new ProgressDialog(getActivity());
|
||||||
|
progressDialog.setTitle(R.string.import_dialog_title);
|
||||||
|
progressDialog.setMessage(getString(R.string.import_dialog_adding_entries));
|
||||||
|
progressDialog.setCanceledOnTouchOutside(false);
|
||||||
|
progressDialog.setCancelable(false);
|
||||||
|
progressDialog.setIndeterminate(false);
|
||||||
|
progressDialog.setIcon(R.drawable.ic_import_export_black);
|
||||||
|
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||||
|
progressDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResultFragment.ImportResult doInBackground(LocalAddressBook... addressBooks) {
|
||||||
|
return importContacts(addressBooks[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onProgressUpdate(Integer... progress) {
|
||||||
|
if (progressDialog != null)
|
||||||
|
progressDialog.setProgress(progress[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(ResultFragment.ImportResult result) {
|
||||||
|
progressDialog.dismiss();
|
||||||
|
((ResultFragment.OnImportCallback) getActivity()).onImportResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResultFragment.ImportResult importContacts(LocalAddressBook localAddressBook) {
|
||||||
|
ResultFragment.ImportResult result = new ResultFragment.ImportResult();
|
||||||
|
try {
|
||||||
|
LocalAddressBook addressBook = new LocalAddressBook(account,
|
||||||
|
getContext().getContentResolver().acquireContentProviderClient(ContactsContract.RawContacts.CONTENT_URI));
|
||||||
|
LocalContact[] localContacts = localAddressBook.getAll();
|
||||||
|
int total = localContacts.length;
|
||||||
|
progressDialog.setMax(total);
|
||||||
|
result.total = total;
|
||||||
|
int progress = 0;
|
||||||
|
for (LocalContact currentLocalContact : localContacts) {
|
||||||
|
Contact contact = currentLocalContact.getContact();
|
||||||
|
(new LocalContact(addressBook, contact, contact.uid, null)).createAsDirty();
|
||||||
|
|
||||||
|
try {
|
||||||
|
LocalContact localContact = contact.uid == null ?
|
||||||
|
null : (LocalContact) addressBook.getByUid(contact.uid);
|
||||||
|
if (localContact != null) {
|
||||||
|
localContact.updateAsDirty(contact);
|
||||||
|
result.updated++;
|
||||||
|
} else {
|
||||||
|
localContact = new LocalContact(addressBook, contact, contact.uid, null);
|
||||||
|
localContact.createAsDirty();
|
||||||
|
result.added++;
|
||||||
|
}
|
||||||
|
} catch (ContactsStorageException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
result.e = e;
|
||||||
|
}
|
||||||
|
publishProgress(++progress);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.e = e;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ImportContactAdapter extends RecyclerView.Adapter<ImportContactAdapter.ViewHolder> {
|
||||||
|
private static final String TAG = "ImportContactAdapter";
|
||||||
|
|
||||||
|
private List<LocalAddressBook> mAddressBooks;
|
||||||
|
private OnAccountSelected mOnAccountSelected;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a reference to the type of views that you are using (custom ViewHolder)
|
||||||
|
*/
|
||||||
|
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
private final TextView titleTextView;
|
||||||
|
private final TextView descTextView;
|
||||||
|
|
||||||
|
public ViewHolder(View v, final OnAccountSelected onAccountSelected) {
|
||||||
|
super(v);
|
||||||
|
// Define click listener for the ViewHolder's View.
|
||||||
|
v.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
onAccountSelected.accountSelected(getAdapterPosition());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
titleTextView = (TextView) v.findViewById(R.id.title);
|
||||||
|
descTextView = (TextView) v.findViewById(R.id.description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextView getTitleTextView() {
|
||||||
|
return titleTextView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextView getDescriptionTextView() {
|
||||||
|
return descTextView;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the dataset of the Adapter.
|
||||||
|
*
|
||||||
|
* @param addressBooks containing the data to populate views to be used by RecyclerView.
|
||||||
|
*/
|
||||||
|
public ImportContactAdapter(List<LocalAddressBook> addressBooks, OnAccountSelected onAccountSelected) {
|
||||||
|
mAddressBooks = addressBooks;
|
||||||
|
mOnAccountSelected = onAccountSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new views (invoked by the layout manager)
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
|
||||||
|
// Create a new view.
|
||||||
|
View v = LayoutInflater.from(viewGroup.getContext())
|
||||||
|
.inflate(R.layout.import_contacts_list_item, viewGroup, false);
|
||||||
|
|
||||||
|
return new ViewHolder(v, mOnAccountSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
|
||||||
|
viewHolder.getTitleTextView().setText(mAddressBooks.get(position).account.name);
|
||||||
|
viewHolder.getDescriptionTextView().setText(mAddressBooks.get(position).account.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return mAddressBooks.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface OnAccountSelected {
|
||||||
|
void accountSelected(int index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DividerItemDecoration extends RecyclerView.ItemDecoration {
|
||||||
|
|
||||||
|
private static final int[] ATTRS = new int[]{
|
||||||
|
android.R.attr.listDivider
|
||||||
|
};
|
||||||
|
|
||||||
|
private Drawable mDivider;
|
||||||
|
|
||||||
|
public DividerItemDecoration(Context context) {
|
||||||
|
final TypedArray a = context.obtainStyledAttributes(ATTRS);
|
||||||
|
mDivider = a.getDrawable(0);
|
||||||
|
a.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||||
|
drawVertical(c, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawVertical(Canvas c, RecyclerView parent) {
|
||||||
|
final int left = parent.getPaddingLeft();
|
||||||
|
final int right = parent.getWidth() - parent.getPaddingRight();
|
||||||
|
|
||||||
|
final int childCount = parent.getChildCount();
|
||||||
|
for (int i = 0; i < childCount; i++) {
|
||||||
|
final View child = parent.getChildAt(i);
|
||||||
|
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
|
||||||
|
.getLayoutParams();
|
||||||
|
final int top = child.getBottom() + params.bottomMargin;
|
||||||
|
final int bottom = top + mDivider.getIntrinsicHeight();
|
||||||
|
mDivider.setBounds(left, top, right, bottom);
|
||||||
|
mDivider.draw(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
|
||||||
|
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
package com.etesync.syncadapter.ui.importlocal;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
|
||||||
|
import com.etesync.syncadapter.R;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by tal on 30/03/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ResultFragment extends DialogFragment {
|
||||||
|
private static final String KEY_RESULT = "result";
|
||||||
|
private ImportResult result;
|
||||||
|
|
||||||
|
public static ResultFragment newInstance(ImportResult result) {
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putSerializable(KEY_RESULT, result);
|
||||||
|
ResultFragment fragment = new ResultFragment();
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
result = (ImportResult) getArguments().getSerializable(KEY_RESULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDismiss(DialogInterface dialog) {
|
||||||
|
super.onDismiss(dialog);
|
||||||
|
Activity activity = getActivity();
|
||||||
|
if (activity instanceof DialogInterface) {
|
||||||
|
((DialogInterface)activity).dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
int icon;
|
||||||
|
int title;
|
||||||
|
String msg;
|
||||||
|
if (result.isFailed()) {
|
||||||
|
icon = R.drawable.ic_error_dark;
|
||||||
|
title = R.string.import_dialog_failed_title;
|
||||||
|
msg = result.e.getLocalizedMessage();
|
||||||
|
} else {
|
||||||
|
icon = R.drawable.ic_import_export_black;
|
||||||
|
title = R.string.import_dialog_title;
|
||||||
|
msg = getString(R.string.import_dialog_success, result.total, result.added, result.updated, result.getSkipped());
|
||||||
|
}
|
||||||
|
return new AlertDialog.Builder(getActivity())
|
||||||
|
.setTitle(title)
|
||||||
|
.setIcon(icon)
|
||||||
|
.setMessage(msg)
|
||||||
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
// dismiss
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
public static class ImportResult implements Serializable {
|
||||||
|
public long total;
|
||||||
|
public long added;
|
||||||
|
public long updated;
|
||||||
|
public Exception e;
|
||||||
|
|
||||||
|
public boolean isFailed() {
|
||||||
|
return (e != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSkipped() {
|
||||||
|
return total - (added + updated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnImportCallback {
|
||||||
|
void onImportResult(ImportResult importResult);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.etesync.syncadapter.ui.importlocal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by tal on 30/03/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface SelectImportMethod {
|
||||||
|
void importFile();
|
||||||
|
|
||||||
|
void importAccount();
|
||||||
|
}
|
7
app/src/main/res/layout/activity_import.xml
Normal file
7
app/src/main/res/layout/activity_import.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/fragment_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="@dimen/activity_margin">
|
||||||
|
</FrameLayout>
|
18
app/src/main/res/layout/fragment_import.xml
Normal file
18
app/src/main/res/layout/fragment_import.xml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/import_button_file"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/import_button_file"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/import_button_account"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/import_button_local"/>
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ExpandableListView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@android:id/list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:choiceMode="singleChoice"
|
||||||
|
android:descendantFocusability="beforeDescendants"/>
|
||||||
|
|
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.v7.widget.RecyclerView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/recyclerView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:choiceMode="singleChoice"/>
|
||||||
|
|
20
app/src/main/res/layout/import_calendars_list_group.xml
Normal file
20
app/src/main/res/layout/import_calendars_list_group.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/light_green700"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
tools:text="Title"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
15
app/src/main/res/layout/import_calendars_list_item.xml
Normal file
15
app/src/main/res/layout/import_calendars_list_item.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/listItemText"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="?android:attr/expandableListPreferredChildPaddingLeft"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
21
app/src/main/res/layout/import_contacts_list_item.xml
Normal file
21
app/src/main/res/layout/import_contacts_list_item.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
tools:text="Title"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/description"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="Subtitle"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -248,4 +248,10 @@
|
|||||||
<string name="loading">Loading...</string>
|
<string name="loading">Loading...</string>
|
||||||
<string name="loading_error_title">Loading Error</string>
|
<string name="loading_error_title">Loading Error</string>
|
||||||
<string name="loading_error_content">Refresh</string>
|
<string name="loading_error_content">Refresh</string>
|
||||||
|
|
||||||
|
<!-- Import Activity -->
|
||||||
|
<string name="import_button_file">Import from file</string>
|
||||||
|
<string name="import_button_local">Import from local</string>
|
||||||
|
<string name="import_select_account">Select Account</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user