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:
Ricki Hirner 2016-01-23 00:04:48 +01:00
parent 6f98fdb76e
commit a10649d818
24 changed files with 258 additions and 126 deletions

View file

@ -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()) {

View file

@ -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;

View file

@ -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);

View file

@ -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

View file

@ -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 {

View file

@ -23,5 +23,4 @@ public interface LocalCollection {
String getCTag() throws CalendarStorageException, ContactsStorageException;
void setCTag(String cTag) throws CalendarStorageException, ContactsStorageException;
}

View file

@ -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)

View file

@ -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 {

View file

@ -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 {

View file

@ -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");

View file

@ -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;
}
}
}

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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"

View file

@ -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"

View file

@ -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>

View file

@ -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

View file

@ -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>

View file

@ -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"/>

View file

@ -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>

View file

@ -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>