Commit f2d5ae81 authored by Ricki Hirner's avatar Ricki Hirner 🐑

Version 0.9 ready!

* fix lint warnings
* line-break too long messages of network trace logs
* DebugInfoActivity "send": attach log file instead if sending it as plain text
* revert to ez-vcard 0.9.6 because of https://github.com/mangstadt/ez-vcard/issues/33
* German translations
parent 950b5f31
......@@ -17,8 +17,8 @@ android {
minSdkVersion 14
targetSdkVersion 23
versionCode 77
versionName "0.9-beta2"
versionCode 78
versionName "0.9"
buildConfigField "java.util.Date", "buildTime", "new java.util.Date()"
}
......@@ -33,10 +33,16 @@ android {
}
}
lintOptions {
abortOnError false
disable 'ExtraTranslation'
disable 'IconColors'
disable 'IconLauncherShape'
disable 'IconMissingDensityFolder'
disable 'InconsistentLayout'
disable 'MergeRootFrame'
disable 'MissingTranslation'
disable 'RtlEnabled'
disable 'Typos'
disable 'UnusedAttribute'
}
packagingOptions {
exclude 'LICENSE'
exclude 'META-INF/LICENSE.txt'
......
......@@ -7,12 +7,6 @@
-dontobfuscate
# SimpleXML
-keep class org.simpleframework.** { *; } # keep all interfaces etc. to allow reflection
-dontwarn com.bea.xml.stream.** # StAX API not used
-dontwarn javax.xml.stream.**
# ez-vcard
-dontwarn com.fasterxml.jackson.** # Jackson JSON Processor (for jCards) not used
-dontwarn freemarker.** # freemarker templating library (for creating hCards) not used
......@@ -21,17 +15,22 @@
-keep class ezvcard.property.** { *; } # keep all VCard properties (created at runtime)
# ical4j: ignore unused dynamic libraries
-dontwarn aQute.**
-dontwarn groovy.** # Groovy-based ContentBuilder not used
-dontwarn org.codehaus.groovy.**
-dontwarn org.apache.commons.logging.** # Commons logging is not available
-dontwarn net.fortuna.ical4j.model.** # ignore warnings from Groovy dependency
-keep class net.fortuna.ical4j.model.** { *; } # keep all model classes (properties/factories, created at runtime)
# okhttp
-dontwarn java.nio.file.** # not available on Android
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
# MemorizingTrustManager
-dontwarn de.duenndns.ssl.MemorizingTrustManager
# dnsjava
-dontwarn sun.net.spi.nameservice.** # not available on Android
# DAVdroid
-keep class at.bitfire.davdroid.** { *; } # all DAVdroid code is required
# unneeded libraries
-dontwarn aQute.**
# DAVdroid + libs
-keep class at.bitfire.** { *; } # all DAVdroid code is required
......@@ -7,7 +7,7 @@
~ http://www.gnu.org/licenses/gpl.html
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
package="at.bitfire.davdroid"
android:installLocation="internalOnly">
......@@ -26,7 +26,7 @@
<uses-permission android:name="org.dmfs.permission.WRITE_TASKS" />
<application
android:allowBackup="true"
android:allowBackup="true" android:fullBackupContent="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
......@@ -37,15 +37,16 @@
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/account_authenticator" />
</service>
<service
android:name=".syncadapter.ContactsSyncAdapterService"
android:exported="true"
android:process=":sync">
android:process=":sync"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
......@@ -59,7 +60,8 @@
<service
android:name=".syncadapter.CalendarsSyncAdapterService"
android:exported="true"
android:process=":sync">
android:process=":sync"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
......@@ -70,7 +72,8 @@
<service
android:name=".syncadapter.TasksSyncAdapterService"
android:exported="true"
android:process=":sync">
android:process=":sync"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
......@@ -89,7 +92,8 @@
</activity>
<activity
android:name=".ui.DebugInfoActivity"
android:label="@string/debug_info_title">
android:label="@string/debug_info_title"
android:exported="true">
</activity>
<activity
android:name=".ui.setup.AddAccountActivity"
......
......@@ -10,6 +10,7 @@ package at.bitfire.davdroid;
import android.content.Context;
import android.os.Build;
import android.text.TextUtils;
import com.squareup.okhttp.Authenticator;
import com.squareup.okhttp.CertificatePinner;
......@@ -23,12 +24,15 @@ import com.squareup.okhttp.logging.HttpLoggingInterceptor;
import org.slf4j.Logger;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.net.Proxy;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
......@@ -41,11 +45,13 @@ import de.duenndns.ssl.MemorizingTrustManager;
import lombok.RequiredArgsConstructor;
public class HttpClient extends OkHttpClient {
private final int MAX_LOG_LINE_LENGTH = 71;
final static UserAgentInterceptor userAgentInterceptor = new UserAgentInterceptor();
static final String userAgent;
static {
String date = new SimpleDateFormat("yyyy/MM/dd").format(BuildConfig.buildTime);
String date = new SimpleDateFormat("yyyy/MM/dd", Locale.US).format(BuildConfig.buildTime);
userAgent = "DAVdroid/" + BuildConfig.VERSION_NAME + " (" + date + "; dav4android) Android/" + Build.VERSION.RELEASE;
}
......@@ -87,7 +93,21 @@ public class HttpClient extends OkHttpClient {
HttpLoggingInterceptor logger = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
log.trace(message);
BufferedReader reader = new BufferedReader(new StringReader(message));
String line;
try {
while ((line = reader.readLine()) != null) {
int len = line.length();
for (int pos = 0; pos < len; pos += MAX_LOG_LINE_LENGTH)
if (pos < len - MAX_LOG_LINE_LENGTH)
log.trace(line.substring(pos, pos + MAX_LOG_LINE_LENGTH) + "\\");
else
log.trace(line.substring(pos));
}
} catch(IOException e) {
// for some reason, we couldn't split our message
log.trace(message);
}
}
});
logger.setLevel(HttpLoggingInterceptor.Level.BODY);
......
......@@ -26,6 +26,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
......@@ -98,7 +99,7 @@ public class DavResourceFinder {
} else if (service == Service.CARDDAV)
addressbooks.clear();
log.info("*** STARTING COLLECTION DISCOVERY FOR SERVICE " + service.name.toUpperCase() + "***");
log.info("*** STARTING COLLECTION DISCOVERY FOR SERVICE " + service.name.toUpperCase(Locale.US) + "***");
if ("http".equals(baseURI.getScheme()) || "https".equals(baseURI.getScheme())) {
HttpUrl userURL = HttpUrl.get(baseURI);
......
......@@ -87,6 +87,7 @@ public class LocalAddressBook extends AndroidAddressBook implements LocalCollect
* @param displayName title of the group to look for
* @return group with given title, or null if none
*/
@SuppressWarnings("Recycle")
public LocalGroup findGroupByTitle(String displayName) throws ContactsStorageException {
try {
@Cleanup Cursor cursor = provider.query(syncAdapterURI(Groups.CONTENT_URI),
......@@ -100,6 +101,7 @@ public class LocalAddressBook extends AndroidAddressBook implements LocalCollect
return null;
}
@SuppressWarnings("Recycle")
public LocalGroup[] getDeletedGroups() throws ContactsStorageException {
List<LocalGroup> groups = new LinkedList<>();
try {
......@@ -114,6 +116,7 @@ public class LocalAddressBook extends AndroidAddressBook implements LocalCollect
return groups.toArray(new LocalGroup[groups.size()]);
}
@SuppressWarnings("Recycle")
public LocalGroup[] getDirtyGroups() throws ContactsStorageException {
List<LocalGroup> groups = new LinkedList<>();
try {
......@@ -128,6 +131,7 @@ public class LocalAddressBook extends AndroidAddressBook implements LocalCollect
return groups.toArray(new LocalGroup[groups.size()]);
}
@SuppressWarnings("Recycle")
public void markMembersDirty(long groupId) throws ContactsStorageException {
ContentValues dirty = new ContentValues(1);
dirty.put(RawContacts.DIRTY, 1);
......@@ -150,6 +154,7 @@ public class LocalAddressBook extends AndroidAddressBook implements LocalCollect
// SYNC STATE
@SuppressWarnings("Recycle")
protected void readSyncState() throws ContactsStorageException {
@Cleanup("recycle") Parcel parcel = Parcel.obtain();
byte[] raw = getSyncState();
......@@ -161,6 +166,7 @@ public class LocalAddressBook extends AndroidAddressBook implements LocalCollect
syncState.clear();
}
@SuppressWarnings("Recycle")
protected void writeSyncState() throws ContactsStorageException {
@Cleanup("recycle") Parcel parcel = Parcel.obtain();
parcel.writeBundle(syncState);
......
......@@ -9,6 +9,7 @@
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;
......@@ -67,6 +68,7 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {
super(account, provider, LocalEvent.Factory.INSTANCE, id);
}
@TargetApi(15)
public static Uri create(Account account, ContentResolver resolver, ServerInfo.ResourceInfo info) throws CalendarStorageException {
@Cleanup("release") ContentProviderClient provider = resolver.acquireContentProviderClient(CalendarContract.AUTHORITY);
if (provider == null)
......@@ -136,6 +138,7 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {
@Override
@SuppressWarnings("Recycle")
public String getCTag() throws CalendarStorageException {
try {
@Cleanup Cursor cursor = provider.query(calendarSyncURI(), new String[] { COLUMN_CTAG }, null, null, null);
......@@ -158,6 +161,7 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {
}
}
@SuppressWarnings("Recycle")
public void processDirtyExceptions() throws CalendarStorageException {
// process deleted exceptions
Constants.log.info("Processing deleted exceptions");
......
......@@ -28,7 +28,7 @@ import ezvcard.Ezvcard;
public class LocalContact extends AndroidContact implements LocalResource {
static {
Contact.productID = "+//IDN bitfire.at//DAVdroid/" + BuildConfig.VERSION_NAME + "vcard4android ez-vcard/" + Ezvcard.VERSION;
Contact.productID = "+//IDN bitfire.at//DAVdroid/" + BuildConfig.VERSION_NAME + " vcard4android ez-vcard/" + Ezvcard.VERSION;
}
protected LocalContact(AndroidAddressBook addressBook, long id, String fileName, String eTag) {
......
......@@ -8,6 +8,7 @@
package at.bitfire.davdroid.resource;
import android.annotation.TargetApi;
import android.content.ContentProviderOperation;
import android.content.ContentValues;
import android.os.Build;
......@@ -27,6 +28,7 @@ import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
@TargetApi(17)
public class LocalEvent extends AndroidEvent implements LocalResource {
static {
Event.prodId = new ProdId("+//IDN bitfire.at//DAVdroid/" + BuildConfig.VERSION_NAME + " ical4android ical4j/2.x");
......
......@@ -39,6 +39,7 @@ public class LocalTaskList extends AndroidTaskList implements LocalCollection {
LocalTask.COLUMN_ETAG
};
// can be cached, because after installing OpenTasks, you have to re-install DAVdroid anyway
private static Boolean tasksProviderAvailable;
......
......@@ -91,7 +91,7 @@ public class AccountSettings {
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@TargetApi(21)
protected void showNotification(int id, String title, String message) {
NotificationManager nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder n = new Notification.Builder(context);
......@@ -190,7 +190,8 @@ public class AccountSettings {
Constants.log.error("Couldn't update account settings (DAVdroid will probably crash)!", e);
}
}
@SuppressWarnings("Recycle")
private void update_0_1() throws URISyntaxException {
String v0_principalURL = accountManager.getUserData(account, "principal_url"),
v0_addressBookPath = accountManager.getUserData(account, "addressbook_path");
......@@ -234,6 +235,7 @@ public class AccountSettings {
accountManager.setUserData(account, KEY_SETTINGS_VERSION, "1");
}
@SuppressWarnings("Recycle")
private void update_1_2() throws ContactsStorageException {
/* - KEY_ADDRESSBOOK_URL ("addressbook_url"),
- KEY_ADDRESSBOOK_CTAG ("addressbook_ctag"),
......
......@@ -8,6 +8,7 @@
package at.bitfire.davdroid.syncadapter;
import android.accounts.Account;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
......@@ -127,6 +128,7 @@ abstract public class SyncManager {
protected abstract String getSyncErrorTitle();
@TargetApi(21)
public void performSync() {
int syncPhase = SYNC_PHASE_PREPARE;
try {
......
......@@ -13,15 +13,19 @@ import android.accounts.AccountManager;
import android.app.Activity;
import android.app.LoaderManager;
import android.content.AsyncTaskLoader;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.Environment;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
import android.text.TextUtils;
......@@ -31,10 +35,16 @@ import android.widget.TextView;
import org.apache.commons.lang3.exception.ExceptionUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import at.bitfire.dav4android.exception.HttpException;
import at.bitfire.davdroid.BuildConfig;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
import ezvcard.util.IOUtils;
public class DebugInfoActivity extends Activity implements LoaderManager.LoaderCallbacks<String> {
public static final String
......@@ -68,13 +78,35 @@ public class DebugInfoActivity extends Activity implements LoaderManager.LoaderC
if (!TextUtils.isEmpty(report)) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, "DAVdroid Exception Details");
sendIntent.putExtra(Intent.EXTRA_TEXT, report);
sendIntent.setType("text/plain");
startActivity(sendIntent);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, "DAVdroid Exception Details");
try {
File reportFile = reportFile();
FileWriter writer = new FileWriter(reportFile());
writer.write(report);
writer.close();
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(reportFile));
} catch (IOException e) {
// let's hope the report is < 1 MB
sendIntent.putExtra(Intent.EXTRA_TEXT, report);
}
startActivityForResult(sendIntent, 0);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
reportFile().delete();
}
File reportFile() {
return new File(getExternalCacheDir(), "error-report.txt");
}
@Override
public Loader<String> onCreateLoader(int id, Bundle args) {
return new ReportLoader(this, args);
......@@ -137,7 +169,7 @@ public class DebugInfoActivity extends Activity implements LoaderManager.LoaderC
if (http.request != null)
report.append("HTTP REQUEST:\n" + http.request + "\n\n");
if (http.response != null)
report.append("HTTP RESPONSE:\n" + http.response + "\n");
report.append("HTTP RESPONSE:\n" + http.response + "\n\n");
}
if (exception != null) {
......@@ -176,8 +208,9 @@ public class DebugInfoActivity extends Activity implements LoaderManager.LoaderC
report.append(
"CONFIGURATION\n" +
"System-wide synchronization: " + (ContentResolver.getMasterSyncAutomatically() ? "automatically" : "manually") + "\n"
"System-wide synchronization: " + (ContentResolver.getMasterSyncAutomatically() ? "automatically" : "manually") + "\n\n"
);
/* TODO
AccountManager accountManager = AccountManager.get(getContext());
for (Account acc : accountManager.getAccountsByType(Constants.ACCOUNT_TYPE)) {
report.append(
......@@ -186,7 +219,7 @@ public class DebugInfoActivity extends Activity implements LoaderManager.LoaderC
" Calendar synchronization: " + syncStatus(acc, CalendarContract.AUTHORITY) + "\n" +
" OpenTasks synchronization: " + syncStatus(acc, "org.dmfs.tasks") + "\n"
);
}
}*/
report.append("\n");
try {
......
......@@ -89,7 +89,8 @@ public class AccountDetailsFragment extends Fragment implements TextWatcher {
// actions
@SuppressWarnings("Recycle")
void addAccount() {
String accountName = editAccountName.getText().toString();
......
......@@ -7,6 +7,7 @@
*/
package at.bitfire.davdroid.ui.setup;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
......@@ -62,6 +63,7 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC
}
@Override
@SuppressLint("CommitTransaction")
public void onLoadFinished(Loader<ServerInfo> loader, ServerInfo serverInfo) {
if (serverInfo.isEmpty()) {
// resource detection didn't find anything
......
......@@ -7,22 +7,17 @@
~ http://www.gnu.org/licenses/gpl.html
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:typeface="monospace"
android:text="@string/please_wait"
android:id="@+id/text_report"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:typeface="monospace"
android:id="@+id/text_report"/>
</ScrollView>
</LinearLayout>
\ No newline at end of file
</ScrollView>
\ No newline at end of file
......@@ -74,6 +74,11 @@
android:text="@string/main_license_html"
android:layout_marginBottom="20dp"
android:linksClickable="true" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/main_translation_info"
android:layout_marginBottom="20dp" />
<TextView
android:id="@+id/text_libraries_heading"
android:layout_width="fill_parent"
......
......@@ -9,15 +9,14 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="20dp"
tools:context=".MainActivity" >
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
<LinearLayout android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
......
......@@ -8,6 +8,7 @@
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
......@@ -37,7 +38,7 @@
android:layout_width="0dp"
android:scrollHorizontally="true"
android:scrollbars="horizontal"
android:hint="[email protected]">
android:hint="[email protected]" tools:ignore="HardcodedText">
<requestFocus />
</EditText>
......
......@@ -8,8 +8,9 @@
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<GridLayout
android:layout_width="fill_parent"
......@@ -39,7 +40,7 @@
android:layout_width="0dp"
android:scrollHorizontally="true"
android:scrollbars="horizontal"
android:hint="my.webhost.com">
android:hint="my.webhost.com" tools:ignore="HardcodedText">
<requestFocus />
</EditText>
......
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2013 – 2015 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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_marginEnd="10dp"
android:orientation="horizontal"
android:padding="20dp" >
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_marginEnd="10dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/setup_querying_server" />
</LinearLayout>
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<!--common strings-->
<string name="app_name">DAVdroid</string>
<string name="help">Ajuda</string>
<string name="next">Següent</string>
<string name="exception_http">Error HTTP: %s</string>
<string name="exception_incapable_resource">Falten capacitats: %s</string>
<string name="exception_io">Error E/S: %s</string>
<string name="exception_uri_syntax">URI invàlida: %s</string>
<!--MainActivity-->
<string name="main_manage_accounts">Gestioneu els comptes a sincronitzar</string>
<string name="main_play_workaround_html"><![CDATA[
<p>Gràcies per comprar DAVDroid a travès de Google Play i així donar suport al projecte. De totes formes, hi ha dos incidències amb el Google Play:</p>
<h2>1. Les comptes poden desaparèixer desprès de reiniciar</h2>
<p>Podeu trobar-vos amb el problema de que totes les vostres <strong>comptes de DAVdroid (contactes i events inclosos) han desaparegut
desprès de reiniciar el dispositiu</strong>. La raó és un <a href=\"https://code.google.com/p/android/issues/detail?id=34880\">error de Android</a>
que causa que totes les comptes de les aplicacións de pagament s\'eliminen al iniciar perquè els fitxers (encriptats) APK es
carreguen <i>desprès</i> de revisar si existeixen comptes orfanes.</p>
<p>Usuaris afectats:<br/>
* Tots els usuaris de Android 4.1 que han obtingut el DAVdroid del Play Store;<br/>
* El usuaris de Android 4.2 que han obtingut el DAVdroid del Play Store només en alguns dispositius (per exemple, la majoria de dispositius Samsung)</p>
<h2>2. Els comptes poden desaparèixer desprès d\'actualitar el DAVdroid</h2>
<p>Podeu trobar-vos amb el problema de que totes les vostres <strong>comptes de DAVdroid (contactes i events inclosos) han desaparegut quan el Play Store
actualitza el DAVdroid</strong>. La raó és un<a href=\"https://code.google.com/p/android/issues/detail?id=66905\">altre error de Android</a>
que causa que les comptes del les aplicacións de pagament s\'esborrin al actualitzar per alguna raó similar.</p>
<p>Usuaris afectats: alguns de Android 4.4.2 que han obtingut el DAVdroid desde el Play Store (es coneix per dispositius Nexus i Moto G)</p>
<p><strong>Si esteu afectats per algun d\'aquests errors, sisuplau <a href=\"market://details?id=at.bitfire.davdroid.jbworkaround\">instaleu el
DAVdroid JB Workaround</a>.</strong></p>
]]></string>
<string name="html_main_info"><![CDATA[
<h1>Benvingut a DAVdroid/%s!</h1>
<p>DAVdroid es un adaptador de Android 4+ per la sincronització de CalDAV/CardDAV. Per a utilitzar-lo afegieu una compte de DAVdroid
pel vostre servidor de CalDAV/CardDAV i els vostres contactes/esdeveiments es sincronitzaràn de forma bidireccional.</p>
<p>Per mes informació, podeu visitar la <a href=\"http://davdroid.bitfire.at?pk_campaign=davdroid-app&amp;pk_kwd=main-activity\">pàgina web de DAVdroid</a>.
També hi ha una <a href=\"http://davdroid.bitfire.at/configuration?pk_campaign=davdroid-app&amp;pk_kwd=main-activity\">guia de configuració</a>. El DAVdroid respecta
la teva privacitat, feu una ullada a <a href=\"http://davdroid.bitfire.at/privacy?pk_campaign=davdroid-app&amp;pk_kwd=main-activity\">la nostra politica de privacitat</a>.</p>
<p>Si feu servir CyanogenMod, \"Privacy Guard\" ha d\'estar deshabilitat pel DAVdroid. Si no es així, el DAVdroid no pot accedir ni sincronitzar
els vostres contactes i events.</p>
<p><b>En cas de problemes, sisplau llegiu el <a href=\"http://davdroid.bitfire.at/configuration?pk_campaign=davdroid-app&amp;pk_kwd=main-activity\">FAQ</a> primer.
To get assistance or discuss about DAVdroid-related topics, have a look in our
<a href=\"https://davdroid.bitfire.at/forums?pk_campaign=davdroid-app&amp;pk_kwd=main-activity\">DAVdroid forums</a>.</b>
Please do not blackmail us with bad ratings in the stores (it\'s futile and discouraging for both sides).</p>
<h1>Codi Obert</h1>
<p>DAVdroid està disenyat per a ser un proejcte de codi obert desde bon principi. Sempres es possible compilar l\'aplicació per la vostra part
i utilitzar-la gratuitament sense cap obligació. El codi font es troba
app yourself and use it for free without any obligations. The source code is
<a href=\"https://github.com/rfc2822/davdroid\">disponible a Github</a>, i també podeu
<a href=\"https://f-droid.org/app/at.bitfire.davdroid\">descarregar l\'aplicació al F-droid</a>.</p>
<p>De totes formes, es va fer molta feina per crear aquesta applicació, així que vam decidir ficar-la a les tendes comercials per un petit import.
Si voleu suportar aquest project, siusplau considereu <a href=\"http://davdroid.bitf