1
0
mirror of https://github.com/etesync/android synced 2025-05-07 01:19:11 +00:00
etesync-android/app/src/main/java/com/etesync/syncadapter/App.java
Tom Hacohen a4a32045e8 Journal: get and persist owner and key.
The server was changed so the owner of the journal, and the encrypted
key (if a shared journal) would be exposed. This change fetches it, and
saves it.
2017-04-20 19:48:45 +01:00

325 lines
13 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 com.etesync.syncadapter;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Process;
import android.os.StrictMode;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.NotificationCompat;
import android.util.Log;
import com.etesync.syncadapter.log.LogcatHandler;
import com.etesync.syncadapter.log.PlainTextFormatter;
import com.etesync.syncadapter.model.CollectionInfo;
import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.model.Models;
import com.etesync.syncadapter.model.ServiceDB;
import com.etesync.syncadapter.model.Settings;
import com.etesync.syncadapter.resource.LocalAddressBook;
import com.etesync.syncadapter.resource.LocalCalendar;
import com.etesync.syncadapter.ui.AccountsActivity;
import com.etesync.syncadapter.utils.HintManager;
import net.fortuna.ical4j.util.UidGenerator;
import org.apache.commons.lang3.time.DateFormatUtils;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.HostnameVerifier;
import at.bitfire.cert4android.CustomCertManager;
import at.bitfire.ical4android.CalendarStorageException;
import at.bitfire.vcard4android.ContactsStorageException;
import io.requery.Persistable;
import io.requery.android.sqlite.DatabaseSource;
import io.requery.sql.Configuration;
import io.requery.sql.EntityDataStore;
import io.requery.sql.TableCreationMode;
import lombok.Cleanup;
import lombok.Getter;
import okhttp3.internal.tls.OkHostnameVerifier;
public class App extends Application {
public static final String FLAVOR_GOOGLE_PLAY = "gplay";
public static final String
DISTRUST_SYSTEM_CERTIFICATES = "distrustSystemCerts",
LOG_TO_EXTERNAL_STORAGE = "logToExternalStorage",
OVERRIDE_PROXY = "overrideProxy",
OVERRIDE_PROXY_HOST = "overrideProxyHost",
OVERRIDE_PROXY_PORT = "overrideProxyPort";
public static final String OVERRIDE_PROXY_HOST_DEFAULT = "localhost";
public static final int OVERRIDE_PROXY_PORT_DEFAULT = 8118;
@Getter
private CustomCertManager certManager;
@Getter
private static SSLSocketFactoryCompat sslSocketFactoryCompat;
@Getter
private static HostnameVerifier hostnameVerifier;
@Getter
private static UidGenerator uidGenerator;
public final static Logger log = Logger.getLogger("syncadapter");
static {
at.bitfire.cert4android.Constants.log = Logger.getLogger("syncadapter.cert4android");
}
@Override
@SuppressLint("HardwareIds")
public void onCreate() {
super.onCreate();
reinitCertManager();
reinitLogger();
StrictMode.enableDefaults();
initPrefVersion();
uidGenerator = new UidGenerator(null, android.provider.Settings.Secure.getString(getContentResolver(), android.provider.Settings.Secure.ANDROID_ID));
}
public void reinitCertManager() {
if (BuildConfig.customCerts) {
if (certManager != null)
certManager.close();
@Cleanup ServiceDB.OpenHelper dbHelper = new ServiceDB.OpenHelper(this);
Settings settings = new Settings(dbHelper.getReadableDatabase());
certManager = new CustomCertManager(this, !settings.getBoolean(DISTRUST_SYSTEM_CERTIFICATES, false));
sslSocketFactoryCompat = new SSLSocketFactoryCompat(certManager);
hostnameVerifier = certManager.hostnameVerifier(OkHostnameVerifier.INSTANCE);
}
}
public void reinitLogger() {
@Cleanup ServiceDB.OpenHelper dbHelper = new ServiceDB.OpenHelper(this);
Settings settings = new Settings(dbHelper.getReadableDatabase());
boolean logToFile = settings.getBoolean(LOG_TO_EXTERNAL_STORAGE, false),
logVerbose = logToFile || Log.isLoggable(log.getName(), Log.DEBUG);
App.log.info("Verbose logging: " + logVerbose);
// set logging level according to preferences
final Logger rootLogger = Logger.getLogger("");
rootLogger.setLevel(logVerbose ? Level.ALL : Level.INFO);
// remove all handlers and add our own logcat handler
rootLogger.setUseParentHandlers(false);
for (Handler handler : rootLogger.getHandlers())
rootLogger.removeHandler(handler);
rootLogger.addHandler(LogcatHandler.INSTANCE);
NotificationManagerCompat nm = NotificationManagerCompat.from(this);
// log to external file according to preferences
if (logToFile) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder .setSmallIcon(R.drawable.ic_sd_storage_light)
.setLargeIcon(getLauncherBitmap(this))
.setContentTitle(getString(R.string.logging_davdroid_file_logging))
.setLocalOnly(true);
File dir = getExternalFilesDir(null);
if (dir != null)
try {
String fileName = new File(dir, "etesync-" + Process.myPid() + "-" +
DateFormatUtils.format(System.currentTimeMillis(), "yyyyMMdd-HHmmss") + ".txt").toString();
log.info("Logging to " + fileName);
FileHandler fileHandler = new FileHandler(fileName);
fileHandler.setFormatter(PlainTextFormatter.DEFAULT);
log.addHandler(fileHandler);
builder .setContentText(dir.getPath())
.setSubText(getString(R.string.logging_to_external_storage_warning))
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(getString(R.string.logging_to_external_storage, dir.getPath())))
.setOngoing(true);
} catch (IOException e) {
log.log(Level.SEVERE, "Couldn't create external log file", e);
builder .setContentText(getString(R.string.logging_couldnt_create_file, e.getLocalizedMessage()))
.setCategory(NotificationCompat.CATEGORY_ERROR);
}
else
builder.setContentText(getString(R.string.logging_no_external_storage));
nm.notify(Constants.NOTIFICATION_EXTERNAL_FILE_LOGGING, builder.build());
} else
nm.cancel(Constants.NOTIFICATION_EXTERNAL_FILE_LOGGING);
}
@Nullable
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static Bitmap getLauncherBitmap(@NonNull Context context) {
Bitmap bitmapLogo = null;
Drawable drawableLogo = ContextCompat.getDrawable(context, R.mipmap.ic_launcher);
if (drawableLogo instanceof BitmapDrawable)
bitmapLogo = ((BitmapDrawable)drawableLogo).getBitmap();
return bitmapLogo;
}
public static class ReinitLoggingReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
log.info("Received broadcast: re-initializing logger");
App app = (App)context.getApplicationContext();
app.reinitLogger();
}
}
private EntityDataStore<Persistable> dataStore;
/**
* @return {@link EntityDataStore} single instance for the application.
* <p/>
* Note if you're using Dagger you can make this part of your application level module returning
* {@code @Provides @Singleton}.
*/
public EntityDataStore<Persistable> getData() {
if (dataStore == null) {
// override onUpgrade to handle migrating to a new version
DatabaseSource source = new DatabaseSource(this, Models.DEFAULT, 2);
Configuration configuration = source.getConfiguration();
dataStore = new EntityDataStore<>(configuration);
}
return dataStore;
}
// update from previous account settings
private final static String PREF_VERSION = "version";
/** Init the preferences version of the app.
* This is used to initialise the first version if not alrady set. */
private void initPrefVersion() {
SharedPreferences prefs = getSharedPreferences("app", Context.MODE_PRIVATE);
if (prefs.getInt(PREF_VERSION, 0) == 0) {
prefs.edit().putInt(PREF_VERSION, BuildConfig.VERSION_CODE).apply();
}
}
private void update(int fromVersion) {
App.log.info("Updating from version " + fromVersion + " to " + BuildConfig.VERSION_CODE);
if (fromVersion < 6) {
EntityDataStore<Persistable> data = this.getData();
ServiceDB.OpenHelper dbHelper = new ServiceDB.OpenHelper(this);
List<CollectionInfo> collections = readCollections(dbHelper);
for (CollectionInfo info : collections) {
JournalEntity journalEntity = new JournalEntity(info);
data.insert(journalEntity);
}
@Cleanup SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete(ServiceDB.Collections._TABLE, null, null);
}
if (fromVersion < 7) {
/* Fix all of the etags to be non-null */
AccountManager am = AccountManager.get(this);
for (Account account : am.getAccountsByType(Constants.ACCOUNT_TYPE)) {
try {
LocalCalendar calendars[] = (LocalCalendar[]) LocalCalendar.find(account, this.getContentResolver().acquireContentProviderClient(CalendarContract.CONTENT_URI),
LocalCalendar.Factory.INSTANCE, null, null);
for (LocalCalendar calendar : calendars) {
calendar.fixEtags();
}
} catch (CalendarStorageException e) {
e.printStackTrace();
}
LocalAddressBook addressBook = new LocalAddressBook(account, this.getContentResolver().acquireContentProviderClient(ContactsContract.Contacts.CONTENT_URI));
try {
addressBook.fixEtags();
} catch (ContactsStorageException e) {
e.printStackTrace();
}
}
}
if (fromVersion < 10) {
HintManager.setHintSeen(this, AccountsActivity.HINT_ACCOUNT_ADD, true);
}
}
public static class AppUpdatedReceiver extends BroadcastReceiver {
@Override
@SuppressLint("UnsafeProtectedBroadcastReceiver,MissingPermission")
public void onReceive(Context context, Intent intent) {
App.log.info("EteSync was updated, checking for app version");
App app = (App) context.getApplicationContext();
SharedPreferences prefs = app.getSharedPreferences("app", Context.MODE_PRIVATE);
int fromVersion = prefs.getInt(PREF_VERSION, 1);
app.update(fromVersion);
prefs.edit().putInt(PREF_VERSION, BuildConfig.VERSION_CODE).apply();
}
}
@NonNull
private List<CollectionInfo> readCollections(ServiceDB.OpenHelper dbHelper) {
@Cleanup SQLiteDatabase db = dbHelper.getWritableDatabase();
List<CollectionInfo> collections = new LinkedList<>();
@Cleanup Cursor cursor = db.query(ServiceDB.Collections._TABLE, null, null, null, null, null, null);
while (cursor.moveToNext()) {
ContentValues values = new ContentValues();
DatabaseUtils.cursorRowToContentValues(cursor, values);
collections.add(CollectionInfo.fromDB(values));
}
return collections;
}
}