Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c27443d9 authored by Ricki Hirner's avatar Ricki Hirner
Browse files

Contact synchronization logic

* use VERSION_CODE and buildTime from BuildConfig
* new HTTP User-Agent, VCard PRODID values
* contact sync: store CTag in SyncState
* sync logic: upload contacts, check CTag, multiget
parent 652f9884
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -17,6 +17,11 @@ android {
        applicationId "at.bitfire.davdroid"
        minSdkVersion 14
        targetSdkVersion 23

        versionCode 73
        versionName "0.9-alpha1"

        buildConfigField "java.util.Date", "buildTime", "new java.util.Date()"
    }

    buildTypes {
+0 −1
Original line number Diff line number Diff line
@@ -9,7 +9,6 @@

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="at.bitfire.davdroid"
    android:versionCode="72" android:versionName="0.8.4.1"
    android:installLocation="internalOnly">

    <uses-permission android:name="android.permission.INTERNET" />
+1 −2
Original line number Diff line number Diff line
@@ -14,13 +14,12 @@ import org.slf4j.LoggerFactory;

public class Constants {
	public static final String
		APP_VERSION = "0.8.4.1",
		ACCOUNT_TYPE = "bitfire.at.davdroid",
		WEB_URL_MAIN = "https://davdroid.bitfire.at/?pk_campaign=davdroid-app",
		WEB_URL_HELP = "https://davdroid.bitfire.at/configuration?pk_campaign=davdroid-app",
		WEB_URL_VIEW_LOGS = "https://github.com/bitfireAT/davdroid/wiki/How-to-view-the-logs";

	public static final ProdId ICAL_PRODID = new ProdId("-//bitfire web engineering//DAVdroid " + Constants.APP_VERSION + " (ical4j 2.0-beta1)//EN");
	public static final ProdId ICAL_PRODID = new ProdId("-//bitfire web engineering//DAVdroid " + BuildConfig.VERSION_CODE + " (ical4j 2.0-beta1)//EN");

    public static final Logger log = LoggerFactory.getLogger("DAVdroid");
}
+40 −16
Original line number Diff line number Diff line
@@ -8,6 +8,8 @@

package at.bitfire.davdroid;

import android.os.Build;

import com.squareup.okhttp.Authenticator;
import com.squareup.okhttp.Credentials;
import com.squareup.okhttp.Interceptor;
@@ -18,6 +20,7 @@ import com.squareup.okhttp.logging.HttpLoggingInterceptor;

import java.io.IOException;
import java.net.Proxy;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.concurrent.TimeUnit;

@@ -26,8 +29,24 @@ import lombok.RequiredArgsConstructor;

public class HttpClient extends OkHttpClient {

    protected static final String
            HEADER_AUTHORIZATION = "Authorization";
    protected static final String HEADER_AUTHORIZATION = "Authorization";

    final static UserAgentInterceptor userAgentInterceptor = new UserAgentInterceptor();
    final static HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
        @Override
        public void log(String message) {
            Constants.log.trace(message);
        }
    });
    static {
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
    }

    static final String userAgent;
    static {
        String date = new SimpleDateFormat("yyyy/MM/dd").format(BuildConfig.buildTime);
        userAgent = "DAVdroid/" + BuildConfig.VERSION_NAME + " (" + date + "; dav4android) Android/" + Build.VERSION.RELEASE;
    }


    public HttpClient() {
@@ -40,13 +59,13 @@ public class HttpClient extends OkHttpClient {
        super();
        initialize();

        // authentication and User-Agent
        enableLogs();

        // authentication
        if (preemptive)
            networkInterceptors().add(new PreemptiveAuthenticationInterceptor(username, password));
        else
            setAuthenticator(new DavAuthenticator(username, password));

        enableLogs();
    }


@@ -54,21 +73,27 @@ public class HttpClient extends OkHttpClient {
        // don't follow redirects automatically because this may rewrite DAV methods to GET
        setFollowRedirects(false);

        // timeouts
        setConnectTimeout(20, TimeUnit.SECONDS);
        setWriteTimeout(15, TimeUnit.SECONDS);
        setReadTimeout(45, TimeUnit.SECONDS);

        // add User-Agent to every request
        networkInterceptors().add(userAgentInterceptor);
    }

    protected void enableLogs() {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
        interceptors().add(loggingInterceptor);
    }


    static class UserAgentInterceptor implements Interceptor {
        @Override
            public void log(String message) {
                at.bitfire.dav4android.Constants.log.trace(message);
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request().newBuilder()
                    .header("User-Agent", userAgent)
                    .build();
            return chain.proceed(request);
        }
        });
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        interceptors().add(logging);
    }


@@ -78,8 +103,7 @@ public class HttpClient extends OkHttpClient {

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            request = request.newBuilder()
            Request request = chain.request().newBuilder()
                    .header("Authorization", Credentials.basic(username, password))
                    .build();
            return chain.proceed(request);
+46 −2
Original line number Diff line number Diff line
@@ -9,6 +9,8 @@ package at.bitfire.davdroid.resource;

import android.accounts.Account;
import android.content.ContentProviderClient;
import android.os.Bundle;
import android.os.Parcel;
import android.provider.ContactsContract;

import at.bitfire.davdroid.Constants;
@@ -18,14 +20,25 @@ import at.bitfire.vcard4android.AndroidContactFactory;
import at.bitfire.vcard4android.AndroidGroupFactory;
import at.bitfire.vcard4android.Contact;
import at.bitfire.vcard4android.ContactsStorageException;
import lombok.Cleanup;
import lombok.Synchronized;


public class LocalAddressBook extends AndroidAddressBook {

    protected static final String SYNC_STATE_CTAG = "ctag";

    private Bundle syncState = new Bundle();


    public LocalAddressBook(Account account, ContentProviderClient provider) {
        super(account, provider, AndroidGroupFactory.INSTANCE, LocalContact.Factory.INSTANCE);
    }


    /**
     * Returns an array of local contacts, excluding those which have been modified locally (and not uploaded yet).
     */
    public LocalContact[] getAll() throws ContactsStorageException {
        LocalContact contacts[] = (LocalContact[])queryContacts(null, null);
        return contacts;
@@ -52,4 +65,35 @@ public class LocalAddressBook extends AndroidAddressBook {
        return (LocalContact[])queryContacts(AndroidContact.COLUMN_FILENAME + " IS NULL", null);
    }


    protected void readSyncState() throws ContactsStorageException {
        @Cleanup("recycle") Parcel parcel = Parcel.obtain();
        byte[] raw = getSyncState();
        if (raw != null) {
            parcel.unmarshall(raw, 0, raw.length);
            parcel.setDataPosition(0);
            syncState = parcel.readBundle();
        } else
            syncState.clear();
    }

    public String getCTag() throws ContactsStorageException {
        synchronized (syncState) {
            readSyncState();
            return syncState.getString(SYNC_STATE_CTAG);
        }
    }

    public void setCTag(String cTag) throws ContactsStorageException {
        synchronized (syncState) {
            readSyncState();
            syncState.putString(SYNC_STATE_CTAG, cTag);

            // write sync state bundle
            @Cleanup("recycle") Parcel parcel = Parcel.obtain();
            parcel.writeBundle(syncState);
            setSyncState(parcel.marshall());
        }
    }

}
Loading