mirror of
https://github.com/bitfireAT/davx5-ose
synced 2024-09-06 18:01:11 +00:00
Improved service detection + GUI
* DavService: query group-membership principals for home sets, too * working collection selection * contacts sync according to selected address book
This commit is contained in:
parent
6f98fdb76e
commit
a10649d818
|
@ -16,7 +16,6 @@ import android.content.Intent;
|
|||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.text.TextUtils;
|
||||
|
@ -36,12 +35,9 @@ import at.bitfire.dav4android.UrlUtils;
|
|||
import at.bitfire.dav4android.exception.DavException;
|
||||
import at.bitfire.dav4android.exception.HttpException;
|
||||
import at.bitfire.dav4android.exception.NotFoundException;
|
||||
import at.bitfire.dav4android.property.AddressbookDescription;
|
||||
import at.bitfire.dav4android.property.AddressbookHomeSet;
|
||||
import at.bitfire.dav4android.property.CalendarDescription;
|
||||
import at.bitfire.dav4android.property.CalendarHomeSet;
|
||||
import at.bitfire.dav4android.property.DisplayName;
|
||||
import at.bitfire.dav4android.property.ResourceType;
|
||||
import at.bitfire.dav4android.property.GroupMembership;
|
||||
import at.bitfire.davdroid.model.CollectionInfo;
|
||||
import at.bitfire.davdroid.model.ServiceDB.*;
|
||||
import at.bitfire.davdroid.syncadapter.AccountSettings;
|
||||
|
@ -161,29 +157,32 @@ public class DavService extends Service {
|
|||
// create authenticating OkHttpClient (credentials taken from account settings)
|
||||
OkHttpClient httpClient = httpClient();
|
||||
|
||||
// refresh home sets
|
||||
// refresh home sets: principal
|
||||
Set<HttpUrl> homeSets = readHomeSets();
|
||||
HttpUrl principal = readPrincipal();
|
||||
if (principal != null) {
|
||||
Constants.log.debug("[DavService {}] Querying principal for home sets", service);
|
||||
DavResource dav = new DavResource(null, httpClient, principal);
|
||||
if (Services.SERVICE_CARDDAV.equals(serviceType)) {
|
||||
dav.propfind(0, AddressbookHomeSet.NAME);
|
||||
AddressbookHomeSet addressbookHomeSet = (AddressbookHomeSet)dav.properties.get(AddressbookHomeSet.NAME);
|
||||
if (addressbookHomeSet != null)
|
||||
for (String href : addressbookHomeSet.hrefs)
|
||||
homeSets.add(UrlUtils.withTrailingSlash(dav.location.resolve(href)));
|
||||
} else if (Services.SERVICE_CALDAV.equals(serviceType)) {
|
||||
dav.propfind(0, CalendarHomeSet.NAME);
|
||||
CalendarHomeSet calendarHomeSet = (CalendarHomeSet)dav.properties.get(CalendarHomeSet.NAME);
|
||||
if (calendarHomeSet != null)
|
||||
for (String href : calendarHomeSet.hrefs)
|
||||
homeSets.add(UrlUtils.withTrailingSlash(dav.location.resolve(href)));
|
||||
}
|
||||
queryHomeSets(serviceType, httpClient, principal, homeSets);
|
||||
|
||||
// refresh home sets: direct group memberships
|
||||
GroupMembership groupMembership = (GroupMembership)dav.properties.get(GroupMembership.NAME);
|
||||
if (groupMembership != null)
|
||||
for (String href : groupMembership.hrefs) {
|
||||
Constants.log.debug("[DavService {}] Querying member group {} for home sets", href);
|
||||
queryHomeSets(serviceType, httpClient, dav.location.resolve(href), homeSets);
|
||||
}
|
||||
}
|
||||
|
||||
// refresh collections in home sets
|
||||
// now refresh collections (taken from home sets)
|
||||
Map<HttpUrl, CollectionInfo> collections = readCollections();
|
||||
|
||||
// (remember selections before)
|
||||
Set<HttpUrl> selectedCollections = new HashSet<>();
|
||||
for (CollectionInfo info : collections.values())
|
||||
if (info.selected)
|
||||
selectedCollections.add(HttpUrl.parse(info.url));
|
||||
|
||||
for (Iterator<HttpUrl> iterator = homeSets.iterator(); iterator.hasNext();) {
|
||||
HttpUrl homeSet = iterator.next();
|
||||
Constants.log.debug("[DavService {}] Listing home set {}", service, homeSet);
|
||||
|
@ -229,6 +228,13 @@ public class DavService extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
// restore selections
|
||||
for (HttpUrl url : selectedCollections) {
|
||||
CollectionInfo info = collections.get(url);
|
||||
if (info != null)
|
||||
info.selected = true;
|
||||
}
|
||||
|
||||
try {
|
||||
db.beginTransaction();
|
||||
saveHomeSets(homeSets);
|
||||
|
@ -249,16 +255,26 @@ public class DavService extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
private String serviceType() {
|
||||
@Cleanup Cursor cursor = db.query(Services._TABLE, new String[]{Services.SERVICE}, Services.ID + "=?", new String[]{String.valueOf(service)}, null, null, null);
|
||||
if (cursor.moveToNext())
|
||||
return cursor.getString(0);
|
||||
else
|
||||
throw new IllegalArgumentException("Service not found");
|
||||
private void queryHomeSets(String serviceType, OkHttpClient client, HttpUrl url, Set<HttpUrl> homeSets) throws IOException, HttpException, DavException {
|
||||
DavResource dav = new DavResource(null, client, url);
|
||||
if (Services.SERVICE_CARDDAV.equals(serviceType)) {
|
||||
dav.propfind(0, AddressbookHomeSet.NAME, GroupMembership.NAME);
|
||||
AddressbookHomeSet addressbookHomeSet = (AddressbookHomeSet)dav.properties.get(AddressbookHomeSet.NAME);
|
||||
if (addressbookHomeSet != null)
|
||||
for (String href : addressbookHomeSet.hrefs)
|
||||
homeSets.add(UrlUtils.withTrailingSlash(dav.location.resolve(href)));
|
||||
} else if (Services.SERVICE_CALDAV.equals(serviceType)) {
|
||||
dav.propfind(0, CalendarHomeSet.NAME, GroupMembership.NAME);
|
||||
CalendarHomeSet calendarHomeSet = (CalendarHomeSet)dav.properties.get(CalendarHomeSet.NAME);
|
||||
if (calendarHomeSet != null)
|
||||
for (String href : calendarHomeSet.hrefs)
|
||||
homeSets.add(UrlUtils.withTrailingSlash(dav.location.resolve(href)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private OkHttpClient httpClient() {
|
||||
@Cleanup Cursor cursor = db.query(Services._TABLE, new String[]{Services.ACCOUNT_NAME}, Services.ID + "=?", new String[]{String.valueOf(service)}, null, null, null);
|
||||
@Cleanup Cursor cursor = db.query(Services._TABLE, new String[]{Services.ACCOUNT_NAME}, Services.ID + "=?", new String[] { String.valueOf(service) }, null, null, null);
|
||||
if (cursor.moveToNext()) {
|
||||
Account account = new Account(cursor.getString(0), Constants.ACCOUNT_TYPE);
|
||||
AccountSettings settings = new AccountSettings(DavService.this, account);
|
||||
|
@ -270,6 +286,14 @@ public class DavService extends Service {
|
|||
throw new IllegalArgumentException("Service not found");
|
||||
}
|
||||
|
||||
private String serviceType() {
|
||||
@Cleanup Cursor cursor = db.query(Services._TABLE, new String[]{Services.SERVICE}, Services.ID + "=?", new String[] { String.valueOf(service) }, null, null, null);
|
||||
if (cursor.moveToNext())
|
||||
return cursor.getString(0);
|
||||
else
|
||||
throw new IllegalArgumentException("Service not found");
|
||||
}
|
||||
|
||||
private HttpUrl readPrincipal() {
|
||||
@Cleanup Cursor cursor = db.query(Services._TABLE, new String[]{Services.PRINCIPAL}, Services.ID + "=?", new String[]{String.valueOf(service)}, null, null, null);
|
||||
if (cursor.moveToNext()) {
|
||||
|
|
|
@ -23,7 +23,7 @@ import java.util.concurrent.TimeUnit;
|
|||
|
||||
import at.bitfire.dav4android.BasicDigestAuthenticator;
|
||||
import de.duenndns.ssl.MemorizingTrustManager;
|
||||
import lombok.NonNull;
|
||||
import android.support.annotation.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import okhttp3.Credentials;
|
||||
import okhttp3.Interceptor;
|
||||
|
|
|
@ -27,6 +27,7 @@ import at.bitfire.davdroid.model.ServiceDB.*;
|
|||
@ToString
|
||||
public class CollectionInfo {
|
||||
public long id;
|
||||
public Long serviceID;
|
||||
|
||||
public enum Type {
|
||||
ADDRESS_BOOK,
|
||||
|
@ -112,6 +113,8 @@ public class CollectionInfo {
|
|||
public static CollectionInfo fromDB(ContentValues values) {
|
||||
CollectionInfo info = new CollectionInfo();
|
||||
info.id = values.getAsLong(Collections.ID);
|
||||
info.serviceID = values.getAsLong(Collections.SERVICE_ID);
|
||||
|
||||
info.url = values.getAsString(Collections.URL);
|
||||
info.displayName = values.getAsString(Collections.DISPLAY_NAME);
|
||||
info.description = values.getAsString(Collections.DESCRIPTION);
|
||||
|
@ -128,6 +131,8 @@ public class CollectionInfo {
|
|||
|
||||
public ContentValues toDB() {
|
||||
ContentValues values = new ContentValues();
|
||||
// Collections.SERVICE_ID is never changed
|
||||
|
||||
values.put(Collections.URL, url);
|
||||
values.put(Collections.DISPLAY_NAME, displayName);
|
||||
values.put(Collections.DESCRIPTION, description);
|
||||
|
|
|
@ -78,6 +78,14 @@ public class LocalAddressBook extends AndroidAddressBook implements LocalCollect
|
|||
return (LocalContact[])queryContacts(AndroidContact.COLUMN_FILENAME + " IS NULL", null);
|
||||
}
|
||||
|
||||
public void deleteAll() throws ContactsStorageException {
|
||||
try {
|
||||
provider.delete(syncAdapterURI(RawContacts.CONTENT_URI), null, null);
|
||||
} catch (RemoteException e) {
|
||||
throw new ContactsStorageException("Couldn't delete all local contacts", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// GROUPS
|
||||
|
||||
|
|
|
@ -9,10 +9,8 @@
|
|||
package at.bitfire.davdroid.resource;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
|
@ -23,6 +21,7 @@ import android.provider.CalendarContract;
|
|||
import android.provider.CalendarContract.Calendars;
|
||||
import android.provider.CalendarContract.Events;
|
||||
import android.provider.CalendarContract.Reminders;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import net.fortuna.ical4j.model.component.VTimeZone;
|
||||
|
@ -42,7 +41,6 @@ import at.bitfire.ical4android.CalendarStorageException;
|
|||
import at.bitfire.ical4android.DateUtils;
|
||||
import at.bitfire.vcard4android.ContactsStorageException;
|
||||
import lombok.Cleanup;
|
||||
import lombok.NonNull;
|
||||
|
||||
public class LocalCalendar extends AndroidCalendar implements LocalCollection {
|
||||
|
||||
|
|
|
@ -23,5 +23,4 @@ public interface LocalCollection {
|
|||
|
||||
String getCTag() throws CalendarStorageException, ContactsStorageException;
|
||||
void setCTag(String cTag) throws CalendarStorageException, ContactsStorageException;
|
||||
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ import at.bitfire.ical4android.AndroidEventFactory;
|
|||
import at.bitfire.ical4android.CalendarStorageException;
|
||||
import at.bitfire.ical4android.Event;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import android.support.annotation.NonNull;
|
||||
import lombok.Setter;
|
||||
|
||||
@TargetApi(17)
|
||||
|
|
|
@ -27,7 +27,7 @@ import at.bitfire.ical4android.AndroidTaskList;
|
|||
import at.bitfire.ical4android.CalendarStorageException;
|
||||
import at.bitfire.ical4android.Task;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import android.support.annotation.NonNull;
|
||||
import lombok.Setter;
|
||||
|
||||
public class LocalTask extends AndroidTask implements LocalResource {
|
||||
|
|
|
@ -26,7 +26,7 @@ import at.bitfire.ical4android.AndroidTaskListFactory;
|
|||
import at.bitfire.ical4android.CalendarStorageException;
|
||||
import at.bitfire.ical4android.TaskProvider;
|
||||
import lombok.Cleanup;
|
||||
import lombok.NonNull;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
public class LocalTaskList extends AndroidTaskList implements LocalCollection {
|
||||
|
||||
|
|
|
@ -35,12 +35,12 @@ import lombok.Cleanup;
|
|||
|
||||
public class CalendarsSyncAdapterService extends Service {
|
||||
private static SyncAdapter syncAdapter;
|
||||
OpenHelper dbHelper;
|
||||
private OpenHelper dbHelper;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
dbHelper = new OpenHelper(this);
|
||||
syncAdapter = new SyncAdapter(this, dbHelper);
|
||||
syncAdapter = new SyncAdapter(this, dbHelper.getReadableDatabase());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -55,14 +55,11 @@ public class CalendarsSyncAdapterService extends Service {
|
|||
|
||||
|
||||
private static class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
private final OpenHelper dbHelper;
|
||||
private final SQLiteDatabase db;
|
||||
|
||||
public SyncAdapter(Context context, OpenHelper dbHelper) {
|
||||
public SyncAdapter(Context context, SQLiteDatabase db) {
|
||||
super(context, false);
|
||||
|
||||
this.dbHelper = dbHelper;
|
||||
db = dbHelper.getReadableDatabase();
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,8 +76,6 @@ public class CalendarsSyncAdapterService extends Service {
|
|||
}
|
||||
} catch (CalendarStorageException e) {
|
||||
Constants.log.error("Couldn't enumerate local calendars", e);
|
||||
} finally {
|
||||
dbHelper.close();
|
||||
}
|
||||
|
||||
Constants.log.info("Calendar sync complete");
|
||||
|
|
|
@ -11,26 +11,37 @@ import android.accounts.Account;
|
|||
import android.app.Service;
|
||||
import android.content.AbstractThreadedSyncAdapter;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SyncResult;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import at.bitfire.davdroid.Constants;
|
||||
import at.bitfire.davdroid.model.CollectionInfo;
|
||||
import at.bitfire.davdroid.model.ServiceDB;
|
||||
import at.bitfire.davdroid.model.ServiceDB.Collections;
|
||||
import at.bitfire.davdroid.model.ServiceDB.OpenHelper;
|
||||
import lombok.Cleanup;
|
||||
|
||||
public class ContactsSyncAdapterService extends Service {
|
||||
private static ContactsSyncAdapter syncAdapter;
|
||||
private OpenHelper dbHelper;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
if (syncAdapter == null)
|
||||
syncAdapter = new ContactsSyncAdapter(getApplicationContext());
|
||||
dbHelper = new OpenHelper(this);
|
||||
syncAdapter = new ContactsSyncAdapter(this, dbHelper.getReadableDatabase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
syncAdapter = null;
|
||||
dbHelper.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -40,21 +51,47 @@ public class ContactsSyncAdapterService extends Service {
|
|||
|
||||
|
||||
private static class ContactsSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
public ContactsSyncAdapter(Context context) {
|
||||
private final SQLiteDatabase db;
|
||||
|
||||
public ContactsSyncAdapter(Context context, @NonNull SQLiteDatabase db) {
|
||||
super(context, false);
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
|
||||
Constants.log.info("Starting address book sync (" + authority + ")");
|
||||
|
||||
ContactsSyncManager syncManager = new ContactsSyncManager(getContext(), account, extras, authority, provider, syncResult);
|
||||
syncManager.performSync();
|
||||
long service = getService(account);
|
||||
CollectionInfo remote = remoteAddressBook(service);
|
||||
|
||||
if (remote != null) {
|
||||
ContactsSyncManager syncManager = new ContactsSyncManager(getContext(), account, extras, authority, provider, syncResult, remote);
|
||||
syncManager.performSync();
|
||||
} else
|
||||
Constants.log.info("No address book collection selected for synchronization");
|
||||
|
||||
Constants.log.info("Address book sync complete");
|
||||
}
|
||||
|
||||
private long getService(@NonNull Account account) {
|
||||
@Cleanup Cursor c = db.query(ServiceDB.Services._TABLE, new String[] { ServiceDB.Services.ID },
|
||||
ServiceDB.Services.ACCOUNT_NAME + "=? AND " + ServiceDB.Services.SERVICE + "=?", new String[] { account.name, ServiceDB.Services.SERVICE_CARDDAV }, null, null, null);
|
||||
c.moveToNext();
|
||||
return c.getLong(0);
|
||||
}
|
||||
|
||||
private CollectionInfo remoteAddressBook(long service) {
|
||||
@Cleanup Cursor c = db.query(Collections._TABLE, Collections._COLUMNS,
|
||||
Collections.SERVICE_ID + "=? AND selected", new String[] { String.valueOf(service) }, null, null, null);
|
||||
if (c.moveToNext()) {
|
||||
ContentValues values = new ContentValues();
|
||||
DatabaseUtils.cursorRowToContentValues(c, values);
|
||||
return CollectionInfo.fromDB(values);
|
||||
} else
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ import android.content.Context;
|
|||
import android.content.SyncResult;
|
||||
import android.os.Bundle;
|
||||
|
||||
import at.bitfire.davdroid.model.CollectionInfo;
|
||||
import at.bitfire.ical4android.CalendarStorageException;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
@ -60,13 +62,15 @@ import lombok.RequiredArgsConstructor;
|
|||
public class ContactsSyncManager extends SyncManager {
|
||||
protected static final int MAX_MULTIGET = 10;
|
||||
|
||||
final protected ContentProviderClient provider;
|
||||
protected boolean hasVCard4;
|
||||
final private ContentProviderClient provider;
|
||||
final private CollectionInfo remote;
|
||||
private boolean hasVCard4;
|
||||
|
||||
|
||||
public ContactsSyncManager(Context context, Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult result) {
|
||||
public ContactsSyncManager(Context context, Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult result, CollectionInfo remote) {
|
||||
super(Constants.NOTIFICATION_CONTACTS_SYNC, context, account, extras, authority, result);
|
||||
this.provider = provider;
|
||||
this.remote = remote;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -80,9 +84,13 @@ public class ContactsSyncManager extends SyncManager {
|
|||
// prepare local address book
|
||||
localCollection = new LocalAddressBook(account, provider);
|
||||
|
||||
String url = localAddressBook().getURL();
|
||||
if (url == null)
|
||||
throw new ContactsStorageException("Couldn't get address book URL");
|
||||
String url = remote.url;
|
||||
String lastUrl = localAddressBook().getURL();
|
||||
if (!url.equals(lastUrl)) {
|
||||
Constants.log.info("Selected address book has changed from {} to {}, deleting all local contacts", lastUrl, url);
|
||||
((LocalAddressBook)localCollection).deleteAll();
|
||||
}
|
||||
|
||||
collectionURL = HttpUrl.parse(url);
|
||||
davCollection = new DavAddressBook(log, httpClient, collectionURL);
|
||||
|
||||
|
@ -204,6 +212,12 @@ public class ContactsSyncManager extends SyncManager {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveSyncState() throws CalendarStorageException, ContactsStorageException {
|
||||
super.saveSyncState();
|
||||
((LocalAddressBook)localCollection).setURL(remote.url);
|
||||
}
|
||||
|
||||
|
||||
// helpers
|
||||
|
||||
|
|
|
@ -34,12 +34,12 @@ import lombok.Cleanup;
|
|||
|
||||
public class TasksSyncAdapterService extends Service {
|
||||
private static SyncAdapter syncAdapter;
|
||||
OpenHelper dbHelper;
|
||||
private OpenHelper dbHelper;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
dbHelper = new OpenHelper(this);
|
||||
syncAdapter = new SyncAdapter(this, dbHelper);
|
||||
syncAdapter = new SyncAdapter(this, dbHelper.getReadableDatabase());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,14 +54,11 @@ public class TasksSyncAdapterService extends Service {
|
|||
|
||||
|
||||
private static class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
private final OpenHelper dbHelper;
|
||||
private final SQLiteDatabase db;
|
||||
|
||||
public SyncAdapter(Context context, OpenHelper dbHelper) {
|
||||
public SyncAdapter(Context context, SQLiteDatabase db) {
|
||||
super(context, false);
|
||||
|
||||
this.dbHelper = dbHelper;
|
||||
db = dbHelper.getReadableDatabase();
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,8 +31,7 @@ import android.os.Build;
|
|||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.view.menu.MenuBuilder;
|
||||
import android.support.v7.view.menu.MenuPresenter;
|
||||
import android.support.v7.widget.AppCompatRadioButton;
|
||||
import android.support.v7.widget.CardView;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.TextUtils;
|
||||
|
@ -41,9 +40,10 @@ import android.view.Menu;
|
|||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
@ -55,6 +55,7 @@ import java.util.List;
|
|||
import at.bitfire.davdroid.Constants;
|
||||
import at.bitfire.davdroid.DavService;
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.log.StringLogger;
|
||||
import at.bitfire.davdroid.model.CollectionInfo;
|
||||
import at.bitfire.davdroid.model.ServiceDB.Collections;
|
||||
import at.bitfire.davdroid.model.ServiceDB.OpenHelper;
|
||||
|
@ -105,6 +106,9 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
|||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.settings:
|
||||
// TODO
|
||||
break;
|
||||
case R.id.delete_account:
|
||||
new AlertDialog.Builder(AccountActivity.this)
|
||||
.setIcon(R.drawable.ic_error_dark)
|
||||
|
@ -161,6 +165,47 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
|||
}
|
||||
}
|
||||
|
||||
private AdapterView.OnItemClickListener onItemClickListener = new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
final ListView list = (ListView)parent;
|
||||
final ArrayAdapter<CollectionInfo> adapter = (ArrayAdapter)list.getAdapter();
|
||||
final CollectionInfo info = adapter.getItem(position);
|
||||
|
||||
boolean nowChecked = !info.selected;
|
||||
|
||||
if (list.getChoiceMode() == AbsListView.CHOICE_MODE_SINGLE)
|
||||
// clear all other checked items
|
||||
for (int i = adapter.getCount()-1; i >= 0; i--)
|
||||
adapter.getItem(i).selected = false;
|
||||
|
||||
OpenHelper dbHelper = new OpenHelper(AccountActivity.this);
|
||||
try {
|
||||
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||
db.beginTransaction();
|
||||
|
||||
if (list.getChoiceMode() == AbsListView.CHOICE_MODE_SINGLE) {
|
||||
// disable all other collections
|
||||
ContentValues values = new ContentValues(1);
|
||||
values.put(Collections.SELECTED, 0);
|
||||
db.update(Collections._TABLE, values, Collections.SERVICE_ID + "=?", new String[] { String.valueOf(info.serviceID) });
|
||||
}
|
||||
|
||||
ContentValues values = new ContentValues(1);
|
||||
values.put(Collections.SELECTED, nowChecked ? 1 : 0);
|
||||
db.update(Collections._TABLE, values, Collections.ID + "=?", new String[] { String.valueOf(info.id) });
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
|
||||
info.selected = nowChecked;
|
||||
adapter.notifyDataSetChanged();
|
||||
} finally {
|
||||
dbHelper.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public Loader<AccountInfo> onCreateLoader(int id, Bundle args) {
|
||||
return new AccountLoader(this, args.getString(EXTRA_ACCOUNT_NAME));
|
||||
|
@ -177,9 +222,12 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
|||
|
||||
ListView list = (ListView)findViewById(R.id.address_books);
|
||||
list.setEnabled(!info.carddav.refreshing);
|
||||
list.setAlpha(info.carddav.refreshing ? 0.5f : 1);
|
||||
|
||||
AddressBookAdapter adapter = new AddressBookAdapter(this);
|
||||
adapter.addAll(info.carddav.collections);
|
||||
list.setAdapter(adapter);
|
||||
list.setOnItemClickListener(onItemClickListener);
|
||||
} else
|
||||
card.setVisibility(View.GONE);
|
||||
|
||||
|
@ -188,11 +236,14 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
|||
ProgressBar progress = (ProgressBar)findViewById(R.id.caldav_refreshing);
|
||||
progress.setVisibility(info.caldav.refreshing ? View.VISIBLE : View.GONE);
|
||||
|
||||
ListView list = (ListView)findViewById(R.id.calendars);
|
||||
final ListView list = (ListView)findViewById(R.id.calendars);
|
||||
list.setEnabled(!info.caldav.refreshing);
|
||||
CalendarAdapter adapter = new CalendarAdapter(this);
|
||||
list.setAlpha(info.caldav.refreshing ? 0.5f : 1);
|
||||
|
||||
final CalendarAdapter adapter = new CalendarAdapter(this);
|
||||
adapter.addAll(info.caldav.collections);
|
||||
list.setAdapter(adapter);
|
||||
list.setOnItemClickListener(onItemClickListener);
|
||||
} else
|
||||
card.setVisibility(View.GONE);
|
||||
}
|
||||
|
@ -291,15 +342,18 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
|||
|
||||
public static class AddressBookAdapter extends ArrayAdapter<CollectionInfo> {
|
||||
public AddressBookAdapter(Context context) {
|
||||
super(context, R.layout.account_address_book_item);
|
||||
super(context, R.layout.account_carddav_item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View v, ViewGroup parent) {
|
||||
if (v == null)
|
||||
v = LayoutInflater.from(getContext()).inflate(R.layout.account_address_book_item, parent, false);
|
||||
v = LayoutInflater.from(getContext()).inflate(R.layout.account_carddav_item, parent, false);
|
||||
|
||||
CollectionInfo info = getItem(position);
|
||||
final CollectionInfo info = getItem(position);
|
||||
|
||||
AppCompatRadioButton checked = (AppCompatRadioButton)v.findViewById(R.id.checked);
|
||||
checked.setChecked(info.selected);
|
||||
|
||||
TextView tv = (TextView)v.findViewById(R.id.title);
|
||||
tv.setText(TextUtils.isEmpty(info.displayName) ? info.url : info.displayName);
|
||||
|
@ -318,32 +372,18 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
|||
|
||||
public static class CalendarAdapter extends ArrayAdapter<CollectionInfo> {
|
||||
public CalendarAdapter(Context context) {
|
||||
super(context, R.layout.account_calendar_item);
|
||||
super(context, R.layout.account_caldav_item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View v, ViewGroup parent) {
|
||||
public View getView(final int position, View v, ViewGroup parent) {
|
||||
if (v == null)
|
||||
v = LayoutInflater.from(getContext()).inflate(R.layout.account_calendar_item, parent, false);
|
||||
v = LayoutInflater.from(getContext()).inflate(R.layout.account_caldav_item, parent, false);
|
||||
|
||||
final CollectionInfo info = getItem(position);
|
||||
|
||||
CheckBox select = (CheckBox)v.findViewById(R.id.selected);
|
||||
select.setChecked(info.selected);
|
||||
select.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
OpenHelper dbHelper = new OpenHelper(getContext());
|
||||
try {
|
||||
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||
ContentValues values = new ContentValues(1);
|
||||
values.put(Collections.SELECTED, isChecked ? 1 : 0);
|
||||
db.update(Collections._TABLE, values, Collections.ID + "=?", new String[]{String.valueOf(info.id)});
|
||||
} finally {
|
||||
dbHelper.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
CheckBox checked = (CheckBox)v.findViewById(R.id.checked);
|
||||
checked.setChecked(info.selected);
|
||||
|
||||
if (info.color != null) {
|
||||
View vColor = v.findViewById(R.id.color);
|
||||
|
|
|
@ -48,7 +48,7 @@ import at.bitfire.davdroid.log.StringLogger;
|
|||
import at.bitfire.davdroid.model.CollectionInfo;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import android.support.annotation.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import okhttp3.HttpUrl;
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 116 KiB |
|
@ -16,9 +16,12 @@
|
|||
android:gravity="center_vertical">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/selected"
|
||||
android:id="@+id/checked"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:clickable="false"
|
||||
android:layout_marginRight="4dp"/>
|
||||
|
||||
<View
|
||||
|
@ -26,7 +29,7 @@
|
|||
android:layout_width="32dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginRight="4dp"
|
||||
tools:background="@color/davdroid_green_dark"/>
|
||||
tools:background="@color/green700"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
|
@ -18,7 +18,10 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="4dp"
|
||||
android:id="@+id/checkBox"/>
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:clickable="false"
|
||||
android:id="@+id/checked"/>
|
||||
|
||||
<LinearLayout android:layout_width="match_parent"
|
||||
android:layout_weight="1"
|
|
@ -7,16 +7,10 @@
|
|||
~ http://www.gnu.org/licenses/gpl.html
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:src="@drawable/sky_birds"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentTop="true" />
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ListView
|
||||
android:id="@id/android:list"
|
||||
|
@ -36,4 +30,4 @@
|
|||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||
android:text="@string/account_list_empty" />
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
|
|
@ -27,15 +27,14 @@
|
|||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay"/>
|
||||
/>
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
@ -41,7 +40,7 @@
|
|||
|
||||
<ProgressBar
|
||||
android:id="@+id/carddav_refreshing"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
|
@ -79,17 +78,18 @@
|
|||
|
||||
<ProgressBar
|
||||
android:id="@+id/caldav_refreshing"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:indeterminate="true"/>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/calendars"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:choiceMode="multipleChoice"
|
||||
android:id="@+id/calendars"/>
|
||||
android:descendantFocusability="beforeDescendants"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
@ -7,7 +7,13 @@
|
|||
~ http://www.gnu.org/licenses/gpl.html
|
||||
-->
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item android:id="@+id/settings"
|
||||
android:icon="@drawable/ic_settings_dark"
|
||||
android:title="@string/account_settings"
|
||||
app:showAsAction="always"/>
|
||||
|
||||
<item android:id="@+id/delete_account"
|
||||
android:title="@string/account_delete"/>
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
<string name="account_list_empty">Welcome to DAVdroid!\n\nYou can add a CalDAV/CardDAV account now.</string>
|
||||
|
||||
<!-- AccountActivity -->
|
||||
<string name="account_settings">Account settings</string>
|
||||
<string name="account_delete">Delete account</string>
|
||||
<string name="account_delete_confirmation_title">Really delete account?</string>
|
||||
<string name="account_delete_confirmation_text">All local copies of address books, calendars and task lists will be deleted.</string>
|
||||
|
|
|
@ -10,20 +10,29 @@
|
|||
|
||||
<!-- colors -->
|
||||
|
||||
<color name="davdroid_green">#58a434</color>
|
||||
<color name="davdroid_green_dark">#407826</color>
|
||||
<color name="davdroid_lightblue">#00a8e6</color>
|
||||
<color name="light_blue_600">#039be5</color>
|
||||
|
||||
<color name="green500">#4caf50</color>
|
||||
<color name="green700">#388e3c</color>
|
||||
|
||||
<color name="light_green300">#aed581</color>
|
||||
<color name="light_green500">#8bc34a</color>
|
||||
<color name="light_green700">#689f38</color>
|
||||
|
||||
<color name="orangeA700">#ff6d00</color>
|
||||
|
||||
<color name="very_light_grey">#ebebeb</color>
|
||||
|
||||
<color name="black">#000000</color>
|
||||
<color name="white">#ffffff</color>
|
||||
|
||||
|
||||
<!-- app theme -->
|
||||
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<item name="colorPrimary">@color/davdroid_green</item>
|
||||
<item name="colorPrimaryDark">@color/davdroid_green_dark</item>
|
||||
<item name="colorAccent">@color/davdroid_lightblue</item>
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light">
|
||||
<item name="colorPrimary">@color/light_green500</item>
|
||||
<item name="colorPrimaryDark">@color/light_green700</item>
|
||||
<item name="colorAccent">@color/orangeA700</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionBar">
|
||||
|
@ -31,8 +40,8 @@
|
|||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
|
||||
<!-- <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/> -->
|
||||
|
||||
|
||||
<!-- AddAccountActivity -->
|
||||
|
@ -76,17 +85,17 @@
|
|||
<item name="android:textColorSecondary">@color/white</item>
|
||||
</style>
|
||||
<style name="toolbar_style" parent="Widget.AppCompat.Toolbar">
|
||||
<item name="android:background">@color/davdroid_lightblue</item>
|
||||
<item name="android:background">@color/light_green700</item>
|
||||
<item name="titleTextColor">@color/white</item>
|
||||
</style>
|
||||
|
||||
|
||||
<!-- text content -->
|
||||
|
||||
<style name="TextView.Heading" parent="AppTheme">
|
||||
<!-- <style name="TextView.Heading" parent="AppTheme">
|
||||
<item name="android:padding">5dp</item>
|
||||
<item name="android:background">#7ca42b</item>
|
||||
<item name="android:textColor">#ffffff</item>
|
||||
</style>
|
||||
<item name="android:textColor">@color/white</item>
|
||||
</style> -->
|
||||
|
||||
</resources>
|
Loading…
Reference in a new issue