From 3f7f6cb02836ed5d9e43678bf93f1255d2deac91 Mon Sep 17 00:00:00 2001 From: Herbert Reiter <4941275-moasda@users.noreply.gitlab.com> Date: Sat, 4 Jan 2020 19:36:50 +0100 Subject: [PATCH] Initial check-in --- app/app.iml | 133 +++++ app/src/main/AndroidManifest.xml | 69 +++ .../net/moasdawiki/app/AndroidSettings.java | 37 ++ .../app/CalendarAccountAuthenticator.java | 83 +++ .../CalendarAccountAuthenticatorService.java | 45 ++ .../app/CalendarContentProvider.java | 70 +++ .../moasdawiki/app/CalendarSyncAdapter.java | 323 +++++++++++ .../app/CalendarSyncAdapterService.java | 46 ++ .../java/net/moasdawiki/app/Constants.java | 37 ++ .../java/net/moasdawiki/app/MainActivity.java | 510 ++++++++++++++++++ .../net/moasdawiki/app/SettingsActivity.java | 42 ++ .../net/moasdawiki/app/SettingsFragment.java | 203 +++++++ .../moasdawiki/app/SynchronizeWikiClient.java | 466 ++++++++++++++++ .../moasdawiki/app/WikiEngineApplication.java | 93 ++++ .../res/drawable-anydpi/ic_action_event.xml | 11 + .../res/drawable-anydpi/ic_action_sync.xml | 11 + .../drawable-anydpi/ic_action_sync_white.xml | 11 + .../res/drawable-hdpi/ic_action_event.png | Bin 0 -> 255 bytes .../main/res/drawable-hdpi/ic_action_sync.png | Bin 0 -> 503 bytes .../drawable-hdpi/ic_action_sync_white.png | Bin 0 -> 444 bytes .../main/res/drawable-hdpi/ic_menu_help.png | Bin 0 -> 1391 bytes .../main/res/drawable-hdpi/ic_menu_home.png | Bin 0 -> 1664 bytes .../drawable-hdpi/ic_menu_info_details.png | Bin 0 -> 1983 bytes .../res/drawable-hdpi/ic_menu_preferences.png | Bin 0 -> 1851 bytes .../main/res/drawable-hdpi/ic_menu_search.png | Bin 0 -> 2966 bytes .../res/drawable-mdpi/ic_action_event.png | Bin 0 -> 159 bytes .../main/res/drawable-mdpi/ic_action_sync.png | Bin 0 -> 334 bytes .../drawable-mdpi/ic_action_sync_white.png | Bin 0 -> 289 bytes .../main/res/drawable-mdpi/ic_menu_help.png | Bin 0 -> 951 bytes .../main/res/drawable-mdpi/ic_menu_home.png | Bin 0 -> 1046 bytes .../drawable-mdpi/ic_menu_info_details.png | Bin 0 -> 1237 bytes .../res/drawable-mdpi/ic_menu_preferences.png | Bin 0 -> 1142 bytes .../main/res/drawable-mdpi/ic_menu_search.png | Bin 0 -> 1704 bytes .../res/drawable-xhdpi/ic_action_event.png | Bin 0 -> 225 bytes .../res/drawable-xhdpi/ic_action_sync.png | Bin 0 -> 623 bytes .../drawable-xhdpi/ic_action_sync_white.png | Bin 0 -> 543 bytes .../main/res/drawable-xhdpi/ic_menu_help.png | Bin 0 -> 1840 bytes .../main/res/drawable-xhdpi/ic_menu_home.png | Bin 0 -> 2239 bytes .../drawable-xhdpi/ic_menu_info_details.png | Bin 0 -> 2763 bytes .../drawable-xhdpi/ic_menu_preferences.png | Bin 0 -> 2507 bytes .../res/drawable-xhdpi/ic_menu_search.png | Bin 0 -> 4281 bytes .../res/drawable-xxhdpi/ic_action_event.png | Bin 0 -> 341 bytes .../res/drawable-xxhdpi/ic_action_sync.png | Bin 0 -> 918 bytes .../drawable-xxhdpi/ic_action_sync_white.png | Bin 0 -> 801 bytes .../main/res/drawable-xxhdpi/ic_menu_help.png | Bin 0 -> 3433 bytes .../main/res/drawable-xxhdpi/ic_menu_home.png | Bin 0 -> 3868 bytes .../drawable-xxhdpi/ic_menu_info_details.png | Bin 0 -> 4891 bytes .../drawable-xxhdpi/ic_menu_preferences.png | Bin 0 -> 4407 bytes .../res/drawable-xxhdpi/ic_menu_search.png | Bin 0 -> 4374 bytes .../res/drawable/ic_info_outline_black.xml | 9 + app/src/main/res/layout/about_layout.xml | 29 + app/src/main/res/layout/main_layout.xml | 130 +++++ app/src/main/res/layout/settings_layout.xml | 7 + app/src/main/res/menu/menu_layout.xml | 40 ++ app/src/main/res/mipmap-hdpi/ic_cow.png | Bin 0 -> 9027 bytes app/src/main/res/mipmap-mdpi/ic_cow.png | Bin 0 -> 4628 bytes app/src/main/res/mipmap-xhdpi/ic_cow.png | Bin 0 -> 15087 bytes app/src/main/res/mipmap-xxhdpi/ic_cow.png | Bin 0 -> 31398 bytes app/src/main/res/values-de/strings.xml | 35 ++ app/src/main/res/values/dimens.xml | 3 + app/src/main/res/values/strings.xml | 36 ++ app/src/main/res/values/styles.xml | 12 + app/src/main/res/xml/authenticator.xml | 6 + app/src/main/res/xml/settings_fragment.xml | 42 ++ app/src/main/res/xml/syncadapter.xml | 8 + 65 files changed, 2547 insertions(+) create mode 100644 app/app.iml create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/net/moasdawiki/app/AndroidSettings.java create mode 100644 app/src/main/java/net/moasdawiki/app/CalendarAccountAuthenticator.java create mode 100644 app/src/main/java/net/moasdawiki/app/CalendarAccountAuthenticatorService.java create mode 100644 app/src/main/java/net/moasdawiki/app/CalendarContentProvider.java create mode 100644 app/src/main/java/net/moasdawiki/app/CalendarSyncAdapter.java create mode 100644 app/src/main/java/net/moasdawiki/app/CalendarSyncAdapterService.java create mode 100644 app/src/main/java/net/moasdawiki/app/Constants.java create mode 100644 app/src/main/java/net/moasdawiki/app/MainActivity.java create mode 100644 app/src/main/java/net/moasdawiki/app/SettingsActivity.java create mode 100644 app/src/main/java/net/moasdawiki/app/SettingsFragment.java create mode 100644 app/src/main/java/net/moasdawiki/app/SynchronizeWikiClient.java create mode 100644 app/src/main/java/net/moasdawiki/app/WikiEngineApplication.java create mode 100644 app/src/main/res/drawable-anydpi/ic_action_event.xml create mode 100644 app/src/main/res/drawable-anydpi/ic_action_sync.xml create mode 100644 app/src/main/res/drawable-anydpi/ic_action_sync_white.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_action_event.png create mode 100644 app/src/main/res/drawable-hdpi/ic_action_sync.png create mode 100644 app/src/main/res/drawable-hdpi/ic_action_sync_white.png create mode 100644 app/src/main/res/drawable-hdpi/ic_menu_help.png create mode 100644 app/src/main/res/drawable-hdpi/ic_menu_home.png create mode 100644 app/src/main/res/drawable-hdpi/ic_menu_info_details.png create mode 100644 app/src/main/res/drawable-hdpi/ic_menu_preferences.png create mode 100644 app/src/main/res/drawable-hdpi/ic_menu_search.png create mode 100644 app/src/main/res/drawable-mdpi/ic_action_event.png create mode 100644 app/src/main/res/drawable-mdpi/ic_action_sync.png create mode 100644 app/src/main/res/drawable-mdpi/ic_action_sync_white.png create mode 100644 app/src/main/res/drawable-mdpi/ic_menu_help.png create mode 100644 app/src/main/res/drawable-mdpi/ic_menu_home.png create mode 100644 app/src/main/res/drawable-mdpi/ic_menu_info_details.png create mode 100644 app/src/main/res/drawable-mdpi/ic_menu_preferences.png create mode 100644 app/src/main/res/drawable-mdpi/ic_menu_search.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_action_event.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_action_sync.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_action_sync_white.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_menu_help.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_menu_home.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_menu_info_details.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_menu_preferences.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_menu_search.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_action_event.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_action_sync.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_action_sync_white.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_menu_help.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_menu_home.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_menu_info_details.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_menu_preferences.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_menu_search.png create mode 100644 app/src/main/res/drawable/ic_info_outline_black.xml create mode 100644 app/src/main/res/layout/about_layout.xml create mode 100644 app/src/main/res/layout/main_layout.xml create mode 100644 app/src/main/res/layout/settings_layout.xml create mode 100644 app/src/main/res/menu/menu_layout.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_cow.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_cow.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_cow.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_cow.png create mode 100644 app/src/main/res/values-de/strings.xml create mode 100644 app/src/main/res/values/dimens.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/styles.xml create mode 100644 app/src/main/res/xml/authenticator.xml create mode 100644 app/src/main/res/xml/settings_fragment.xml create mode 100644 app/src/main/res/xml/syncadapter.xml diff --git a/app/app.iml b/app/app.iml new file mode 100644 index 0000000..6e42112 --- /dev/null +++ b/app/app.iml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..c700c4e --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/net/moasdawiki/app/AndroidSettings.java b/app/src/main/java/net/moasdawiki/app/AndroidSettings.java new file mode 100644 index 0000000..f607e03 --- /dev/null +++ b/app/src/main/java/net/moasdawiki/app/AndroidSettings.java @@ -0,0 +1,37 @@ +/* + * MoasdaWiki App + * Copyright (C) 2008 - 2020 Herbert Reiter (herbert@moasdawiki.net) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moasdawiki.app; + +import net.moasdawiki.base.Logger; +import net.moasdawiki.base.Settings; +import net.moasdawiki.service.repository.RepositoryService; + +import org.jetbrains.annotations.NotNull; + +public class AndroidSettings extends Settings { + + public AndroidSettings(@NotNull Logger logger, @NotNull RepositoryService repositoryService, @NotNull String configFileName) { + super(logger, repositoryService, configFileName); + } + + @NotNull + public String getVersion() { + return BuildConfig.VERSION_NAME; + } +} diff --git a/app/src/main/java/net/moasdawiki/app/CalendarAccountAuthenticator.java b/app/src/main/java/net/moasdawiki/app/CalendarAccountAuthenticator.java new file mode 100644 index 0000000..c4af815 --- /dev/null +++ b/app/src/main/java/net/moasdawiki/app/CalendarAccountAuthenticator.java @@ -0,0 +1,83 @@ +/* + * MoasdaWiki App + * Copyright (C) 2008 - 2020 Herbert Reiter (herbert@moasdawiki.net) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moasdawiki.app; + +import android.accounts.AbstractAccountAuthenticator; +import android.accounts.Account; +import android.accounts.AccountAuthenticatorResponse; +import android.content.Context; +import android.os.Bundle; + +import org.jetbrains.annotations.Nullable; + +/** + * Stub authenticator, required for calendar sync adapter. + * + * @see "https://developer.android.com/training/sync-adapters/creating-authenticator#CreateAuthenticator" + */ +public class CalendarAccountAuthenticator extends AbstractAccountAuthenticator { + + public CalendarAccountAuthenticator(Context context) { + super(context); + } + + @Override + public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) { + // Editing properties is not supported + throw new UnsupportedOperationException(); + } + + @Nullable + @Override + public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) { + // Don't add additional accounts + return null; + } + + @Nullable + @Override + public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) { + // Ignore attempts to confirm credentials + return null; + } + + @Override + public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) { + // Getting an authentication token is not supported + throw new UnsupportedOperationException(); + } + + @Override + public String getAuthTokenLabel(String authTokenType) { + // Getting a label for the auth token is not supported + throw new UnsupportedOperationException(); + } + + @Override + public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) { + // Updating user credentials is not supported + throw new UnsupportedOperationException(); + } + + @Override + public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) { + // Checking features for the account is not supported + throw new UnsupportedOperationException(); + } +} diff --git a/app/src/main/java/net/moasdawiki/app/CalendarAccountAuthenticatorService.java b/app/src/main/java/net/moasdawiki/app/CalendarAccountAuthenticatorService.java new file mode 100644 index 0000000..e503294 --- /dev/null +++ b/app/src/main/java/net/moasdawiki/app/CalendarAccountAuthenticatorService.java @@ -0,0 +1,45 @@ +/* + * MoasdaWiki App + * Copyright (C) 2008 - 2020 Herbert Reiter (herbert@moasdawiki.net) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moasdawiki.app; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +import org.jetbrains.annotations.Nullable; + +/** + * Service to bind the CalendarAccountAuthenticator. + */ +public class CalendarAccountAuthenticatorService extends Service { + + private CalendarAccountAuthenticator authenticator; + + @Override + public void onCreate() { + // Create a new authenticator object + authenticator = new CalendarAccountAuthenticator(this); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return authenticator.getIBinder(); + } +} diff --git a/app/src/main/java/net/moasdawiki/app/CalendarContentProvider.java b/app/src/main/java/net/moasdawiki/app/CalendarContentProvider.java new file mode 100644 index 0000000..5c3ea9b --- /dev/null +++ b/app/src/main/java/net/moasdawiki/app/CalendarContentProvider.java @@ -0,0 +1,70 @@ +/* + * MoasdaWiki App + * Copyright (C) 2008 - 2020 Herbert Reiter (herbert@moasdawiki.net) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moasdawiki.app; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Stub content provider, necessary for sync adapter. + */ +public class CalendarContentProvider extends ContentProvider { + + @Override + public boolean onCreate() { + return true; + } + + @Nullable + @Override + public String getType(@NotNull Uri uri) { + // Return no type for MIME type + return null; + } + + @Nullable + @Override + public Cursor query(@NotNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { + // query() always returns no results + return null; + } + + @Nullable + @Override + public Uri insert(@NotNull Uri uri, @Nullable ContentValues contentValues) { + // Provider doesn't support changes from outside + return null; + } + + @Override + public int delete(@NotNull Uri uri, @Nullable String s, @Nullable String[] strings) { + // Provider doesn't support changes from outside + return 0; + } + + @Override + public int update(@NotNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) { + // Provider doesn't support changes from outside + return 0; + } +} diff --git a/app/src/main/java/net/moasdawiki/app/CalendarSyncAdapter.java b/app/src/main/java/net/moasdawiki/app/CalendarSyncAdapter.java new file mode 100644 index 0000000..de8435f --- /dev/null +++ b/app/src/main/java/net/moasdawiki/app/CalendarSyncAdapter.java @@ -0,0 +1,323 @@ +/* + * MoasdaWiki App + * Copyright (C) 2008 - 2020 Herbert Reiter (herbert@moasdawiki.net) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moasdawiki.app; + +import android.Manifest; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.app.Activity; +import android.content.AbstractThreadedSyncAdapter; +import android.content.ContentProviderClient; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SyncResult; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.provider.CalendarContract; +import androidx.core.app.ActivityCompat; +import androidx.preference.PreferenceManager; + +import android.util.Log; +import android.widget.Toast; + +import net.moasdawiki.plugin.Plugin; +import net.moasdawiki.plugin.PluginService; +import net.moasdawiki.plugin.TerminPlugin; +import net.moasdawiki.util.PathUtils; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Calendar; +import java.util.Collections; +import java.util.List; +import java.util.TimeZone; + +import static android.content.Context.ACCOUNT_SERVICE; + +/** + * Creates an Android calendar with all events found on Wiki pages and keeps them up-to-date. + */ +public class CalendarSyncAdapter extends AbstractThreadedSyncAdapter { + + private static final String TAG = "CalendarSyncAdapter"; + + static final String ACCOUNT_NAME = "MoasdaWiki"; + static final String ACCOUNT_TYPE = "net.moasdawiki"; + static final String PROVIDER_NAME = "net.moasdawiki.app.provider"; + + private static final String CALENDAR_NAME = "MoasdaWiki Events"; + private static final Uri CALENDAR_URI = CalendarContract.Calendars.CONTENT_URI; + private static final Uri EVENT_URI = CalendarContract.Events. CONTENT_URI; + private static final Uri REMINDER_URI = CalendarContract.Reminders.CONTENT_URI; + + private final ContentResolver contentResolver; + + @SuppressWarnings("SameParameterValue") + CalendarSyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) { + super(context, autoInitialize, allowParallelSyncs); + contentResolver = context.getContentResolver(); + } + + @Override + public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { + Log.d(TAG, "Begin of onPerformSync()"); + List events = getWikiEvents(); + String calendarId = createCalendar(); + if (calendarId != null) { + deleteAllEvents(calendarId); + addEvents(calendarId, events); + } + + Handler handler = new Handler(Looper.getMainLooper()); + handler.post(() -> { + Toast toast = Toast.makeText(getContext(), R.string.calendar_sync_finished, Toast.LENGTH_SHORT); + toast.show(); + }); + Log.d(TAG, "End of onPerformSync()"); + } + + /** + * Imports all events on Wiki pages to the Android calendar. + */ + @NotNull + private List getWikiEvents() { + Log.d(TAG, "Reading Wiki events"); + WikiEngineApplication app = (WikiEngineApplication) getContext(); + PluginService pluginService = app.getServiceLocator().getPluginService(); + TerminPlugin terminPlugin = null; + for (Plugin plugin : pluginService.getPlugins()) { + if (plugin instanceof TerminPlugin) { + terminPlugin = (TerminPlugin) plugin; + } + } + if (terminPlugin == null) { + Log.e(TAG, "TerminPlugin not found, cannot retrieve event list"); + return Collections.emptyList(); + } + List events = terminPlugin.getEvents(); + Log.d(TAG, "Wiki events found: " + events.size()); + return events; + } + + @NotNull + private Uri buildUri(@NotNull Uri uri) { + return uri.buildUpon() + .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER, "true") + .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_NAME, ACCOUNT_NAME) + .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_TYPE, ACCOUNT_TYPE).build(); + } + + /** + * Returns the ID of the event calendar. If the calendar doesn't exist, it is created. + * + * @return Calendar ID + */ + @Nullable + private String createCalendar() { + // Calendar already existing? + Log.d(TAG, "Check if calendar already exists"); + Uri calendarUri = buildUri(CALENDAR_URI); + try (Cursor cursor = contentResolver.query(calendarUri, + new String[] { CalendarContract.Calendars._ID }, + "(" + CalendarContract.Calendars.ACCOUNT_TYPE + " = ? AND " + CalendarContract.Calendars.NAME + " = ?)", + new String[] { ACCOUNT_TYPE, CALENDAR_NAME }, + null)) { + if (cursor != null && cursor.moveToFirst()) { + // Calendar is already existing + String calendarId = cursor.getString(0); + Log.d(TAG, "Calendar already exists, id=" + calendarId); + return calendarId; + } + } + + // Create a new calendar + Log.d(TAG, "Calendar does not exist, create new one"); + ContentValues cv = new ContentValues(); + cv.put(CalendarContract.Calendars.ACCOUNT_NAME, ACCOUNT_NAME); + cv.put(CalendarContract.Calendars.ACCOUNT_TYPE, ACCOUNT_TYPE); + cv.put(CalendarContract.Calendars.NAME, CALENDAR_NAME); + String displayName = getContext().getString(R.string.calendar_display_name); + cv.put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, displayName); + //cv.put(CalendarContract.Calendars.CALENDAR_COLOR, 0xEA8561); + cv.put(CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL, CalendarContract.Calendars.CAL_ACCESS_READ); + cv.put(CalendarContract.Calendars.OWNER_ACCOUNT, ACCOUNT_NAME); + cv.put(CalendarContract.Calendars.VISIBLE, 1); + cv.put(CalendarContract.Calendars.SYNC_EVENTS, 1); + Uri newCalendar = contentResolver.insert(calendarUri, cv); + Log.i(TAG, "Created calendar: " + newCalendar); + if (newCalendar != null) { + return newCalendar.getLastPathSegment(); + } else { + return null; + } + } + + /** + * Clears the calendar. This is necessary if e.g. a birthday has been changed or removed. + */ + private void deleteAllEvents(@NotNull String calendarId) { + Log.d(TAG, "Delete all events from calendar"); + Uri eventUri = buildUri(EVENT_URI); + try (Cursor cursor = contentResolver.query(eventUri, + new String[] { CalendarContract.Events._ID }, + "(" + CalendarContract.Events.CALENDAR_ID + " = ?)", + new String[] { calendarId }, + null)) { + int count = 0; + while (cursor != null && cursor.moveToNext()) { + Uri deleteUri = ContentUris.withAppendedId(eventUri, cursor.getInt(0)); + contentResolver.delete(deleteUri, null, null); + count++; + } + Log.d(TAG, "Deleted " + count + " events from calendar"); + } + } + + /** + * Adds all events from the event list. The first occurrence is in the current year, the events + * are repeated every year. + */ + private void addEvents(@NotNull String calendarId, @NotNull List events) { + Log.d(TAG, "Create calendar events"); + for (TerminPlugin.Event event : events) { + int day = event.dateFields.day != null ? event.dateFields.day : 1; + int month = event.dateFields.month != null ? event.dateFields.month : 1; // 1=January + String title = event.description; + if (title == null) { + title = PathUtils.extractWebName(event.pagePath); + } + String description = getContext().getString(R.string.calendar_date) + ": " + TerminPlugin.formatGermanDate(event.dateFields); + String eventId = addEvent(calendarId, day, month, title, description); + if (eventId != null) { + addReminder(eventId); + } + } + Log.i(TAG, "Created events: " + events.size()); + } + + /** + * Adds a single event to the calendar. + */ + @Nullable + private String addEvent(@NotNull String calendarId, int day, int month, @NotNull String title, @NotNull String description) { + Log.d(TAG, "Create calendar event: day=" + day + ", month=" + month + + ", title=" + title + ", description=" + description); + ContentValues cv = new ContentValues(); + cv.put(CalendarContract.Events.CALENDAR_ID, calendarId); + cv.put(CalendarContract.Events.TITLE, title); + cv.put(CalendarContract.Events.DESCRIPTION, description); + + TimeZone utc = TimeZone.getTimeZone("UTC"); + Calendar beginTime = Calendar.getInstance(utc); + int currentYear = beginTime.get(Calendar.YEAR); + beginTime.clear(); + beginTime.set(currentYear, month - 1, day); + cv.put(CalendarContract.Events.DTSTART, beginTime.getTimeInMillis()); + cv.put(CalendarContract.Events.DURATION, "PT1D"); + cv.put(CalendarContract.Events.ALL_DAY, 1); + cv.put(CalendarContract.Events.RRULE, "FREQ=YEARLY"); + cv.put(CalendarContract.Events.AVAILABILITY, CalendarContract.Events.AVAILABILITY_FREE); + + Uri eventUri = buildUri(EVENT_URI); + Uri newEvent = contentResolver.insert(eventUri, cv); + Log.i(TAG, "Created event: " + newEvent); + if (newEvent != null) { + return newEvent.getLastPathSegment(); + } else { + return null; + } + } + + /** + * Add a reminder to the given event. + */ + private void addReminder(@NotNull String eventId) { + Log.d(TAG, "Add reminder to eventId=" + eventId); + ContentValues cv = new ContentValues(); + cv.put(CalendarContract.Reminders.EVENT_ID, eventId); + cv.put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT); + cv.put(CalendarContract.Reminders.MINUTES, 0); // 0 minutes offset + Uri reminderUri = buildUri(REMINDER_URI); + Uri newReminder = contentResolver.insert(reminderUri, cv); + Log.d(TAG, "Created reminder: " + newReminder); + } + + /** + * Initiates the calendar sync. + */ + public static void requestCalendarSync(@NotNull Activity activity) { + Log.d(TAG, "Requesting calendar synchronization"); + Context context = activity.getApplicationContext(); + + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + boolean calendarEnabled = preferences.getBoolean(Constants.PREFERENCES_CALENDAR_ENABLED, false); + if (!calendarEnabled) { + Log.d(TAG, "Calendar integration disabled, cancel operation"); + return; + } + + try { + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_CALENDAR) != PackageManager.PERMISSION_GRANTED) { + if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_CALENDAR)) { + Log.d(TAG, "User has permanently denied permission WRITE_CALENDAR, informing him"); + String hint = context.getString(R.string.calendar_permission_request, "WRITE_CALENDAR"); + Toast toast = Toast.makeText(context, hint, Toast.LENGTH_SHORT); + toast.show(); + } + Log.d(TAG, "Ask for permission WRITE_CALENDAR"); + ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_CALENDAR}, 0); + return; + } + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED) { + if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.READ_CALENDAR)) { + Log.d(TAG, "User has permanently denied permission READ_CALENDAR, informing him"); + String hint = context.getString(R.string.calendar_permission_request, "READ_CALENDAR"); + Toast toast = Toast.makeText(context, hint, Toast.LENGTH_SHORT); + toast.show(); + } + Log.d(TAG, "Ask for permission READ_CALENDAR"); + ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.READ_CALENDAR}, 0); + return; + } + + Account syncAccount = new Account(CalendarSyncAdapter.ACCOUNT_NAME, CalendarSyncAdapter.ACCOUNT_TYPE); + AccountManager accountManager = (AccountManager) context.getSystemService(ACCOUNT_SERVICE); + if (accountManager != null && !accountManager.addAccountExplicitly(syncAccount, null, null)) { + Log.e(TAG, "Error creating sync account, maybe account already exists"); + } + + Bundle settingsBundle = new Bundle(); + settingsBundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); + settingsBundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); + ContentResolver.requestSync(syncAccount, CalendarSyncAdapter.PROVIDER_NAME, settingsBundle); + Log.d(TAG, "syncCalendar() finished"); + } + catch (Throwable t) { + Log.e(TAG, "Error in syncCalendar()", t); + } + } +} diff --git a/app/src/main/java/net/moasdawiki/app/CalendarSyncAdapterService.java b/app/src/main/java/net/moasdawiki/app/CalendarSyncAdapterService.java new file mode 100644 index 0000000..66460e8 --- /dev/null +++ b/app/src/main/java/net/moasdawiki/app/CalendarSyncAdapterService.java @@ -0,0 +1,46 @@ +/* + * MoasdaWiki App + * Copyright (C) 2008 - 2020 Herbert Reiter (herbert@moasdawiki.net) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moasdawiki.app; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +import org.jetbrains.annotations.Nullable; + +public class CalendarSyncAdapterService extends Service { + + private static CalendarSyncAdapter syncAdapter = null; + private static final Object syncAdapterLock = new Object(); + + @Override + public void onCreate() { + synchronized (syncAdapterLock) { + if (syncAdapter == null) { + syncAdapter = new CalendarSyncAdapter(getApplicationContext(), true, false); + } + } + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return syncAdapter.getSyncAdapterBinder(); + } +} diff --git a/app/src/main/java/net/moasdawiki/app/Constants.java b/app/src/main/java/net/moasdawiki/app/Constants.java new file mode 100644 index 0000000..c100d52 --- /dev/null +++ b/app/src/main/java/net/moasdawiki/app/Constants.java @@ -0,0 +1,37 @@ +/* + * MoasdaWiki App + * Copyright (C) 2008 - 2020 Herbert Reiter (herbert@moasdawiki.net) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moasdawiki.app; + +/** + * Enthält zentrale Konstanten. + * + * @author Herbert Reiter + */ +public abstract class Constants { + public static final String PREFERENCES_SYNC_SERVER_HOST = "sync_server_host"; + public static final String PREFERENCES_SYNC_SERVER_PORT = "sync_server_port"; + public static final String PREFERENCES_SYNC_SERVER_NAME = "sync_server_name"; + public static final String PREFERENCES_SYNC_SERVER_VERSION = "sync_server_version"; + public static final String PREFERENCES_SYNC_SERVER_HOST_DISPLAYNAME = "sync_server_host_displayname"; + public static final String PREFERENCES_SYNC_SERVER_SESSION_ID = "sync_server_session_id"; + public static final String PREFERENCES_SYNC_CLIENT_SESSION_ID = "sync_client_session_id"; + public static final String PREFERENCES_SYNC_SERVER_SESSION_AUTHORIZED = "sync_server_session_authorized"; + public static final String PREFERENCES_SYNC_SERVER_TIME = "last_sync_server_time"; + public static final String PREFERENCES_CALENDAR_ENABLED = "calendar_enabled"; +} diff --git a/app/src/main/java/net/moasdawiki/app/MainActivity.java b/app/src/main/java/net/moasdawiki/app/MainActivity.java new file mode 100644 index 0000000..6fa90c9 --- /dev/null +++ b/app/src/main/java/net/moasdawiki/app/MainActivity.java @@ -0,0 +1,510 @@ +/* + * MoasdaWiki App + * Copyright (C) 2008 - 2020 Herbert Reiter (herbert@moasdawiki.net) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moasdawiki.app; + +import android.annotation.SuppressLint; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.util.Log; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.webkit.CookieManager; +import android.webkit.WebBackForwardList; +import android.webkit.WebChromeClient; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.view.menu.MenuBuilder; +import androidx.preference.PreferenceManager; + +import net.moasdawiki.base.ServiceException; +import net.moasdawiki.base.Settings; +import net.moasdawiki.plugin.Plugin; +import net.moasdawiki.plugin.PluginService; +import net.moasdawiki.server.HttpRequest; +import net.moasdawiki.server.HttpResponse; +import net.moasdawiki.service.render.HtmlService; +import net.moasdawiki.service.repository.RepositoryService; +import net.moasdawiki.util.EscapeUtils; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.URLDecoder; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Steuert das Verhalten des Hauptfensters inkl. eingebettetem Browser. + * + * @author Herbert Reiter + */ +public class MainActivity extends AppCompatActivity { + + private static final String TAG = "MainActivity"; + private static final String SERVER_BASE_URL = "http://localhost:1/"; + + private Settings settings; + private RepositoryService repositoryService; + private PluginService pluginService; + private HtmlService htmlService; + private SynchronizeWikiClient synchronizeWikiClient; + + private WebView webview; + + private long backButtonPressedTimestamp; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main_layout); + + // AndroidMainService holen + WikiEngineApplication app = (WikiEngineApplication) getApplication(); + settings = app.getServiceLocator().getSettings(); + repositoryService = app.getServiceLocator().getRepositoryService(); + pluginService = app.getServiceLocator().getPluginService(); + htmlService = app.getServiceLocator().getHtmlService(); + synchronizeWikiClient = app.getSynchronizeWikiClient(); + + // eingebetteten Browser konfigurieren + initWebView(); + + EditText searchInput = findViewById(R.id.search_input); + searchInput.setOnEditorActionListener((TextView v, int actionId, KeyEvent event) -> { + // event==null for on screen keyboard + // filter for key up from physical keyboard + if (event == null || event.getAction() == KeyEvent.ACTION_DOWN) { + onSearch(null); + return true; + } else { + return false; + } + }); + } + + /** + * Konfiguriert den eingebetteten Browser. + */ + @SuppressLint("SetJavaScriptEnabled") + private void initWebView() { + // Cookies deaktivieren + CookieManager cookieManager = CookieManager.getInstance(); + cookieManager.setAcceptCookie(false); + + // localhost-URLs in WebView anzeigen + webview = findViewById(R.id.web_browser); + webview.setWebChromeClient(new WebChromeClient()); + webview.setWebViewClient(new WebViewClient() { + /** + * Steuert, welche Links im eingebetteten Browser und welche im externen Browser + * geöffnet werden sollen. + */ + @Override + public boolean shouldOverrideUrlLoading(@NotNull WebView view, @NotNull WebResourceRequest webResourceRequest) { + Uri uri = webResourceRequest.getUrl(); + String host = uri.getHost(); + if ("localhost".equals(host)) { + // lokale URL im eingebetteten Browser öffnen + return false; + } else { + // externe Links im normalen Browser öffnen + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + startActivity(intent); + return true; + } + } + + /** + * Emuliert den Wikiserver und zu vermeiden, dass ein TCP-Port geöffnet werden muss. + */ + @Override + @Nullable + public WebResourceResponse shouldInterceptRequest (@NotNull WebView view, @NotNull WebResourceRequest request) { + try { + // URL-Pfad ermitteln + Uri uri = request.getUrl(); + String encodedUrl = uri.toString(); + String url = URLDecoder.decode(encodedUrl, "UTF-8"); + if (!url.startsWith(SERVER_BASE_URL)) { + return null; // Daten per HTTP laden + } + String urlPath = url.substring(SERVER_BASE_URL.length() - 1); // ersten "/" behalten + int hashPos = urlPath.indexOf('#'); + if (hashPos >= 0) { + // cut off anchor beginning with '#' + urlPath = urlPath.substring(0, hashPos); + } + + // per URL-Mapping das zuständige Plugin aufrufen + HttpResponse response; + Plugin plugin = pluginService.getPluginByUrl(urlPath); + if (plugin != null) { + HttpRequest httpRequest = new HttpRequest(); + httpRequest.clientIP = InetAddress.getLocalHost(); + httpRequest.httpHeader = Collections.emptyMap(); + httpRequest.method = "GET"; + httpRequest.url = urlPath; + httpRequest.urlPath = urlPath; + httpRequest.urlParameters = convertParameters(uri); + httpRequest.httpBody = new byte[0]; + + response = plugin.handleRequest(httpRequest); + if (response == null) { + response = htmlService.generateErrorPage(404, "wiki.plugin.handleRequest.notsupported", plugin.getClass().getName()); + } + } else { + // unbekannte URL + response = htmlService.generateErrorPage(404, "wiki.server.url.unmapped", urlPath); + } + + // Antwortdaten einspeisen + InputStream responseData = new ByteArrayInputStream(response.getContent()); + return new WebResourceResponse(response.getContentType(), + "UTF-8", responseData); + } catch (IOException e) { + e.printStackTrace(); + } + + return null; // Daten per HTTP laden + } + + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + hideProgressBar(); + } + }); + + // weitere Einstellungen + WebSettings webSettings = webview.getSettings(); + webSettings.setAllowFileAccess(false); + webSettings.setJavaScriptEnabled(true); + } + + @NotNull + private Map convertParameters(@NotNull Uri uri) { + Map result = new HashMap<>(); + for (String name : uri.getQueryParameterNames()) { + String value = uri.getQueryParameter(name); + result.put(name, value); + } + return result; + } + + @Override + public boolean onCreateOptionsMenu(@NotNull Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_layout, menu); + if(menu instanceof MenuBuilder){ + ((MenuBuilder) menu).setOptionalIconsVisible(true); + } + return true; + } + + @Override + protected void onResume() { + super.onResume(); + updateLayoutVisibility(); + } + + @Override + public boolean onOptionsItemSelected(@NotNull MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + + switch (item.getItemId()) { + case R.id.action_synchronize: + synchronizeServer(); + return true; + case R.id.action_startpage: + loadUrl(SERVER_BASE_URL); + return true; + case R.id.action_settings: + showSettingsDialog(); + return true; + case R.id.action_help: + String pagePathHelp = getWikiserverHelpUrl(); + loadUrl(pagePathHelp); + return true; + case R.id.action_about: + showAboutDialog(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NotNull String[] permissions, @NotNull int[] grantResults) { + Log.d(TAG, "Permission granted by user: requestCode=" + requestCode + + ", permissions=" + Arrays.toString(permissions) + ", grantResults=" + Arrays.toString(grantResults)); + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + @Override + public void onBackPressed() { + // webview.canGoBack() liefert immer false + WebBackForwardList backForwardList = webview.copyBackForwardList(); + Log.d(TAG, "Back button pressed, backForwardList index == " + backForwardList.getCurrentIndex()); + if (backForwardList.getCurrentIndex() > 0) { + webview.goBack(); + } else { + long currentTimeMillis = System.currentTimeMillis(); + if (backButtonPressedTimestamp + 5000 < currentTimeMillis) { + // First click on back button or previous click was more than 5 seconds ago + Log.d(TAG, "Back button 1x, show close hint"); + showToast(getString(R.string.action_back_close_hint)); + backButtonPressedTimestamp = currentTimeMillis; + } else { + // Second click on back button within 5 seconds -> close app + Log.d(TAG, "Back button 2x, closing app"); + finish(); + } + } + } + + public void onConfigurationHintClicked(@SuppressWarnings("unused") View view) { + showSettingsDialog(); + } + + public void onSynchronizeHintClicked(@SuppressWarnings("unused") View view) { + synchronizeServer(); + } + + public void onSearch(@SuppressWarnings("unused") View view) { + EditText searchInput = findViewById(R.id.search_input); + String query = searchInput.getText().toString(); + query = query.trim(); + if (query.isEmpty()) { + return; + } + + // Remove focus + on screen keyboard from input field + searchInput.clearFocus(); + InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + if (manager != null) { + manager.hideSoftInputFromWindow(searchInput.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } + + Log.i(TAG, "Full text search for: " + query); + showProgressBarAnimation(); + String url = getWikiserverSearchUrl(query); + loadUrl(url); + + // Der Wartedialog wird nach dem Anzeigen der Suchergebnisse durch Aufruf von + // closeProgressDialog() geschlossen. + } + + private void showSettingsDialog() { + Intent intent = new Intent(this, SettingsActivity.class); + startActivity(intent); + } + + private void showAboutDialog() { + // Versionsnummer einsetzen + @SuppressLint("InflateParams") + View dialogView = getLayoutInflater().inflate(R.layout.about_layout, null); + TextView versionText = dialogView.findViewById(R.id.label_version); + versionText.setText(getString(R.string.about_version, settings.getVersion())); + int year = Calendar.getInstance().get(Calendar.YEAR); + TextView copyrightText = dialogView.findViewById(R.id.label_copyright); + copyrightText.setText(getString(R.string.about_copyright, year)); + + // Dialog anzeigen + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setView(dialogView); + builder.setTitle(R.string.app_name); + builder.setIcon(R.mipmap.ic_cow); + builder.setPositiveButton(R.string.button_close, (dialog, id) -> {}); + Dialog dialog = builder.create(); + dialog.show(); + } + + private void synchronizeServer() { + SyncNowTask syncNowTask = new SyncNowTask(); + syncNowTask.execute(); + } + + /** + * Öffnet die angegebene URL im eingebetteten Browser. + */ + private void loadUrl(@NotNull String url) { + Log.d(TAG, "Open URL " + url); + webview.loadUrl(url); + } + + @NotNull + private String getWikiserverSearchUrl(@NotNull String query) { + return SERVER_BASE_URL + "search/?text=" + EscapeUtils.encodeUrlParameter(query); + } + + @NotNull + private String getWikiserverHelpUrl() { + return SERVER_BASE_URL + "view/wiki/"; + } + + private void updateLayoutVisibility() { + // Show/hide hint on unconfigured host name + LinearLayout hostUnconfiguredHint = findViewById(R.id.hint_host_unconfigured); + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + String host = preferences.getString(Constants.PREFERENCES_SYNC_SERVER_HOST, null); + boolean hostUnconfiguredHintVisible = (host == null || host.isEmpty()); + if (hostUnconfiguredHintVisible) { + hostUnconfiguredHint.setVisibility(View.VISIBLE); + } else { + hostUnconfiguredHint.setVisibility(View.GONE); + } + + // Show/hide hint on synchronization + LinearLayout repositoryEmptyHint = findViewById(R.id.hint_repository_empty); + boolean repositoryEmptyHintVisible = (!hostUnconfiguredHintVisible && repositoryService.getFiles().size() < 3); + if (repositoryEmptyHintVisible) { + repositoryEmptyHint.setVisibility(View.VISIBLE); + } else { + repositoryEmptyHint.setVisibility(View.GONE); + } + + // Show/hide search bar and wiki view + LinearLayout searchArea = findViewById(R.id.search_area); + WebView webView = findViewById(R.id.web_browser); + boolean webViewVisible = !hostUnconfiguredHintVisible && !repositoryEmptyHintVisible; + if (webViewVisible) { + searchArea.setVisibility(View.VISIBLE); + boolean previousVisible = (webView.getVisibility() == View.INVISIBLE); + webView.setVisibility(View.VISIBLE); + + // if WebView is shown the first time, go to start page + if (!previousVisible) { + loadUrl(SERVER_BASE_URL); + } + } else { + searchArea.setVisibility(View.GONE); + webView.setVisibility(View.GONE); + } + } + + private void showProgressBarAnimation() { + ProgressBar progressBar = findViewById(R.id.search_progressbar); + progressBar.setIndeterminate(true); + progressBar.setVisibility(View.VISIBLE); + } + + private void showProgressBar(int progress, int max) { + ProgressBar progressBar = findViewById(R.id.search_progressbar); + progressBar.setIndeterminate(false); + progressBar.setMin(0); + progressBar.setMax(max); + progressBar.setProgress(progress); + progressBar.setVisibility(View.VISIBLE); + } + + private void hideProgressBar() { + ProgressBar progressBar = findViewById(R.id.search_progressbar); + progressBar.setVisibility(View.GONE); + } + + private void showToast(String message) { + Toast toast = Toast.makeText(this, message, Toast.LENGTH_SHORT); + toast.show(); + } + + /** + * Synchronisiert mit dem Wikiserver in einem separaten Thread. + */ + @SuppressLint("StaticFieldLeak") + @SuppressWarnings("NonStaticInnerClassInSecureContext") + private class SyncNowTask extends AsyncTask implements SynchronizeWikiClient.ProgressFeedback { + + @Nullable + @Override + protected Integer doInBackground(Void... params) { + try { + return synchronizeWikiClient.synchronizeRepository(this); + } catch (ServiceException e) { + Log.e(TAG, "Error synchronizing repository", e); + } + return null; + } + + @Override + public void progress(int step, int total) { + ProgressData progressData = new ProgressData(); + progressData.step = step; + progressData.total = total; + publishProgress(progressData); + } + + @Override + protected void onProgressUpdate(ProgressData... progressData) { + showProgressBar(progressData[0].step, progressData[0].total); + } + + @Override + protected void onPostExecute(@Nullable Integer filesCount) { + CalendarSyncAdapter.requestCalendarSync(MainActivity.this); + + if (filesCount == null) { + showToast(getString(R.string.settings_synchronize_failed)); + } else if (filesCount > 0) { + showToast(getString(R.string.settings_synchronize_successful, filesCount)); + + // Reset internal file caches + WikiEngineApplication app = (WikiEngineApplication) getApplication(); + app.resetServices(); + } else { + showToast(getString(R.string.settings_synchronize_not_necessary)); + } + + hideProgressBar(); + updateLayoutVisibility(); + } + } + + private static class ProgressData { + public int step; + public int total; + } +} diff --git a/app/src/main/java/net/moasdawiki/app/SettingsActivity.java b/app/src/main/java/net/moasdawiki/app/SettingsActivity.java new file mode 100644 index 0000000..96ede1b --- /dev/null +++ b/app/src/main/java/net/moasdawiki/app/SettingsActivity.java @@ -0,0 +1,42 @@ +/* + * MoasdaWiki App + * Copyright (C) 2008 - 2020 Herbert Reiter (herbert@moasdawiki.net) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moasdawiki.app; + + +import android.os.Bundle; + +import androidx.appcompat.app.AppCompatActivity; + +import org.jetbrains.annotations.Nullable; + +/** + * Settings dialog + */ +public class SettingsActivity extends AppCompatActivity { + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.settings_layout); + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.settings_layout, new SettingsFragment()) + .commit(); + } +} diff --git a/app/src/main/java/net/moasdawiki/app/SettingsFragment.java b/app/src/main/java/net/moasdawiki/app/SettingsFragment.java new file mode 100644 index 0000000..cd02f47 --- /dev/null +++ b/app/src/main/java/net/moasdawiki/app/SettingsFragment.java @@ -0,0 +1,203 @@ +/* + * MoasdaWiki App + * Copyright (C) 2008 - 2020 Herbert Reiter (herbert@moasdawiki.net) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moasdawiki.app; + +import android.annotation.SuppressLint; +import android.content.SharedPreferences; +import android.os.AsyncTask; +import android.os.Bundle; +import android.text.InputFilter; +import android.text.InputType; +import android.util.Log; +import android.widget.Toast; + +import androidx.annotation.Nullable; +import androidx.preference.EditTextPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; + +import net.moasdawiki.base.Settings; +import net.moasdawiki.service.repository.RepositoryService; + +import org.jetbrains.annotations.NotNull; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +public class SettingsFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener { + + private static final String TAG = "SettingsFragment"; + private SynchronizeWikiClient synchronizeWikiClient; + private RepositoryService repositoryService; + private Settings settings; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + //noinspection ConstantConditions + WikiEngineApplication app = (WikiEngineApplication) getContext().getApplicationContext(); + synchronizeWikiClient = app.getSynchronizeWikiClient(); + repositoryService = app.getServiceLocator().getRepositoryService(); + settings = app.getServiceLocator().getSettings(); + } + + @Override + public void onCreatePreferences(Bundle savedInstanceState, @Nullable String rootKey) { + setPreferencesFromResource(R.xml.settings_fragment, rootKey); + + EditTextPreference preference = findPreference("sync_server_port"); + if (preference != null) { + preference.setOnBindEditTextListener(editText -> { + editText.setInputType(InputType.TYPE_CLASS_NUMBER); + editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(5)}); + }); + } + } + + @Override + public void onResume() { + super.onResume(); + getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); + updateSettingsSummaries(); + updateStatusText(); + } + + @Override + public void onPause() { + super.onPause(); + getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + updateSettingsSummaries(); + + if ("sync_server_host".equals(key) || "sync_server_port".equals(key)) { + checkServerConnection(); + } + } + + private void showToast(String message) { + Toast toast = Toast.makeText(getContext(), message, Toast.LENGTH_SHORT); + toast.show(); + } + + private void updateSettingsSummaries() { + // Host name + SharedPreferences preferences = getPreferenceManager().getSharedPreferences(); + String serverHost = preferences.getString(Constants.PREFERENCES_SYNC_SERVER_HOST, null); + EditTextPreference syncServerHost = findPreference("sync_server_host"); + if (syncServerHost != null) { + if (serverHost != null && !serverHost.isEmpty()) { + syncServerHost.setSummary(serverHost); + } else { + syncServerHost.setSummary(R.string.settings_host_summary_empty); + } + } + + // Port + int serverPort = preferences.getInt(Constants.PREFERENCES_SYNC_SERVER_PORT, 0); + EditTextPreference syncServerPort = findPreference("sync_server_port"); + if (syncServerPort != null) { + if (serverPort > 0) { + syncServerPort.setSummary(Integer.toString(serverPort)); + } else { + String summary = getString(R.string.settings_port_summary_empty, settings.getServerPort()); + syncServerPort.setSummary(summary); + } + } + } + + private void updateStatusText() { + SharedPreferences preferences = getPreferenceManager().getSharedPreferences(); + String serverSessionId = preferences.getString(Constants.PREFERENCES_SYNC_SERVER_SESSION_ID, null); + boolean serverSessionAuthorized = preferences.getBoolean(Constants.PREFERENCES_SYNC_SERVER_SESSION_AUTHORIZED, false); + String serverHost = preferences.getString(Constants.PREFERENCES_SYNC_SERVER_HOST, null); + String serverHostDisplayName = preferences.getString(Constants.PREFERENCES_SYNC_SERVER_HOST_DISPLAYNAME, ""); + + // Connection status + String connectionStatus; + if (serverSessionId == null) { + connectionStatus = getString(R.string.settings_status_server_not_connected); + } else if (!serverSessionAuthorized) { + connectionStatus = getString(R.string.settings_status_server_authorization_mission); + } else { + connectionStatus = getString(R.string.settings_status_server_connected); + } + + // Server Host + String serverHostCombined = serverHostDisplayName; + if (!serverHostCombined.isEmpty() && serverHost != null) { + serverHostCombined += " (" + serverHost + ")"; + } + + // Last synchronization timestamp + long lastSyncTimeMs = preferences.getLong(Constants.PREFERENCES_SYNC_SERVER_TIME, 0); + String lastSyncStr = ""; + if (lastSyncTimeMs > 0) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH); + lastSyncStr = df.format(new Date(lastSyncTimeMs)); + } + + // Number of files in repository + int filesCount = repositoryService.getFiles().size(); + String filesCountStr = Integer.toString(filesCount); + + String htmlText = getString(R.string.settings_status_details, connectionStatus, serverHostCombined, lastSyncStr, filesCountStr); + Preference synchronizationStatus = findPreference("synchronization_status"); + if (synchronizationStatus != null) { + synchronizationStatus.setSummary(htmlText); + } + } + + private void checkServerConnection() { + new ConnectServerTask().execute(); + } + + /** + * Checks the connection to the Wiki server. + */ + @SuppressLint("StaticFieldLeak") + @SuppressWarnings("NonStaticInnerClassInSecureContext") + private class ConnectServerTask extends AsyncTask { + @Override + @NotNull + protected Boolean doInBackground(Void... params) { + SharedPreferences preferences = getPreferenceManager().getSharedPreferences(); + String host = preferences.getString(Constants.PREFERENCES_SYNC_SERVER_HOST, null); + if (host == null) { + Log.d(TAG, "Cancel ConnectServerTask because server host is not configured"); + return false; + } + + // Connect with server + return synchronizeWikiClient.createAndCheckSession(); + } + + @Override + protected void onPostExecute(@NotNull Boolean success) { + updateStatusText(); + + if (!success) { + showToast(getString(R.string.settings_search_failed)); + } + } + } +} diff --git a/app/src/main/java/net/moasdawiki/app/SynchronizeWikiClient.java b/app/src/main/java/net/moasdawiki/app/SynchronizeWikiClient.java new file mode 100644 index 0000000..12d8e7c --- /dev/null +++ b/app/src/main/java/net/moasdawiki/app/SynchronizeWikiClient.java @@ -0,0 +1,466 @@ +/* + * MoasdaWiki App + * Copyright (C) 2008 - 2020 Herbert Reiter (herbert@moasdawiki.net) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moasdawiki.app; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Build; +import android.util.Base64; +import android.util.Log; + +import androidx.preference.PreferenceManager; + +import net.moasdawiki.base.Logger; +import net.moasdawiki.base.ServiceException; +import net.moasdawiki.base.Settings; +import net.moasdawiki.plugin.ServiceLocator; +import net.moasdawiki.plugin.sync.AbstractSyncXml; +import net.moasdawiki.plugin.sync.CheckSessionResponseXml; +import net.moasdawiki.plugin.sync.CheckSessionXml; +import net.moasdawiki.plugin.sync.CreateSessionResponseXml; +import net.moasdawiki.plugin.sync.CreateSessionXml; +import net.moasdawiki.plugin.sync.ErrorResponseXml; +import net.moasdawiki.plugin.sync.ListModifiedFilesResponseXml; +import net.moasdawiki.plugin.sync.ListModifiedFilesXml; +import net.moasdawiki.plugin.sync.ReadFileResponseXml; +import net.moasdawiki.plugin.sync.ReadFileXml; +import net.moasdawiki.plugin.sync.SingleFileXml; +import net.moasdawiki.service.repository.AnyFile; +import net.moasdawiki.service.repository.RepositoryService; +import net.moasdawiki.util.DateUtils; +import net.moasdawiki.util.xml.XmlGenerator; +import net.moasdawiki.util.xml.XmlParser; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import java.util.Date; +import java.util.Enumeration; + +/** + * Sucht einen Wikiserver in Netzwerk und synchronisiert alle Wikidateien + * im eigenen Repository.. + * + * @author Herbert Reiter + */ +public class SynchronizeWikiClient { + + private static final String TAG = "SynchronizeWikiClient"; + + private static final String PROTOCOL_VERSION = "2.0"; + + @NotNull + private final Context mContext; + @NotNull + private final Logger logger; + @NotNull + private final Settings settings; + @NotNull + private final RepositoryService repositoryService; + @NotNull + private final SecureRandom random = new SecureRandom(); + + public SynchronizeWikiClient(@NotNull Context mContext, @NotNull ServiceLocator serviceLocator) { + this.mContext = mContext; + this.logger = serviceLocator.getLogger(); + this.settings = serviceLocator.getSettings(); + this.repositoryService = serviceLocator.getRepositoryService(); + } + + /** + * Verbindet sich mit dem Wikiserver. Ist bereits eine Serversession vorhanden, wird diese + * weiter verwendet. + */ + public boolean createAndCheckSession() { + PreferenceManager.getDefaultSharedPreferences(mContext); + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext); + String host = preferences.getString(Constants.PREFERENCES_SYNC_SERVER_HOST, null); + int port = preferences.getInt(Constants.PREFERENCES_SYNC_SERVER_PORT, settings.getServerPort()); + String serverSessionId = preferences.getString(Constants.PREFERENCES_SYNC_SERVER_SESSION_ID, null); + + // Host konfiguriert? + if (host == null) { + return false; + } + String serverHostPort = host + ':' + port; + + try { + // Wenn noch keine Session vorhanden ist, eine erzeugen + boolean createSessionCalled = false; + if (serverSessionId == null) { + createSessionCalled = true; + createSession(serverHostPort); + } + + // Ist Session freigeschaltet zur Synchronisierung? + boolean[] checkResult = checkSession(serverHostPort); + if (checkResult[0]) { + return true; + } + + // Session ist ungültig --> neue Session holen + if (createSessionCalled) { + // createSession nicht erneut aufrufen + return false; + } + createSession(serverHostPort); + + // Session erneut prüfen + boolean[] checkResult2 = checkSession(serverHostPort); + return checkResult2[0]; + } + catch (ServiceException e) { + return false; + } + } + + private void createSession(@NotNull String serverHostPort) throws ServiceException { + // Anfrage schicken + CreateSessionXml createSessionXml = new CreateSessionXml(); + createSessionXml.version = PROTOCOL_VERSION; + createSessionXml.clientSessionId = generateSessionId(); + createSessionXml.clientName = "MoasdaWiki-App"; + createSessionXml.clientVersion = settings.getVersion(); + createSessionXml.clientHost = getDeviceName(); + String hostName = getLocalHostname(); + if (hostName != null) { + createSessionXml.clientHost += " / " + hostName; + } + String requestXml = generateXml(createSessionXml); + String responseXml = sendXmlRequest(serverHostPort, "/sync/create-session", requestXml); + + // Antwort auswerten + CreateSessionResponseXml response = parseXml(responseXml, CreateSessionResponseXml.class); + Log.d(TAG, "Current sync session ID '" + response.serverSessionId + "'"); + + // Session prüfen + if (response.serverSessionId == null) { + throw new ServiceException("Didn't get a server session ID"); + } + + SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(mContext); + SharedPreferences.Editor editor = settings.edit(); + editor.putString(Constants.PREFERENCES_SYNC_SERVER_NAME, response.serverName); + editor.putString(Constants.PREFERENCES_SYNC_SERVER_VERSION, response.serverVersion); + editor.putString(Constants.PREFERENCES_SYNC_SERVER_HOST_DISPLAYNAME, response.serverHost); + editor.putString(Constants.PREFERENCES_SYNC_SERVER_SESSION_ID, response.serverSessionId); + editor.putString(Constants.PREFERENCES_SYNC_CLIENT_SESSION_ID, createSessionXml.clientSessionId); + // Synchronisierungszeiten können nicht mehr genutzt werden + editor.remove(Constants.PREFERENCES_SYNC_SERVER_TIME); + editor.remove(Constants.PREFERENCES_SYNC_SERVER_SESSION_AUTHORIZED); + editor.apply(); + } + + private String generateSessionId() { + return new BigInteger(130, random).toString(32); + } + + @NotNull + private boolean[] checkSession(@NotNull String serverHostPort) throws ServiceException { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext); + String serverSessionId = preferences.getString(Constants.PREFERENCES_SYNC_SERVER_SESSION_ID, null); + String clientSessionId = preferences.getString(Constants.PREFERENCES_SYNC_CLIENT_SESSION_ID, null); + + // Anfrage schicken + CheckSessionXml checkSessionXml = new CheckSessionXml(); + checkSessionXml.version = PROTOCOL_VERSION; + checkSessionXml.serverSessionId = serverSessionId; + String requestXml = generateXml(checkSessionXml); + String responseXml = sendXmlRequest(serverHostPort, "/sync/check-session", requestXml); + + // Antwort auswerten + CheckSessionResponseXml response = parseXml(responseXml, CheckSessionResponseXml.class); + if (response.valid == null || !response.valid) { + Log.d(TAG, "Sync server session ID '" + serverSessionId + "' is not valid any more"); + SharedPreferences.Editor editor = preferences.edit(); + editor.remove(Constants.PREFERENCES_SYNC_SERVER_SESSION_ID); + editor.remove(Constants.PREFERENCES_SYNC_CLIENT_SESSION_ID); + editor.apply(); + return new boolean[]{ false, false }; + } + if (clientSessionId != null && !clientSessionId.equals(response.clientSessionId)) { + Log.d(TAG, "Sync server authentication failed, client session ID does not match"); + SharedPreferences.Editor editor = preferences.edit(); + editor.remove(Constants.PREFERENCES_SYNC_SERVER_SESSION_ID); + editor.remove(Constants.PREFERENCES_SYNC_CLIENT_SESSION_ID); + editor.apply(); + return new boolean[]{ false, false }; + } + + SharedPreferences.Editor editor = preferences.edit(); + boolean authorized = response.authorized != null && response.authorized; + editor.putBoolean(Constants.PREFERENCES_SYNC_SERVER_SESSION_AUTHORIZED, authorized); + editor.apply(); + return new boolean[]{ true, authorized }; + } + + /** + * Synchronisiert alle Repository-Dateien mit dem Wikiserver. Vorher wird geprüft, ob + * die Server-Session noch gültig ist und wir mit demselben Server verbunden sind. + * + * @return Anzahl der synchronisierten Dateien. + */ + public int synchronizeRepository(ProgressFeedback feedback) throws ServiceException { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext); + String host = preferences.getString(Constants.PREFERENCES_SYNC_SERVER_HOST, null); + int port = preferences.getInt(Constants.PREFERENCES_SYNC_SERVER_PORT, settings.getServerPort()); + if (host == null) { + throw new ServiceException("No wiki server configured"); + } + String serverSessionId = preferences.getString(Constants.PREFERENCES_SYNC_SERVER_SESSION_ID, null); + if (serverSessionId == null) { + throw new ServiceException("Not server session ID found"); + } + long lastSyncServerTimeMs = preferences.getLong(Constants.PREFERENCES_SYNC_SERVER_TIME, 0); + Date lastSyncServerTime = null; + if (lastSyncServerTimeMs > 0) { + lastSyncServerTime = new Date(lastSyncServerTimeMs); + } + + // Aktuelle Session überprüfen + String serverHostPort = host + ':' + port; + boolean[] checkResult = checkSession(serverHostPort); + if (!checkResult[1]) { + throw new ServiceException("Client not authenticated at wiki server"); + } + + // Anfrage schicken + ListModifiedFilesXml listModifiedFilesXml = new ListModifiedFilesXml(); + listModifiedFilesXml.version = PROTOCOL_VERSION; + listModifiedFilesXml.serverSessionId = serverSessionId; + listModifiedFilesXml.lastSyncServerTime = DateUtils.formatUtcDate(lastSyncServerTime); + String requestXml = generateXml(listModifiedFilesXml); + String responseXml = sendXmlRequest(serverHostPort, "/sync/list-modified-files", requestXml); + + // Antwort auswerten + ListModifiedFilesResponseXml response = parseXml(responseXml, ListModifiedFilesResponseXml.class); + int fileCount = response.fileList.size(); + Log.d(TAG, "Downloading " + fileCount + " files from server"); + if (fileCount == 0) { + // no files to download, cancel process + return 0; + } + + for (int i = 0; i < fileCount && !feedback.isCancelled(); i++) { + feedback.progress(i, fileCount); + SingleFileXml serverFile = response.fileList.get(i); + try { + downloadFileFromServer(serverHostPort, serverSessionId, serverFile.filePath); + } + catch (ServiceException e) { + Log.w(TAG, "Error reading file from server, ignoring it", e); + } + } + + // Reset internal caches + WikiEngineApplication app = (WikiEngineApplication) mContext.getApplicationContext(); + app.resetServices(); + + // Update last sync time + if (!feedback.isCancelled()) { + Date currentServerDate = DateUtils.parseUtcDate(response.currentServerTime); + if (currentServerDate != null) { + long newLastSyncServerTimeMs = currentServerDate.getTime(); + SharedPreferences.Editor editor = preferences.edit(); + editor.putLong(Constants.PREFERENCES_SYNC_SERVER_TIME, newLastSyncServerTimeMs); + editor.apply(); + } + } + + return fileCount; + } + + private void downloadFileFromServer(@NotNull String serverHostPort, @NotNull String serverSessionId, @NotNull String filePath) throws ServiceException { + // Anfrage schicken + ReadFileXml readFileXml = new ReadFileXml(); + readFileXml.version = PROTOCOL_VERSION; + readFileXml.serverSessionId = serverSessionId; + readFileXml.filePath = filePath; + String requestXml = generateXml(readFileXml); + String responseXml = sendXmlRequest(serverHostPort, "/sync/read-file", requestXml); + + // Datei aus Antwort speichern + ReadFileResponseXml response = parseXml(responseXml, ReadFileResponseXml.class); + byte[] fileContent = Base64.decode(response.content, Base64.NO_WRAP); + Date fileTimestamp = DateUtils.parseUtcDate(response.timestamp); + AnyFile anyFile = new AnyFile(filePath); + repositoryService.writeBinaryFile(anyFile, fileContent, fileTimestamp); + Log.d(TAG, "File '" + filePath + "' replaced by newer content from server"); + } + + /** + * Versucht den Wifi-Hostnamen zu ermitteln und gibt ihn zurück. + */ + @Nullable + private String getLocalHostname() { + try { + String wlanHostname = null; + String ethHostname = null; + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + while (networkInterfaces.hasMoreElements()) { + NetworkInterface ni = networkInterfaces.nextElement(); + Enumeration inetAddresses = ni.getInetAddresses(); + while (inetAddresses.hasMoreElements()) { + InetAddress ia = inetAddresses.nextElement(); + // Indikator: Hostname und Adresse sind unterschiedlich + // http://stackoverflow.com/questions/21898456/get-android-wifi-net-hostname-from-code + if (!ia.getCanonicalHostName().equals(ia.getHostAddress())) { + if ("wlan0".equals(ni.getDisplayName())) { + wlanHostname = ia.getCanonicalHostName(); + } else if ("eth0".equals(ia.getHostAddress())) { + ethHostname = ia.getCanonicalHostName(); + } + } + } + } + if (wlanHostname != null) { + Log.d(TAG, "Hostname (wlan0): " + wlanHostname); + return wlanHostname; + } + if (ethHostname != null) { + Log.d(TAG, "Hostname (eth0): " + ethHostname); + return ethHostname; + } + Log.d(TAG, "No hostname found"); + } catch (SocketException e) { + Log.e(TAG, "Error determining hostname", e); + } + return null; + } + + @NotNull + private String getDeviceName() { + String manufacturer = Build.MANUFACTURER; + String model = Build.MODEL; + if (model.startsWith(manufacturer)) { + return capitalize(model); + } else { + return capitalize(manufacturer) + " " + model; + } + } + + private String capitalize(String s) { + if (s == null || s.isEmpty()) { + return ""; + } + return Character.toUpperCase(s.charAt(0)) + s.substring(1); + } + + /** + * Schickt einen XML-Request und liest die XML-Antwort ein. + * + * @param urlPath HTTP-URL, nicht null + * @param requestXml XML-Anfrage, nicht null. + * @return XML-Antwort, nicht null. + */ + @NotNull + private String sendXmlRequest(@NotNull String serverHostPort, @NotNull String urlPath, @NotNull String requestXml) throws ServiceException { + try { + String url = "http://" + serverHostPort + urlPath; + Log.d(TAG, "Request to " + url + ": " + truncateLogText(requestXml, 200)); + byte[] requestBytes = requestXml.getBytes(StandardCharsets.UTF_8); + + URI uri = new URI(url); + HttpURLConnection conn = (HttpURLConnection) uri.toURL().openConnection(); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "text/xml"); + conn.setRequestProperty("Content-Length", Integer.toString(requestBytes.length)); + conn.setUseCaches(false); + conn.setDoOutput(true); + conn.setDoInput(true); + conn.setConnectTimeout(1000); // 1 Sekunde + conn.setReadTimeout(10000); // 10 Sekunden + conn.connect(); + + OutputStream out = conn.getOutputStream(); + out.write(requestBytes); + out.flush(); + + InputStream in = conn.getInputStream(); + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(in.available()); + int bytesRead; + byte[] buffer = new byte[1024]; + while ((bytesRead = in.read(buffer)) != -1) { + byteStream.write(buffer, 0, bytesRead); + } + byte[] responseBytes = byteStream.toByteArray(); + + String responseXml = new String(responseBytes, StandardCharsets.UTF_8); + Log.d(TAG, "Response: " + truncateLogText(responseXml, 400)); + return responseXml; + } catch (Exception e) { + Log.e(TAG, "Error sending XML request", e); + throw new ServiceException("Error sending XML request", e); + } + } + + private String truncateLogText(String logText, int maxLength) { + if (logText.length() <= maxLength) { + return logText; + } + return logText.substring(0, maxLength) + '…'; + } + + /** + * Wandelt eine JAXB-Bean in einen XML-Strom um. + */ + @NotNull + private String generateXml(@NotNull AbstractSyncXml xmlBean) throws ServiceException { + XmlGenerator xmlGenerator = new XmlGenerator(logger); + return xmlGenerator.generate(xmlBean); + } + + /** + * Wandelt einen XML-Strom in eine JAXB-Bean um. + */ + @NotNull + private T parseXml(@NotNull String xml, @NotNull Class xmlBeanType) throws ServiceException { + try { + XmlParser xmlParser = new XmlParser(logger); + return xmlParser.parse(xml, xmlBeanType); + } catch (ServiceException e) { + logger.write("Failed to parse XML for class " + xmlBeanType.getSimpleName() + ", try class ErrorResponseXml", e); + // Versuche eine Fehlerantwort zu parsen + XmlParser xmlParser = new XmlParser(logger); + ErrorResponseXml errorResponseXml = xmlParser.parse(xml, ErrorResponseXml.class); + throw new ServiceException(errorResponseXml.message); + } + } + + /** + * Interface, um den Fortschritt während der Synchronisierung mit dem Server + * zurückzuübermitteln. + */ + public interface ProgressFeedback { + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + boolean isCancelled(); + void progress(int step, int total); + } +} diff --git a/app/src/main/java/net/moasdawiki/app/WikiEngineApplication.java b/app/src/main/java/net/moasdawiki/app/WikiEngineApplication.java new file mode 100644 index 0000000..6ee60d4 --- /dev/null +++ b/app/src/main/java/net/moasdawiki/app/WikiEngineApplication.java @@ -0,0 +1,93 @@ +/* + * MoasdaWiki App + * Copyright (C) 2008 - 2020 Herbert Reiter (herbert@moasdawiki.net) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moasdawiki.app; + +import android.app.Application; + +import net.moasdawiki.base.Logger; +import net.moasdawiki.base.Messages; +import net.moasdawiki.base.Settings; +import net.moasdawiki.plugin.PluginService; +import net.moasdawiki.plugin.ServiceLocator; +import net.moasdawiki.service.render.HtmlService; +import net.moasdawiki.service.repository.FilesystemRepositoryService; +import net.moasdawiki.service.repository.RepositoryService; +import net.moasdawiki.service.search.SearchService; +import net.moasdawiki.service.wiki.WikiService; +import net.moasdawiki.service.wiki.WikiServiceImpl; + +import java.io.File; + +/** + * Verwaltet den Lebenszyklus der Wiki Engine. Muss außerhalb der Activities erfolgen, weil diese + * z.B. beim Drehen des Bildschirm neu erzeugt werden. + * + * @author Herbert Reiter + */ +public class WikiEngineApplication extends Application { + + private Logger logger; + private ServiceLocator serviceLocator; + private SynchronizeWikiClient synchronizeWikiClient; + + @Override + public void onCreate() { + super.onCreate(); + + logger = new Logger(); + logger.write("MoasdaWiki app starting"); + + File internalStorageRepositoryRoot = new File(getFilesDir(), "repository"); + RepositoryService repositoryService = new FilesystemRepositoryService(logger, internalStorageRepositoryRoot); + repositoryService.init(); + WikiService wikiService = new WikiServiceImpl(logger, repositoryService); + SearchService searchService = new SearchService(logger, wikiService); + Settings settings = new AndroidSettings(logger, repositoryService, AndroidSettings.getConfigFileApp()); + Messages messages = new Messages(logger, settings, repositoryService); + PluginService pluginService = new PluginService(logger, settings); + HtmlService htmlService = new HtmlService(logger, settings, messages, wikiService, pluginService); + serviceLocator = new ServiceLocator(logger, settings, messages, repositoryService, wikiService, htmlService, searchService, pluginService); + + pluginService.loadPlugins(serviceLocator); + + synchronizeWikiClient = new SynchronizeWikiClient(this, serviceLocator); + } + + public void resetServices() { + serviceLocator.getRepositoryService().rebuildCache(); + serviceLocator.getWikiService().reset(); + serviceLocator.getSettings().reset(); + serviceLocator.getMessages().reset(); + serviceLocator.getPluginService().loadPlugins(serviceLocator); + } + + @Override + public void onTerminate() { + logger.write("MoasdaWiki app stopping"); + super.onTerminate(); + } + + public ServiceLocator getServiceLocator() { + return serviceLocator; + } + + public SynchronizeWikiClient getSynchronizeWikiClient() { + return synchronizeWikiClient; + } +} diff --git a/app/src/main/res/drawable-anydpi/ic_action_event.xml b/app/src/main/res/drawable-anydpi/ic_action_event.xml new file mode 100644 index 0000000..4ade0e8 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_action_event.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable-anydpi/ic_action_sync.xml b/app/src/main/res/drawable-anydpi/ic_action_sync.xml new file mode 100644 index 0000000..45fc1b4 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_action_sync.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable-anydpi/ic_action_sync_white.xml b/app/src/main/res/drawable-anydpi/ic_action_sync_white.xml new file mode 100644 index 0000000..2f3d983 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_action_sync_white.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable-hdpi/ic_action_event.png b/app/src/main/res/drawable-hdpi/ic_action_event.png new file mode 100644 index 0000000000000000000000000000000000000000..b9141615f905d38926c8ba849ffef031399459c2 GIT binary patch literal 255 zcmV}myA?_1Zbppp zStOa;H<0)K974E2t8r000*XF!p(fO3!5CxixKImau$9{4Q)`$aQm3r7ixd!y?+^$M zNdb{ofdUN;RCV2>t3-jS0_A0ORiM8BqW<3F1F;hnNP&J8Xz!t)Qkunqnk~lefbeaf zzaLy5b|k3DdgOyO>B#=6u(*N$|K`jX$=iVyZ~Wl=X4i;qy7K@002ovPDHLk FV1kXCX}bUb literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-hdpi/ic_action_sync.png b/app/src/main/res/drawable-hdpi/ic_action_sync.png new file mode 100644 index 0000000000000000000000000000000000000000..92ec8fc01a05b4d7b441e4738e2fe82e727d76e7 GIT binary patch literal 503 zcmV4qH5Qdf3QYu6I|b7^kPg!-P#FwX7>1v;cC?p9QS?f$!$yGUM?Z9! zvgP~!7p>&#V5bzS0L$r~apO>n#p2eotVaTv(EF9H7JdJ%)hvtSc!ZeZP-3O%sqeEa zyD_apwr$_h{+hc_6jPq(y_1n;9jH_LfYOvoj9~C2kXad7(no#MUIzDR@$`F@=*B5C zvXRCd4|LQi6NOR+G?4&wM+t5C$pwNB*g?1!K)uEPc>r+r_|11<5wsg!GbJszsGWRj) z{ngKD7_K-}5$MPq3}qP3_+BGWN~yf==-QN0h^eGRt@&^Nms-m#+W-002ovPDHLkV1jfo>9qg= literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-hdpi/ic_action_sync_white.png b/app/src/main/res/drawable-hdpi/ic_action_sync_white.png new file mode 100644 index 0000000000000000000000000000000000000000..0cae6ac3444692cc5eae5e70f0b5edebd226cdc3 GIT binary patch literal 444 zcmV;t0YmnDhg`JoQ$vLIkncWHZ`ysW zQVCZ^VVMh*8gth&>^?gB0*=s*K&}Gs+>!7$96(9{v4GH;@D3cnod8w>LQBHCaR3hj zmEKqw}>lmplhz)?WxN_aaCAizni zhJ@UWel@A$u*}c+^Sp=#yKiP=};DGR8;Q mKZG)8KFv5K6@kgoqD@R2Wnc5hTQby;KlL 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 KMNUMnLSTZ0o&22u literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-hdpi/ic_menu_info_details.png b/app/src/main/res/drawable-hdpi/ic_menu_info_details.png new file mode 100644 index 0000000000000000000000000000000000000000..6a7a1e94a731c8865dba132ee9aafd04c14e7d10 GIT binary patch literal 1983 zcmZ{ldpy$%8^C|FC6g`JF!x&utC_7WVo0u8ZYP%=mtmBM32jNFa(|UXIOdXO=~Oy4 z)5#^L$!#H3vIsoX{6GVeP7y??xaJkR&@JfG+LdH#EzOgEwfOd25#007L%(au9m z)US|&i1YRyQyDQNf?XW!fSvT4uV08OXpE!p1u-4}4g0QhR==7FF*f01{_6rg^EuuC zAlcz$XXE9=oAdUMQS(+3^^6s8j7bDhUL?PrfUhC(P2rjU#FeP9h-xFdt2uYjeO=&V zE4ysNX%&_87#%QO8b&Fqk=2n5?d1Iw|@v0fbtJJBVVqz2zR#7ZI(&FjBFBpP{#hQAjtS) zp@mwcKYAP*{M)SpLWS)LRTKL>n*QnP9wU9g9CKaPL@J;xYpDvuo+x|_RP;5h7hbO0 z|28EeoSHp&?x3=C3Zz{odHM3?1O$k=vqW|fsu<81fc#g$qPr?^VuL#|7ST9G_M5tX z(9vo;YHkGs8{W}W1v~Z8-vCA!)FamsZ#851aEpB%$hn^-wG2t|pnW0xsFcbXuCp%O zbr>{Ta)Bs~i~uLScx)kQ^c^Yr)6t;%aBWf6#r~FcYm+J+kagbRMpMGfh3Z5rEhwoq-9mzj<6auHUxY?WKsrQg>^RHWWZ%~X0x}Blx1w{GRP1= zf=2TyRO_#SX+q9E{P%J1JG3y{J`ZOk2>3etAj7hrb*UV(CD~{gqTIYdgxIfKR&i$C zZJ4MWCowayX7^`o7V72rT60gD!Z<zWd-p6$19q)H({tW8 z2wdAKOJi=cw(Q1?eS4LxiUX?>O_EXN z<(QXu6pj;f$zk2Ja+511+CL1d)0!+r1N3ebV26B4+ZFdE0Az3vuukjEBTUZ~BSy+o zXe9|s;fSK5yI}2B_tb~VUTEUvnW95KtEiP`fMg5)+7d1#oVqVPL}e7w^W&Skvgm-n zIl>Bz4sdg)9PXMH%BP(1&6|OO%uDPlR*hQQ8UM5>>RMKf8gfsqamR0WBY*sj(k2OQr?2e=-;0gHOAN-^i8;>eN7D+vm=Px zG1LB}Z1hMI`mCpzj?F4fPE$dt$Q;3s`b9=yER+9p)dN)T>D%OV)T9K(n}VSDjuwSm z-#Mu4nCLX>{ad-HPmrmD-|H|1s*lF!G4JDb5O)|%@U24TL1#}#2LKNhX3#Z6a;Cgl zeQoK`-@k5xQHzrh4ix;rz%!sfw>|kk&sfq^pU@xh4%$_!KBv<{?VNeD!cq*g)9;|mCHGk+lR^LzM zwH3{MkGwCemhnd!G6I~fhp@kw&j+Vu_{geW2(PDjh%DID%76!Otasz{xS=f*ipa^;k0h755hNwgFmC^OcW7r{!GP zycF3)6MOug5!~+(m(150QqEDOkE=_r!70sxdIKE7eXoTu-PnWcAFC(ePTnAsx_l1Q zc;=XvK=(^3h+Y$xoJ^Ps7i#9dDF~Kl`WHYP96hq_vxRD9LG4LU@r0Z~e2>3dUusw+ z_h%{28M$G`2Ag|m5fgGlws>7Vii@Swm$vN^m#Pn~WdrcZRkiM#VI>9Xd4S(t$k^cG zoPWsXfR_9wh81tRr(CmTpR@MDdM(Fb3zB)wfi*bm7ca{w6n@eTZv%%n?8je`gcbFt zBj_}II)Rx)-DxW8ls@dVBy1qPIDbjzWl_EbhiCJyBR3#%S?G6N`Kh)B?%ZQnB?5a+ zxA>|)EO?6>XHw-5MAkm8wfN_SyY_=+>xB0@8tknW<0}^x=eS%Kic8x9{^v@=B(OgscQ Mogmshu?IX?8Yd{AVtm0RVvC9BpjRqU!hH zzGX_pm$fG^13KW8N31bocZd4=ZMN|Nu4+_E&U<8(FB+V1>i$0o?nhqcY_&3bGLUNpwN$TzRm z4@&f-2zNeJ4lrxR3LkZ-clMk{u^cWLb$vUzzvpDO|VT8)v~Pue)#P`rtComA>u^5 z-hU6;%P;BF#6KI&I&-_P`}k}shE1NZOJSE{9aPTd@?8Q>@U2~;3!_s>XNY=0`s;DC zf5TK?E?~$;w8%8kO9Zl|Cr5WmdH~V}_5_mFNrASUJ1O6BfkuTQIEYRjD`v6)SSNDT zBj#cbH&r1JmXXr2Q&a)(EGi(?rtuo0`tCWNcxTo8Ehy!IcFpmjNCpHdf7pt~@cK>B zu(@omJ8`j9VxYC!IJfzdj8yK@|zslb0%qbk*0tTh}#Xqsm znn;0)t~A&rF&;~T_15dX=;qA^Id+X}nlFZhvjJCGr!_Zha-CgJ2{UpiHi1u`n|)+? zYkJeV0)Fq7Na|!`ORV?}Y{uY2%5W#FZDz1!e3q317}9U@OT2`t0ONuZp(Gyawi1bFI9@MKDHo&=Wl>-rLeU7Vi8dKyk6s=&Az6 zkFKbCq2F^oA1q{&hNX|UptG0HCLkMJW`dRH))#oIrD=JA0UE-0OG)(7(qZ@ZIh+=( z<|lo6^ddAb!s?Bn64>B?Fxgel##z3BD9tu=s4~kl*A~G&PUh&E|FPm%*Vio`kvKk4 zMc9)4@!-)BBP(xxk(#(iul2$-&bjV&g>i8}TA_>`)o|qcvP!6Um`%fm-ldcsbLA%lsP@5(24;c%|!PX6xjrmgtyfs2}#p?L1_%n`qlF*uJWaul%2aZG%9P zi1PEsxFf-sdY`hQeV-+X<)G8TD1+Iu?XEjL4spHnw+<(tw!i|BfS^^Xf5V)!*`N%i zt#YJu21(`W4ZA25H$36dP3G%a;TOWl-WFf}@(bB`g}Ql=V)fuy;k6W#$geSs{*YPy z1>L=>(WK1mbeD?1&Kd&DO>B%QC|trnDzi)4 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-hdpi/ic_menu_search.png b/app/src/main/res/drawable-hdpi/ic_menu_search.png new file mode 100644 index 0000000000000000000000000000000000000000..ae2f44bc81edf26a2eda82182a0d261a45fb9c52 GIT binary patch literal 2966 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 Xco(Aw^+;|&A{+O{K(l7@6Jy^8yP%Z{an^L HB{Ts5f4wwc literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_action_sync.png b/app/src/main/res/drawable-mdpi/ic_action_sync.png new file mode 100644 index 0000000000000000000000000000000000000000..88cdf46abaaeac12d63ac5b65eb2b2afc045999b GIT binary patch literal 334 zcmV-U0kQsxP)7@mKAp|wa&4Y&?6-qBw+Q<0x?%)cc)Vfgv?LrV@vXh;eY@+Cwq9_U# zG`Y3*88mqi05>^?G3HL+CeL$|W!Y(S0C&*82Yqed_q)wNs;b&+trH}sYC4|3c!5xY zSR!&+mS>v3F^Cl<2d@NhRZ1NtHU$VO0bL?Z>=58rODi7tKzPaG28+S72c-!gs-9gu z3YVto!ACO&XeEwmxq?^|zMzWxL@}TD%8VgXK^zEGV%mEDdXm zTZd!S+dBx)E6L@Y3|xC$|4}%gfoHS{rw^@r8yLiLLz`-L>mMNS%EC9?s<#FBpij8u g-$ee8hkr@D0JZORljp&AJ^%m!07*qoM6N<$f=9!PSpWb4 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_action_sync_white.png b/app/src/main/res/drawable-mdpi/ic_action_sync_white.png new file mode 100644 index 0000000000000000000000000000000000000000..8c2eef8390f8f9ae2336f28401e1c804c14ce2c0 GIT binary patch literal 289 zcmV++0p9+JP) zRdT{vmAr~TN)D_9Pp|}CLmk4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE>PQkPZ!4!kK;$DoX!@B6lvcdyj^SRlD8Vm0+UUA zYO-A}w7y7c>FHVE$7ClKDCoi(zAnw!yYzAVf*k@e9c&?Yb{cFJ`e?}4HK9eqv&6%w za>ZS>+}VERudWpu_Fc#gHBs(if4AtJ?f&=k?(e+P_+Iu?t1;u3$FoGQ{dDAE`@!5` zDO4VMvy7>K&Xt!64x*nu7d$x;z@W0K&tu)n4XY#fFIu>=Y~lKzC-e77zFJo~LAKv% z3S&=1YU(3zYYLgh5tZ`J2%N0@RgXi>5#y1u2v zH%3K$k!t9SpaT;amesxW6Bi9Gxjb){m%;_Nv&-N07cSj(LZ0l zFI&f}zm82w=|x$;+YXs&YP(bCOL4T8^YdTn=jFBkRyAAQ>e*b|i<@*TdAmK-?w?GY zD<}}K+OAc7F7v@?uEr9Ls9YO{O+J2iFVv>y{yyV0E79oSszBY2z}SGp^Mt##o0(3` zzsC3OczN2I2lE4}Tv)P$W^Vf|-6yo-ymWEi3_Xok;S3InDpM_LDr2)P*2-BHtK6@h z9k|5nvvKr~=@)V`c3!w5C3bZ(V|neoOH)7X*dG`m^?lBsnLKN>=bpQFabI?c z(p0Ojk*lP+SKJTl4dp$ve*Ld?A9#+p1PiV#6jPY^$za~>kGt+&;=h`ueECkqfge9R zcVD{|6IwP$KrPwLD}{ej&XVINbvBj$m^AnQrG5HFlEwXa>QWZRN6Vp?JQWH}u3s0un02MKKy85}Sb4q9e E05UX@*#H0l literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_menu_home.png b/app/src/main/res/drawable-mdpi/ic_menu_home.png new file mode 100644 index 0000000000000000000000000000000000000000..f19f58dd9298a1a2762ffb365e2d4c7825447b9a GIT binary patch literal 1046 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~IzoTrOph{y4}A(0+a0wrwk@BM#6$5LvRsn1OA zDPcmQqAvoRFQ#=bbIk}yQQ8(D_(j0>tB?!7Yk~ zbKD?@(ae>7lX;4tY`lDC7H@@T%QOyE2bVJ*a#O;MQg@%3^071A?bq#()12HSH5?u= zJv`+R9cPxc@5amBYx7M~j@^^I{BPlct4Ent_r(faSiDB|bGQYc4|@vv8BTi_YlD($Df-(OBo|K+l9 z{k7a@+je@-b@!c+y}R$~0psLbje^Ow4#7uuU3+CUBh11}X<=CPxv-n-wtZz=?9pHs zxxM;R`O^2j853@v*Iu0v_u59t`q280%o(S8>+C~6M%16u(h5fgma~f_ zf-BF@aqhnMV~U^1y5>>???Q`38_pD+-P~~hL+J}ChPm33wke&(8a3^F94}bzF4QTs zy=x&E!E6>_5vS ze{*qU@QB$P9hK@1U^?Tr&b;J+_ZM5Uobrz7;e}F)R>h=d$8Ac0c$xoaYbo4-VI8{g}BcALs(JjB1H%L`h0wNvc(H zQ7VvPFfuSS)HN{FHL?gXGO{u;w=y->HL$QUFj(SObP`2FZhlH;S|x4`SA|cN0yRj2 uYzWRzD=AMbN@Z|N$xljE@XSq2PYp^k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~JeT~8Os5RcKO*~zCK~&O-7Ao~AbGr#YwNN@>wa^8idq+xeyUAxLKAoKuMdaPg4%UB_!b2A z2??HJ{Q0av;M?w97ST3b#-$rdIc~qEdhUx#{R6h7)tgNm6`NO_c$fRz?`i(U z8FPI985vqIx9*$n^n0&h?)1aXYoCAJl%d$%%^CMh-BGhXw|;ZJm;J1~7nANkl{aP# z^9X-Ajajv{F;&R7?*0MQB{BNCryK(2-T!&+=+pX)FMj#OrEj#=uS>^svzoh2Vt=cz zoW1C*cH9Kp!@Bc7KWEm>`L4PuB{j76bIi1Nk}p;-x1K)x+nIyk@89|O=zQXGc{rz&hk^3rj;tcC+Kly`KqOj**EsK1w83JwrB6W;%f=7*Q%Hu z7EZ3u|6yS~ueP+|cGA+Qtfd?>lbM&g|H(BelkJi{n%!`+Z}!&Yt;w?HGg;PKo>~}@ z94_tt$EB;lhwEiwy8B7J*wCz6BiHD?g5JCd?SCVg692F0^?ar2&AYaDwmt7OvGTOj z6<+MwjzL*tiSmku*= z*a$ZqtQ6AIdVBlgxxgE8Nvba*j-Ozi$_VpI$C96`T zj|&}P+pu=Zsyxm`4&P7CGh4|m7QTURIp?!S%BGi1FMkw#HtRrQY@uuH24BB#7v3t# zr999$^yW<~f49D#eYi;N>g^}D7%%^t{#l`7`cBKl&$BWwS&Z8H~w|8`F zHwFFs)u{G<$v*R|cM?l4r7t_cwdvLrx6^jqZ7Q?=tZVc>p|^L_jR3BW#Y(%kzqg(n zzwGU&(oLyKc8f)Rm9=T@oxRnWkN^L-wQ*v)3j3xgH?|5+DZLkdKd%1Fe#Ntw*1hgL zB5r$9{wMdx7d5{UbhEZEn78I9Z_u{Pt4%BWO220w7oPFzZ{pd2*(y>>{Q47~tn;ZT zoOj_`u*{}276(iO7;@C+|4FKR@n&QA?aTfD8LA#GJkPNHbY%}>cptHiD0s_>~&paw~h u4Z-k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~H|ou`Xqh{y4#A+b4=LnZ3&eXfmvd8^knnmajp zwXv%2Nuv!d=N4S^G~w2`$g{&h$54r-*Ui`9qDv3cL<{4K6^-{K_8PR!R#iK^Q({G) z!L*$(I9wux6IVwr+;R5S)vb5G&z(1&d$+v8g_m5ZK1x5#bzk0^`0urxR$=whB zJz=ekXUqn_WlG66Kc_ZlMs8td7HiyS_vg#>cTp#OSPTSxCD*Rpx41*}?Exi&Q?Iwx zJzQt%yQ#C|%_LQ(tesQm94ZOqHk-jzX|gXOTwgaaxufsNc6BeKNXA1Vla8`@FjZ!3 zS$4C*mBnSHec!<~Mai2!JUPd-!QGhEm(ORn|OR!_DA53IQaBy*Ck zHMh%i%C6iIUzqqvvW-)t?NF?hLc`6M2D4WkersyYvN=gBhP`A}+LNh_krBFu(V0y7 zLJ{io?Y9_5-CnfUtT_MA6r~^qhTe$xuBrPc9E*8*wAz8Gi}7J-|GTW5@@o%_D-&!A zg{Dh?oAYg_4yT&)p6Qk4cQkz+{+Onf9-OmX;Y6Fv^pLc}z6<%jt63dh8*I-v#dX55 zS)M6Vxo0V~{;B&NXMf^g+s3u~(mD3OEVkF%d1W5=&cp4m=Lr_xGyQy(^*>_5St74T0_kJ8tFr&%XGiqQ|YE)tI62z%KW^o02v$uiTecDSymlii)+dg!8ETLU-W43EUJizgCZoUfJ3ntc80yOi&i=_zSVd+u!7 z{jeih)o$whBkR70@~ZkYi?D`DwRkhE(s1PN4!Qg)e{w*4aJha}3-@h#4%xLgJOsC< z7X8S%nYpA+U8(z^^XF;8HsbnL3>iEOXREfp&nkBPapRSlVVCU7bBT{d9%XU_&G1(~ zrmMhEcHTRk*-rQte^$WW9AOKK-w!?BRsTQw;{NSo=}bctffTlSmZ;nl)h`_%@2E1m zcK7Y8Q!&1d7w?{ASYVmBQ2AeD+0uP=H_t!1+1X#c*3WX$r>;+~Ct116Odijfw!_~< zVKtj!+w53|Jyql51QubpI~&@49t?MC9V-ADTyViR>?)F zK#IZ0z|c_Fz);u7BE-nZ%Ea8t)L7TR!pgv4iC@u46b-rgDVb@NxHViAK2-|TAPKS| sI6tkVJh3R1!7(L2DOJHUH!(dmC^a#qvhZZ84Nwt-r>mdKI;Vst0F6rPu>b%7 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_menu_search.png b/app/src/main/res/drawable-mdpi/ic_menu_search.png new file mode 100644 index 0000000000000000000000000000000000000000..d18f54227ed2f5f7b88c528270dd136ef557c558 GIT binary patch literal 1704 zcmZ`)eK^$V8h(e2&oCA>NKEpv8_lG$21C;feoGT0l6*7D$24gM4P%-eGx>a-ao zIwvPsjal05(JZBr1$H8=-@QcV>3>5)7M895{3U07;4dYMd5)Mlh*gS zK(;*qJOKcI!YSS~02x>SW@7=sUjtyv>B@V-o^SyfAK*sXPk?{7Zt1*E?M@^qRP``?)@xF9U%U)`Los3Dxj~DDhW09K|Wmc|; zw2mh2{^;C=rqj`{Wz2i$Mm4I*v()BvK5Icc*#w$uqO&v2MONdwac*uS^!*iw zHL*`LdYY~Dk4J=rFs~LUUB2%%ynzOWR$n(w4k_ke{!~|0pnOuYU9~9)EnyDa4Lehe z>s-$EDp^gPo}Fz*mELS=Zq=nDdNak>?q8qa;WUfF*NbF7Vyfwa>mzT#S z2ZyJpr&~oY74L&geHusn=tLdIB4SZ3Da$6}RYH84sh-e3G*rj-1#6K8O02yG?Y*{H zT80GLf8kmlhy>Z-5{><`w#+WM8WIYH-T12S&gbX1gX>~T8{baG_%9M0$YkzSTaG?Y z1B6FL{+C$bC@|&=iMDhu1bc1|?qz&ry#BXbCe2TbX1~QeS4@a100k%C6$B%mybaAJ8%#Ks3gD9X%N=)oX z{SJ*r>jFYoceguRJwI^@Gb?Uv{A}SfQzi{FLwWcI29g|2`Ta>bocDcbw9kCa!F&>v z$^3J5b=70XjveoPY=ZjS&v|=!t-Z_BHsX#B4PA~13IgCtSy^o>XXQ*Jc59yrG3_et zr9*GCg`RNq8|YWezw98L{C3CPyGtH06_h-A%8!Js`O3o;5Bj1n ztVzJo;NX`Q78Z-U%mav-nLpcLii?W{6Q|UVq^ToF&2MEvG}@FOrOtIManEfMsTjvq zj!J@Zaz3e@ot)WCydoetGB&tL4``8@@ETg#rvelF< zK?SRy-}Li9aNw$4GK!mQ*q~w3d11>l72sZ+c2saON5oNk@pwHzmP(~7FxLU2p{PWm zhf035Q1^U%sqy(%1^X(=ytU{6$4|wSv@vsnC>N8Jm0cD^oGhPbQQ9 z>Dcqy=DH`lS$gM=+6W}TQXLE#-XsV@VRhAugP5I2wV9(Z)`59%&wGD7gFdyJ2O+Ry5gth){2^(+zP8Kx^?dg4>VB?Zxlg=Ztm3W3exq3UvR6KxZT-C$axOp>7Tfg)whc|$2)wTBB&0jz6ddtrD+25x{AV`&cTCC6| SUgOmXAjO`pelF{r5}E*)G*Kh~ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_action_sync.png b/app/src/main/res/drawable-xhdpi/ic_action_sync.png new file mode 100644 index 0000000000000000000000000000000000000000..3defe6d08b78533eb84b2ad83b74817b27739013 GIT binary patch literal 623 zcmV-#0+9WQP)4qH5N+BDt8$5)nM)BKBlUz%S^J@uznlRoD($n~zzNDK6v?S;u+v zW@g`x7Z>OCk5?FmzG~;=IQ~-Yyh;G3^XdR_=T!m7b6yPqoAY?0DB9BVLC>7t zyx;FHRXA^Z=sO#LNz?R3B>9jbveFP)s!1Qpw(11aIfKkF9=6t<}I6s;~rYga~H zmJo9uX9C@6I-TB%0U(2kL2)S%1q0OKHG$t0aelR0U6Jv%Y25eyC+QR&4u@~77S|n^iWm)~ zovqEzK*)j>xrj>U>L9}*$?C0N+bWK8E+ug6OM4G_!li^*$1}y;k%gk&sEUFW6?0gS zwvf22#id13sfvO{`xHMgammQJ%f^3!s3OD5T%O9-!XjrhlAcN@mU_*O+i~HxUB+=y z&toHJZ?hmU`ZzTjL0Vyif>8=aDwch%U}8~onAI!@)C3BfcPCXKj%*wzZX1;VOhzy| z2N(#z!$^Ir!ucZSfJ*0E*+%OCQ2D>HYUitnxz;V!&YxF-^$lsboXSu8dl&!!002ov JPDHLkV1ibu8E^mq literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_action_sync_white.png b/app/src/main/res/drawable-xhdpi/ic_action_sync_white.png new file mode 100644 index 0000000000000000000000000000000000000000..d8bdd7d0b689b0070eb51edb2766e706309735f0 GIT binary patch literal 543 zcmV+)0^t3LP)!d%~sa>3rk}o_zjdaTDzN=wNzunhq^;DCS)=o;7njPlHA7N zeRCMzdl%?*-p4;a?)Uqb>Ec0&>EcZSWQ#WkK#Mm8z!h%>Ag_4T1@r@oK_O@c>RBni z2r7m7unO?d736~)%Mht{jd!B}!UYXMpLpB+=A3r|ja$Er!f##B5)0-a+SQ4I^U}^l zIk%IeQP@N}N?&>Ec_tjJ^n9@4K<-;_HOD hHUii!{(Y1<{s30OIfNmV0?PmZ002ovPDHLkV1o02>q!6r literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_menu_help.png b/app/src/main/res/drawable-xhdpi/ic_menu_help.png new file mode 100644 index 0000000000000000000000000000000000000000..128c7e807dc04d89fb65d9906e36208138d3e135 GIT binary patch literal 1840 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 Pj+%fArq-x>6A#KiIGGGZ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_menu_preferences.png b/app/src/main/res/drawable-xhdpi/ic_menu_preferences.png new file mode 100644 index 0000000000000000000000000000000000000000..02cfbad0b08d9276615ba7b25ca7f745dc3cfe4d GIT binary patch literal 2507 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 PIRr2@vP9Jz;Nt!U*I|wz literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_menu_search.png b/app/src/main/res/drawable-xhdpi/ic_menu_search.png new file mode 100644 index 0000000000000000000000000000000000000000..44444950d65040336b716b5e68dc67cb2fe8ad78 GIT binary patch literal 4281 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 B@5ulF literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_event.png b/app/src/main/res/drawable-xxhdpi/ic_action_event.png new file mode 100644 index 0000000000000000000000000000000000000000..cbb790cb0f31c9e4d17be01d1d999df8c4729e1e GIT binary patch literal 341 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@Zgyv2V3hH6aSW-r_4f8zuOkK$tQQW> z-Pe9@o9`_rc?X%}YXknvE$h!?R^BmVCAYB^=kr7E(|!3DZfV?ov%`b)WD1eGU~@h^_Ij=}2mk4>Pp>@E%boVsuiZ5Nvhf_&beZF?pR)Z93h?u-YLj^E z`)%&!ZEurbTHK$w>D`iLnR(d$$O=ID>@Lnjnh8ZG$iiJyY4>J?X3a$mke`=VR;HsevOMWJn7y6A| zD`kMrd}Eyvsq;dN>hF1a?NtJS`SG_t V9=(?{zX%vy44$rjF6*2UngIDyh`ay* literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_sync.png b/app/src/main/res/drawable-xxhdpi/ic_action_sync.png new file mode 100644 index 0000000000000000000000000000000000000000..299c08a3000e258bcbdf8c99bdc00dbb9dedc50b GIT binary patch literal 918 zcmV;H18Mw;P)TVpAKm%G1ti?eUN3jMo4Z+9 z0%3nasS|Rn5>2!Laa15P(=O=*+YPH%kg(Gybs9XBo&Tu$< zu7KpNc-v?+zOGiQM+!*>^*acH7Ya%CPdD`Wq41C>T0Ma^^!xo|ieev|&E|-{zfK70 zp8o$+8HM6L{jExUk_1$tA(_Tf1gp^anaPq0yWNtG9}EUxA`q-kDx~9n$awNmZzcbP z=;l-cAO)z`>qjmNX5?>PNsyqEoWP;gdrB+xmp(7Egp_ij-f`OvVv^=~XdO_m*K4dN zktL)p7VNX&k2+T2K&wQ;Y?(ErEyvF|E3%s4(nC+Nmi^6j zWaPvo0s3WS0x(t1A<`Cm;{l*5CmyX7-3Y>aW&&3(x7Yt^32pTSW8o)Oqt{07`XWH1 z1UC*)Xu}cyU;v%^0*DJDWaVcz1mZ>9!^h+CYhM7NRKhswxsCkkimBrqcm(JrBSRLs zKb%(KF7Dxr#p1CqfRF|;7Ifzf5GLgdAS{+MKwJogS^EMA+g*?%xm(bj0d(vOAOsA? z$}em<>9bu8^N=VubWq3?%10gn>c-6xBG*b4Q3#=4q#B$QL7HU60Sd+Ps%tt(n7L%t zT1^P00|wBn*@qa!p)0{<4pe2c@TlAqKwIy@@&f}S>JMt9c@BFc^GA4 zt4oDKIA=vDpl=a@mRUox61`#+^`T%z_c)443n)uS(b1HfGzf=s!WsEB4)xLk$_kQ| zDk9f#tc&Aa2@;Nn(*nu@lJkZO0-I-Ko7)W+4blQC1SDKFk#1V7qgxUQAdKH_NVv3= z@VeY2Qr!Cw0aO@BCQI6I;v-zML#YH?6G=x0{?VZ0^3}Hbggt1PK70#ED6T z6g9jqfV2d!4=5mWf*at46or$Z=!O&kcS)l literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_sync_white.png b/app/src/main/res/drawable-xxhdpi/ic_action_sync_white.png new file mode 100644 index 0000000000000000000000000000000000000000..23158a29c2498102d3c508e7adcf7a911da20ece GIT binary patch literal 801 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_menu_home.png b/app/src/main/res/drawable-xxhdpi/ic_menu_home.png new file mode 100644 index 0000000000000000000000000000000000000000..23c67d080e816472f36c95f5241bdb3284caab69 GIT binary patch literal 3868 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 DiFU5n literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_menu_info_details.png b/app/src/main/res/drawable-xxhdpi/ic_menu_info_details.png new file mode 100644 index 0000000000000000000000000000000000000000..4414bea7e3ca7af8e29730fe2301b21eab09aa2e GIT binary patch literal 4891 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 LV|0a%bIkt$uYj^g literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_menu_preferences.png b/app/src/main/res/drawable-xxhdpi/ic_menu_preferences.png new file mode 100644 index 0000000000000000000000000000000000000000..b03953772cdda3bf6cdb4946ff25e24b7149d133 GIT binary patch literal 4407 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 TouSU`{;4oEw!)QTU8DX3y#tp> literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/ic_info_outline_black.xml b/app/src/main/res/drawable/ic_info_outline_black.xml new file mode 100644 index 0000000..cf53e14 --- /dev/null +++ b/app/src/main/res/drawable/ic_info_outline_black.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/about_layout.xml b/app/src/main/res/layout/about_layout.xml new file mode 100644 index 0000000..933256f --- /dev/null +++ b/app/src/main/res/layout/about_layout.xml @@ -0,0 +1,29 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/main_layout.xml b/app/src/main/res/layout/main_layout.xml new file mode 100644 index 0000000..bf1e191 --- /dev/null +++ b/app/src/main/res/layout/main_layout.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/settings_layout.xml b/app/src/main/res/layout/settings_layout.xml new file mode 100644 index 0000000..8274687 --- /dev/null +++ b/app/src/main/res/layout/settings_layout.xml @@ -0,0 +1,7 @@ + + + + diff --git a/app/src/main/res/menu/menu_layout.xml b/app/src/main/res/menu/menu_layout.xml new file mode 100644 index 0000000..a9fa5d3 --- /dev/null +++ b/app/src/main/res/menu/menu_layout.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/mipmap-hdpi/ic_cow.png b/app/src/main/res/mipmap-hdpi/ic_cow.png new file mode 100644 index 0000000000000000000000000000000000000000..2813dff1d7bef4aab3272edcd3b9a337b4685359 GIT binary patch literal 9027 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-mdpi/ic_cow.png b/app/src/main/res/mipmap-mdpi/ic_cow.png new file mode 100644 index 0000000000000000000000000000000000000000..30d257f246f9362d40a2bd17ab6f03ef50b12308 GIT binary patch literal 4628 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 KMNUMnLSTZl7xvcx literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/ic_cow.png b/app/src/main/res/mipmap-xhdpi/ic_cow.png new file mode 100644 index 0000000000000000000000000000000000000000..db8177d2ecaf39a1c71b24136930c18b1a94cfb0 GIT binary patch literal 15087 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 Rtycg5002ovPDHLkV1h-;#CZS! literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_cow.png b/app/src/main/res/mipmap-xxhdpi/ic_cow.png new file mode 100644 index 0000000000000000000000000000000000000000..50c33f361acc785fb5ff69e6ba2e4d1c43b43a40 GIT binary patch literal 31398 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

-%r|WQ*6%%Q{8& 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 + + Copyright %1$d © Herbert Reiter + Version %1$s + Hilfe + Über + Erneut drücken um die App zu beenden. + Einstellungen + Startseite + Synchronisieren + Schließen + MoasdaWiki Termine + Datum: + Bitte erteile die Berechtigung %1$s damit es funktioniert + Kalender fertig aktualisiert + Vielen Dank für die Installation der MoasdaWiki App. Bitte konfiguriere nun die Serververbindung, um die Wikiseiten synchronisieren zu können. Drücke hier um die Einstellungen zu öffnen. + Die App hat noch keine Wikiseiten. Drücke hier um jetzt mit dem MoasdaWiki-Server zu synchronisieren. + Durchsuche Wiki + Suchen + Einstellungen + Mit Wikiserver synchronisieren + Hostname + Gib den Hostnamen des Rechners auf dem der MoasdaWiki server läuft an. + Host-Port + %1$d (Standardwert) + Status der Synchronisierung + + Nicht verbunden + Am Server angemeldet + Erfordert Berechtigung am Server + Keinen Wikiserver gefunden! + %1$d Dateien erfolgreich synchronisiert + Dateien sind bereits aktuell + Synchronisierung nicht möglich, bitte überprüfen Sie die Einstellungen und den Status! + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..5c1e384 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,3 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..7442c59 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,36 @@ + + MoasdaWiki App + Copyright %1$d © Herbert Reiter + https://www.moasdawiki.net/ + Version %1$s + About + Press again to exit the app. + Help + Settings + Start page + Synchronize + Close + MoasdaWiki Events + Date + Please grant permission %1$s to make it work + Calendar update finished + Thank you for installing the MoasdaWiki App. Please configure the server connection to synchronize the Wiki pages. Tap here to go to settings. + The App has still no Wiki content. Tap here to synchronize with the MoasdaWiki server now. + Search in Wiki pages + Search + Settings + Synchronize with Wiki server + Host name + Enter the host name of a MoasdaWiki server instance. + Host port + %1$d (default value) + Synchronization status + + Not connected + Connected to server + Needs authorization at server + No Wiki server found! + Successfully synchronized %1$d files + All files are up-to-date + Synchronization failed, please check settings and status! + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..0f18e07 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/app/src/main/res/xml/authenticator.xml b/app/src/main/res/xml/authenticator.xml new file mode 100644 index 0000000..7edf0c0 --- /dev/null +++ b/app/src/main/res/xml/authenticator.xml @@ -0,0 +1,6 @@ + + diff --git a/app/src/main/res/xml/settings_fragment.xml b/app/src/main/res/xml/settings_fragment.xml new file mode 100644 index 0000000..b47f09c --- /dev/null +++ b/app/src/main/res/xml/settings_fragment.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/syncadapter.xml b/app/src/main/res/xml/syncadapter.xml new file mode 100644 index 0000000..b151c08 --- /dev/null +++ b/app/src/main/res/xml/syncadapter.xml @@ -0,0 +1,8 @@ + +