mirror of
https://github.com/bitfireAT/davx5-ose
synced 2024-09-06 18:01:11 +00:00
DavService: refresh collections
This commit is contained in:
parent
c1a8cab84e
commit
671a11e479
|
@ -49,7 +49,6 @@
|
|||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme"
|
||||
tools:ignore="UnusedAttribute">
|
||||
|
||||
<service
|
||||
android:name=".syncadapter.AccountAuthenticatorService"
|
||||
android:exported="false">
|
||||
|
@ -103,11 +102,18 @@
|
|||
android:name="android.content.SyncAdapter"
|
||||
android:resource="@xml/sync_tasks"/>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".DavService"
|
||||
android:enabled="true">
|
||||
</service>
|
||||
<receiver
|
||||
android:name=".AccountsChangedReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<activity
|
||||
android:name=".ui.AccountsActivity"
|
||||
|
@ -123,6 +129,8 @@
|
|||
android:name=".ui.AccountActivity"
|
||||
android:parentActivityName=".ui.AccountsActivity">
|
||||
</activity>
|
||||
<activity android:name=".ui.CreateAddressBookActivity">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.DebugInfoActivity"
|
||||
android:exported="true"
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
public class AccountsChangedReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Intent serviceIntent = new Intent(context, DavService.class);
|
||||
serviceIntent.setAction(DavService.ACTION_ACCOUNTS_UPDATED);
|
||||
context.startService(serviceIntent);
|
||||
}
|
||||
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
package at.bitfire.davdroid;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.app.Service;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
|
@ -18,6 +19,7 @@ import android.database.sqlite.SQLiteDatabase;
|
|||
import android.database.sqlite.SQLiteException;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
|
@ -50,6 +52,7 @@ import okhttp3.OkHttpClient;
|
|||
public class DavService extends Service {
|
||||
|
||||
public static final String
|
||||
ACTION_ACCOUNTS_UPDATED = "accountsUpdated",
|
||||
ACTION_REFRESH_COLLECTIONS = "refreshCollections",
|
||||
EXTRA_DAV_SERVICE_ID = "davServiceID";
|
||||
|
||||
|
@ -65,6 +68,9 @@ public class DavService extends Service {
|
|||
long id = intent.getLongExtra(EXTRA_DAV_SERVICE_ID, -1);
|
||||
|
||||
switch (action) {
|
||||
case ACTION_ACCOUNTS_UPDATED:
|
||||
cleanupAccounts();
|
||||
break;
|
||||
case ACTION_REFRESH_COLLECTIONS:
|
||||
if (runningRefresh.add(id)) {
|
||||
new Thread(new RefreshCollections(id)).start();
|
||||
|
@ -113,6 +119,27 @@ public class DavService extends Service {
|
|||
which actually do the work
|
||||
*/
|
||||
|
||||
void cleanupAccounts() {
|
||||
Constants.log.info("[DavService] Cleaning up orphaned accounts");
|
||||
|
||||
final OpenHelper dbHelper = new OpenHelper(this);
|
||||
try {
|
||||
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||
|
||||
List<String> sqlAccountNames = new LinkedList<>();
|
||||
AccountManager am = AccountManager.get(this);
|
||||
for (Account account : am.getAccountsByType(Constants.ACCOUNT_TYPE))
|
||||
sqlAccountNames.add(DatabaseUtils.sqlEscapeString(account.name));
|
||||
|
||||
if (sqlAccountNames.isEmpty())
|
||||
db.delete(Services._TABLE, null, null);
|
||||
else
|
||||
db.delete(Services._TABLE, Services.ACCOUNT_NAME + " NOT IN (" + TextUtils.join(",", sqlAccountNames) + ")", null);
|
||||
} finally {
|
||||
dbHelper.close();
|
||||
}
|
||||
}
|
||||
|
||||
private class RefreshCollections implements Runnable {
|
||||
final long service;
|
||||
final OpenHelper dbHelper;
|
||||
|
@ -127,7 +154,6 @@ public class DavService extends Service {
|
|||
public void run() {
|
||||
try {
|
||||
db = dbHelper.getWritableDatabase();
|
||||
db.beginTransactionNonExclusive();
|
||||
|
||||
String serviceType = serviceType();
|
||||
Constants.log.info("[DavService {}] Refreshing {} collections", service, serviceType);
|
||||
|
@ -155,7 +181,6 @@ public class DavService extends Service {
|
|||
homeSets.add(UrlUtils.withTrailingSlash(dav.location.resolve(href)));
|
||||
}
|
||||
}
|
||||
saveHomeSets(homeSets);
|
||||
|
||||
// refresh collections in home sets
|
||||
Map<HttpUrl, CollectionInfo> collections = readCollections();
|
||||
|
@ -203,13 +228,19 @@ public class DavService extends Service {
|
|||
iterator.remove();
|
||||
}
|
||||
}
|
||||
saveCollections(collections.values());
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} catch (SQLiteException | IOException|HttpException|DavException e) {
|
||||
try {
|
||||
db.beginTransaction();
|
||||
saveHomeSets(homeSets);
|
||||
saveCollections(collections.values());
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
} catch (IOException|HttpException|DavException e) {
|
||||
Constants.log.error("Couldn't refresh collection list", e);
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
dbHelper.close();
|
||||
|
||||
runningRefresh.remove(service);
|
||||
|
|
|
@ -18,10 +18,11 @@ import at.bitfire.dav4android.property.CalendarDescription;
|
|||
import at.bitfire.dav4android.property.CurrentUserPrivilegeSet;
|
||||
import at.bitfire.dav4android.property.DisplayName;
|
||||
import at.bitfire.dav4android.property.ResourceType;
|
||||
import at.bitfire.dav4android.property.SupportedAddressData;
|
||||
import at.bitfire.dav4android.property.SupportedCalendarComponentSet;
|
||||
import lombok.ToString;
|
||||
import okhttp3.HttpUrl;
|
||||
import at.bitfire.davdroid.model.ServiceDB.*;
|
||||
import okhttp3.MediaType;
|
||||
|
||||
@ToString
|
||||
public class CollectionInfo {
|
||||
|
@ -39,6 +40,8 @@ public class CollectionInfo {
|
|||
public String displayName, description;
|
||||
public Integer color;
|
||||
|
||||
public Integer vCardVersion;
|
||||
|
||||
public Boolean supportsVEVENT;
|
||||
public Boolean supportsVTODO;
|
||||
|
||||
|
@ -50,8 +53,8 @@ public class CollectionInfo {
|
|||
ResourceType.NAME,
|
||||
CurrentUserPrivilegeSet.NAME,
|
||||
DisplayName.NAME,
|
||||
AddressbookDescription.NAME, CalendarDescription.NAME,
|
||||
CalendarColor.NAME, SupportedCalendarComponentSet.NAME
|
||||
AddressbookDescription.NAME, SupportedAddressData.NAME,
|
||||
CalendarDescription.NAME, CalendarColor.NAME, SupportedCalendarComponentSet.NAME
|
||||
};
|
||||
|
||||
public static CollectionInfo fromDavResource(DavResource dav) {
|
||||
|
@ -80,6 +83,15 @@ public class CollectionInfo {
|
|||
if (addressbookDescription != null)
|
||||
info.description = addressbookDescription.description;
|
||||
|
||||
SupportedAddressData addressData = (SupportedAddressData)dav.properties.get(SupportedAddressData.NAME);
|
||||
if (addressData != null) {
|
||||
boolean vCard4 = false;
|
||||
for (MediaType contentType : addressData.types)
|
||||
if ("text/vcard".equals(contentType.type()) && contentType.toString().contains("version=4.0"))
|
||||
vCard4 = true;
|
||||
info.vCardVersion = vCard4 ? 4 : 3;
|
||||
}
|
||||
|
||||
} else if (info.type == Type.CALENDAR) {
|
||||
CalendarDescription calendarDescription = (CalendarDescription)dav.properties.get(CalendarDescription.NAME);
|
||||
if (calendarDescription != null)
|
||||
|
@ -107,9 +119,13 @@ public class CollectionInfo {
|
|||
info.url = values.getAsString(Collections.URL);
|
||||
info.displayName = values.getAsString(Collections.DISPLAY_NAME);
|
||||
info.description = values.getAsString(Collections.DESCRIPTION);
|
||||
|
||||
info.vCardVersion = values.getAsInteger(Collections.VCARD_VERSION);
|
||||
|
||||
info.color = values.getAsInteger(Collections.COLOR);
|
||||
info.supportsVEVENT = values.getAsBoolean(Collections.SUPPORTS_VEVENT);
|
||||
info.supportsVTODO = values.getAsBoolean(Collections.SUPPORTS_VTODO);
|
||||
|
||||
info.supportsVEVENT = booleanField(values, Collections.SUPPORTS_VEVENT);
|
||||
info.supportsVTODO = booleanField(values, Collections.SUPPORTS_VTODO);
|
||||
return info;
|
||||
}
|
||||
|
||||
|
@ -118,10 +134,23 @@ public class CollectionInfo {
|
|||
values.put(Collections.URL, url);
|
||||
values.put(Collections.DISPLAY_NAME, displayName);
|
||||
values.put(Collections.DESCRIPTION, description);
|
||||
|
||||
values.put(Collections.VCARD_VERSION, vCardVersion);
|
||||
|
||||
values.put(Collections.COLOR, color);
|
||||
values.put(Collections.SUPPORTS_VEVENT, supportsVEVENT);
|
||||
values.put(Collections.SUPPORTS_VTODO, supportsVTODO);
|
||||
if (supportsVEVENT != null)
|
||||
values.put(Collections.SUPPORTS_VEVENT, supportsVEVENT ? 1 : 0);
|
||||
if (supportsVTODO != null)
|
||||
values.put(Collections.SUPPORTS_VTODO, supportsVTODO ? 1 : 0);
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
private static Boolean booleanField(ContentValues values, String field) {
|
||||
Integer i = values.getAsInteger(field);
|
||||
if (i == null)
|
||||
return null;
|
||||
return i != 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -51,11 +51,12 @@ public class ServiceDB {
|
|||
DISPLAY_NAME = "displayName",
|
||||
DESCRIPTION = "description",
|
||||
COLOR = "color",
|
||||
VCARD_VERSION = "vCardVersion",
|
||||
SUPPORTS_VEVENT = "supportsVEVENT",
|
||||
SUPPORTS_VTODO = "supportsVTODO";
|
||||
|
||||
public static String[] _COLUMNS = new String[] {
|
||||
ID, SERVICE_ID, URL, DISPLAY_NAME, DESCRIPTION, COLOR, SUPPORTS_VEVENT, SUPPORTS_VTODO
|
||||
ID, SERVICE_ID, URL, DISPLAY_NAME, DESCRIPTION, COLOR, VCARD_VERSION, SUPPORTS_VEVENT, SUPPORTS_VTODO
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -70,6 +71,7 @@ public class ServiceDB {
|
|||
|
||||
@Override
|
||||
public void onOpen(SQLiteDatabase db) {
|
||||
db.enableWriteAheadLogging();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
|
||||
db.setForeignKeyConstraintsEnabled(true);
|
||||
else
|
||||
|
@ -103,6 +105,7 @@ public class ServiceDB {
|
|||
Collections.DISPLAY_NAME + " TEXT NULL," +
|
||||
Collections.DESCRIPTION + " TEXT NULL," +
|
||||
Collections.COLOR + " INTEGER NULL," +
|
||||
Collections.VCARD_VERSION + " INTEGER NULL," +
|
||||
Collections.SUPPORTS_VEVENT + " INTEGER NULL," +
|
||||
Collections.SUPPORTS_VTODO + " INTEGER NULL" +
|
||||
")");
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.dmfs.provider.tasks.TaskContract.Tasks;
|
|||
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import at.bitfire.davdroid.model.CollectionInfo;
|
||||
import at.bitfire.ical4android.AndroidTaskList;
|
||||
import at.bitfire.ical4android.AndroidTaskListFactory;
|
||||
import at.bitfire.ical4android.CalendarStorageException;
|
||||
|
@ -53,14 +54,10 @@ public class LocalTaskList extends AndroidTaskList implements LocalCollection {
|
|||
super(account, provider, LocalTask.Factory.INSTANCE, id);
|
||||
}
|
||||
|
||||
public static Uri create(Account account, ContentResolver resolver, ServerInfo.ResourceInfo info) throws CalendarStorageException {
|
||||
TaskProvider provider = TaskProvider.acquire(resolver, TaskProvider.ProviderName.OpenTasks);
|
||||
if (provider == null)
|
||||
throw new CalendarStorageException("Couldn't access OpenTasks provider");
|
||||
|
||||
public static Uri create(Account account, TaskProvider provider, CollectionInfo info) throws CalendarStorageException {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(TaskLists._SYNC_ID, info.getUrl());
|
||||
values.put(TaskLists.LIST_NAME, info.getTitle());
|
||||
values.put(TaskLists._SYNC_ID, info.url);
|
||||
values.put(TaskLists.LIST_NAME, info.displayName);
|
||||
values.put(TaskLists.LIST_COLOR, info.color != null ? info.color : defaultColor);
|
||||
values.put(TaskLists.OWNER, account.name);
|
||||
values.put(TaskLists.SYNC_ENABLED, 1);
|
||||
|
|
|
@ -65,7 +65,7 @@ public class AccountAuthenticatorService extends Service {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -11,13 +11,24 @@ 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 java.util.*;
|
||||
|
||||
import at.bitfire.davdroid.Constants;
|
||||
import at.bitfire.davdroid.model.CollectionInfo;
|
||||
import at.bitfire.davdroid.model.ServiceDB;
|
||||
import at.bitfire.davdroid.model.ServiceDB.*;
|
||||
import at.bitfire.davdroid.model.ServiceDB.Collections;
|
||||
import at.bitfire.davdroid.resource.LocalTask;
|
||||
import at.bitfire.davdroid.resource.LocalTaskList;
|
||||
import at.bitfire.ical4android.CalendarStorageException;
|
||||
import at.bitfire.ical4android.TaskProvider;
|
||||
|
@ -25,27 +36,34 @@ import lombok.Cleanup;
|
|||
|
||||
public class TasksSyncAdapterService extends Service {
|
||||
private static SyncAdapter syncAdapter;
|
||||
OpenHelper dbHelper;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
if (syncAdapter == null)
|
||||
syncAdapter = new SyncAdapter(getApplicationContext());
|
||||
dbHelper = new OpenHelper(this);
|
||||
syncAdapter = new SyncAdapter(this, dbHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
syncAdapter = null;
|
||||
}
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
dbHelper.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return syncAdapter.getSyncAdapterBinder();
|
||||
return syncAdapter.getSyncAdapterBinder();
|
||||
}
|
||||
|
||||
|
||||
private static class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
public SyncAdapter(Context context) {
|
||||
final OpenHelper dbHelper;
|
||||
final SQLiteDatabase db;
|
||||
|
||||
public SyncAdapter(Context context, OpenHelper dbHelper) {
|
||||
super(context, false);
|
||||
|
||||
this.dbHelper = dbHelper;
|
||||
db = dbHelper.getReadableDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -57,6 +75,8 @@ public class TasksSyncAdapterService extends Service {
|
|||
if (provider == null)
|
||||
throw new CalendarStorageException("Couldn't access OpenTasks provider");
|
||||
|
||||
syncLocalTaskLists(provider, account);
|
||||
|
||||
for (LocalTaskList taskList : (LocalTaskList[])LocalTaskList.find(account, provider, LocalTaskList.Factory.INSTANCE, null, null)) {
|
||||
Constants.log.info("Synchronizing task list #" + taskList.getId() + ", URL: " + taskList.getSyncId());
|
||||
TasksSyncManager syncManager = new TasksSyncManager(getContext(), account, extras, authority, provider, syncResult, taskList);
|
||||
|
@ -68,6 +88,54 @@ public class TasksSyncAdapterService extends Service {
|
|||
|
||||
Constants.log.info("Task sync complete");
|
||||
}
|
||||
|
||||
private void syncLocalTaskLists(TaskProvider provider, Account account) throws CalendarStorageException {
|
||||
long service = getService(account);
|
||||
|
||||
// enumerate remote and local task lists
|
||||
Map<String, CollectionInfo> remote = remoteTaskLists(service);
|
||||
LocalTaskList[] local = (LocalTaskList[])LocalTaskList.find(account, provider, LocalTaskList.Factory.INSTANCE, null, null);
|
||||
|
||||
// delete obsolete local task lists
|
||||
for (LocalTaskList list : local) {
|
||||
String url = list.getSyncId();
|
||||
Constants.log.debug("Checking local task list {} {}", list.getId(), url);
|
||||
if (!remote.containsKey(url)) {
|
||||
Constants.log.debug("Deleting local task list {}", url);
|
||||
list.delete();
|
||||
} else
|
||||
// we already have a local task list for this remote collection
|
||||
remote.remove(url);
|
||||
}
|
||||
|
||||
// create new local task lists
|
||||
for (String url : remote.keySet()) {
|
||||
CollectionInfo info = remote.get(url);
|
||||
Constants.log.info("Adding local task list {}", info);
|
||||
LocalTaskList.create(account, provider, info);
|
||||
}
|
||||
}
|
||||
|
||||
long getService(Account account) {
|
||||
@Cleanup Cursor c = db.query(Services._TABLE, new String[]{ Services.ID },
|
||||
Services.ACCOUNT_NAME + "=? AND " + Services.SERVICE + "=?", new String[]{ account.name, Services.SERVICE_CALDAV }, null, null, null);
|
||||
c.moveToNext();
|
||||
return c.getLong(0);
|
||||
}
|
||||
|
||||
private Map<String, CollectionInfo> remoteTaskLists(long service) {
|
||||
Map<String, CollectionInfo> collections = new LinkedHashMap<>();
|
||||
@Cleanup Cursor cursor = db.query(Collections._TABLE, Collections._COLUMNS,
|
||||
Collections.SERVICE_ID + "=? AND " + Collections.SUPPORTS_VTODO + "!=0",
|
||||
new String[] { String.valueOf(service) }, null, null, null);
|
||||
while (cursor.moveToNext()) {
|
||||
ContentValues values = new ContentValues();
|
||||
DatabaseUtils.cursorRowToContentValues(cursor, values);
|
||||
CollectionInfo info = CollectionInfo.fromDB(values);
|
||||
collections.put(info.url, info);
|
||||
}
|
||||
return collections;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,12 +33,17 @@ import android.os.IBinder;
|
|||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.CardView;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
|
@ -134,6 +139,9 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
|||
intent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, accountInfo.carddav.id);
|
||||
startService(intent);
|
||||
break;
|
||||
case R.id.create_address_book:
|
||||
// TODO
|
||||
break;
|
||||
case R.id.refresh_calendars:
|
||||
intent = new Intent(this, DavService.class);
|
||||
intent.setAction(DavService.ACTION_REFRESH_COLLECTIONS);
|
||||
|
@ -193,10 +201,9 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
|||
progress.setVisibility(info.carddav.refreshing ? View.VISIBLE : View.GONE);
|
||||
|
||||
ListView list = (ListView)findViewById(R.id.address_books);
|
||||
List<String> names = new LinkedList<>();
|
||||
for (CollectionInfo addrBook : info.carddav.collections)
|
||||
names.add(addrBook.displayName != null ? addrBook.displayName : addrBook.url);
|
||||
list.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_checked, android.R.id.text1, names));
|
||||
AddressBookAdapter adapter = new AddressBookAdapter(this);
|
||||
adapter.addAll(info.carddav.collections);
|
||||
list.setAdapter(adapter);
|
||||
} else
|
||||
card.setVisibility(View.GONE);
|
||||
|
||||
|
@ -206,10 +213,9 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
|||
progress.setVisibility(info.caldav.refreshing ? View.VISIBLE : View.GONE);
|
||||
|
||||
ListView list = (ListView)findViewById(R.id.calendars);
|
||||
List<String> names = new LinkedList<>();
|
||||
for (CollectionInfo calendar : info.caldav.collections)
|
||||
names.add(calendar.displayName != null ? calendar.displayName : calendar.url);
|
||||
list.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_checked, android.R.id.text1, names));
|
||||
CalendarAdapter adapter = new CalendarAdapter(this);
|
||||
adapter.addAll(info.caldav.collections);
|
||||
list.setAdapter(adapter);
|
||||
} else
|
||||
card.setVisibility(View.GONE);
|
||||
}
|
||||
|
@ -269,7 +275,6 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
|||
null, null, null);
|
||||
while (cursor.moveToNext()) {
|
||||
long id = cursor.getLong(0);
|
||||
|
||||
String service = cursor.getString(1);
|
||||
if (Services.SERVICE_CARDDAV.equals(service)) {
|
||||
info.carddav = new AccountInfo.ServiceInfo();
|
||||
|
@ -303,6 +308,69 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
|
|||
}
|
||||
|
||||
|
||||
/* LIST ADAPTERS */
|
||||
|
||||
public static class AddressBookAdapter extends ArrayAdapter<CollectionInfo> {
|
||||
public AddressBookAdapter(Context context) {
|
||||
super(context, R.layout.account_address_book_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);
|
||||
|
||||
CollectionInfo info = getItem(position);
|
||||
|
||||
TextView tv = (TextView)v.findViewById(R.id.title);
|
||||
tv.setText(TextUtils.isEmpty(info.displayName) ? info.url : info.displayName);
|
||||
|
||||
tv = (TextView)v.findViewById(R.id.description);
|
||||
if (TextUtils.isEmpty(info.description))
|
||||
tv.setVisibility(View.GONE);
|
||||
else {
|
||||
tv.setVisibility(View.VISIBLE);
|
||||
tv.setText(info.description);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CalendarAdapter extends ArrayAdapter<CollectionInfo> {
|
||||
public CalendarAdapter(Context context) {
|
||||
super(context, R.layout.account_calendar_item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View v, ViewGroup parent) {
|
||||
if (v == null)
|
||||
v = LayoutInflater.from(getContext()).inflate(R.layout.account_calendar_item, parent, false);
|
||||
|
||||
CollectionInfo info = getItem(position);
|
||||
|
||||
TextView tv = (TextView)v.findViewById(R.id.title);
|
||||
tv.setText(TextUtils.isEmpty(info.displayName) ? info.url : info.displayName);
|
||||
|
||||
tv = (TextView)v.findViewById(R.id.description);
|
||||
if (TextUtils.isEmpty(info.description))
|
||||
tv.setVisibility(View.GONE);
|
||||
else {
|
||||
tv.setVisibility(View.VISIBLE);
|
||||
tv.setText(info.description);
|
||||
}
|
||||
|
||||
tv = (TextView)v.findViewById(R.id.events);
|
||||
tv.setVisibility(info.supportsVEVENT ? View.VISIBLE : View.GONE);
|
||||
|
||||
tv = (TextView)v.findViewById(R.id.tasks);
|
||||
tv.setVisibility(info.supportsVTODO ? View.VISIBLE : View.GONE);
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* USER ACTIONS */
|
||||
|
||||
protected void deleteAccount() {
|
||||
|
|
|
@ -125,34 +125,20 @@ public class AccountListFragment extends ListFragment implements OnAccountsUpdat
|
|||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View v = convertView;
|
||||
|
||||
if (v == null) {
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
v = inflater.inflate(R.layout.account_list_item, parent, false);
|
||||
}
|
||||
public View getView(int position, View v, ViewGroup parent) {
|
||||
if (v == null)
|
||||
v = LayoutInflater.from(getContext()).inflate(R.layout.account_list_item, parent, false);
|
||||
|
||||
AccountInfo info = getItem(position);
|
||||
|
||||
TextView tv = (TextView)v.findViewById(R.id.account_name);
|
||||
tv.setText(info.account.name);
|
||||
|
||||
ProgressBar progressRefresh = (ProgressBar)v.findViewById(R.id.refreshing);
|
||||
progressRefresh.setVisibility(info.isRefreshing ? View.VISIBLE : View.GONE);
|
||||
|
||||
Animation blink = new AlphaAnimation(0, 1);
|
||||
blink.setDuration(400);
|
||||
blink.setRepeatMode(Animation.REVERSE);
|
||||
blink.setRepeatCount(Animation.INFINITE);
|
||||
|
||||
tv = (TextView)v.findViewById(R.id.carddav);
|
||||
tv.setVisibility(info.cardDavService != null ? View.VISIBLE : View.GONE);
|
||||
tv.setAnimation(info.isRefreshing ? blink : null);
|
||||
|
||||
tv = (TextView)v.findViewById(R.id.caldav);
|
||||
tv.setVisibility(info.calDavService != null ? View.VISIBLE : View.GONE);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,10 +10,13 @@ package at.bitfire.davdroid.ui.setup;
|
|||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.Bundle;
|
||||
import android.provider.CalendarContract;
|
||||
import android.provider.ContactsContract;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -30,6 +33,7 @@ import at.bitfire.davdroid.R;
|
|||
import at.bitfire.davdroid.model.CollectionInfo;
|
||||
import at.bitfire.davdroid.syncadapter.AccountSettings;
|
||||
import at.bitfire.davdroid.model.ServiceDB.*;
|
||||
import at.bitfire.ical4android.TaskProvider;
|
||||
import lombok.Cleanup;
|
||||
import okhttp3.HttpUrl;
|
||||
|
||||
|
@ -105,12 +109,22 @@ public class AccountDetailsFragment extends Fragment {
|
|||
long id = insertService(db, accountName, Services.SERVICE_CARDDAV, config.cardDAV);
|
||||
refreshIntent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, id);
|
||||
getActivity().startService(refreshIntent);
|
||||
|
||||
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
|
||||
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
|
||||
}
|
||||
|
||||
if (config.calDAV != null) {
|
||||
long id = insertService(db, accountName, Services.SERVICE_CALDAV, config.calDAV);
|
||||
refreshIntent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, id);
|
||||
getActivity().startService(refreshIntent);
|
||||
|
||||
ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 1);
|
||||
ContentResolver.setSyncAutomatically(account, CalendarContract.AUTHORITY, true);
|
||||
|
||||
// TODO check for tasks availability
|
||||
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 1);
|
||||
ContentResolver.setSyncAutomatically(account, TaskProvider.ProviderName.OpenTasks.authority, true);
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
|
|
13
app/src/main/res/drawable/ic_alarm_on_dark.xml
Normal file
13
app/src/main/res/drawable/ic_alarm_on_dark.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!--
|
||||
~ Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
~ All rights reserved. This program and the accompanying materials
|
||||
~ are made available under the terms of the GNU Public License v3.0
|
||||
~ which accompanies this distribution, and is available at
|
||||
~ http://www.gnu.org/licenses/gpl.html
|
||||
-->
|
||||
|
||||
<vector android:alpha="0.54" android:height="32dp"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm0,16c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7zm-1.46,-5.47L8.41,12.4l-1.06,1.06 3.18,3.18 6,-6 -1.06,-1.06 -4.93,4.95z"/>
|
||||
</vector>
|
13
app/src/main/res/drawable/ic_today_dark.xml
Normal file
13
app/src/main/res/drawable/ic_today_dark.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!--
|
||||
~ Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
~ All rights reserved. This program and the accompanying materials
|
||||
~ are made available under the terms of the GNU Public License v3.0
|
||||
~ which accompanies this distribution, and is available at
|
||||
~ http://www.gnu.org/licenses/gpl.html
|
||||
-->
|
||||
|
||||
<vector android:alpha="0.54" android:height="32dp"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M19,3h-1V1h-2v2H8V1H6v2H5c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V8h14v11zM7,10h5v5H7z"/>
|
||||
</vector>
|
31
app/src/main/res/layout/account_address_book_item.xml
Normal file
31
app/src/main/res/layout/account_address_book_item.xml
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
~ All rights reserved. This program and the accompanying materials
|
||||
~ are made available under the terms of the GNU Public License v3.0
|
||||
~ which accompanies this distribution, and is available at
|
||||
~ http://www.gnu.org/licenses/gpl.html
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
tools:text="My Address Book"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
tools:text="Address Book Description"/>
|
||||
|
||||
</LinearLayout>
|
51
app/src/main/res/layout/account_calendar_item.xml
Normal file
51
app/src/main/res/layout/account_calendar_item.xml
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
~ All rights reserved. This program and the accompanying materials
|
||||
~ are made available under the terms of the GNU Public License v3.0
|
||||
~ which accompanies this distribution, and is available at
|
||||
~ http://www.gnu.org/licenses/gpl.html
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
tools:text="My Calendar"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
tools:text="Calendar Description"/>
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/events"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:background="@drawable/ic_today_dark"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tasks"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:background="@drawable/ic_alarm_on_dark"/>
|
||||
|
||||
</LinearLayout>
|
|
@ -28,7 +28,7 @@
|
|||
android:gravity="right|center_vertical">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/refreshing"
|
||||
android:id="@+id/synchronizing"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"
|
||||
|
|
15
app/src/main/res/layout/activity_create_address_book.xml
Normal file
15
app/src/main/res/layout/activity_create_address_book.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||
~ All rights reserved. This program and the accompanying materials
|
||||
~ are made available under the terms of the GNU Public License v3.0
|
||||
~ which accompanies this distribution, and is available at
|
||||
~ http://www.gnu.org/licenses/gpl.html
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
</LinearLayout>
|
Loading…
Reference in a new issue