otherLikelyBundles) {
+ if (mClient == null) return false;
+
+ CustomTabsSession session = getSession();
+ if (session == null) return false;
+
+ return session.mayLaunchUrl(uri, extras, otherLikelyBundles);
+ }
+
+ public boolean warmup(int flags) {
+ return mClient.warmup(flags);
+ }
+
+ /**
+ * A Callback for when the service is connected or disconnected. Use those callbacks to
+ * handle UI changes when the service is connected or disconnected
+ */
+ public interface ConnectionCallback {
+ /**
+ * Called when the service is connected
+ */
+ void onCustomTabsConnected();
+
+ /**
+ * Called when the service is disconnected
+ */
+ void onCustomTabsDisconnected();
+ }
+
+ /**
+ * To be used as a fallback to open the Uri when Custom Tabs is not available
+ */
+ public interface CustomTabFallback {
+ /**
+ * @param activity The Activity that wants to open the Uri
+ * @param uri The uri to be opened by the fallback
+ */
+ void openUri(Activity activity, Uri uri);
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/github/dfa/diaspora_android/web/custom_tab/CustomTabsHelper.java b/app/src/main/java/com/github/dfa/diaspora_android/web/custom_tab/CustomTabsHelper.java
new file mode 100644
index 000000000..aaa89f06e
--- /dev/null
+++ b/app/src/main/java/com/github/dfa/diaspora_android/web/custom_tab/CustomTabsHelper.java
@@ -0,0 +1,151 @@
+/*
+ This file is part of the dandelion*.
+
+ dandelion* 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.
+
+ dandelion* 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 the dandelion*.
+
+ If not, see .
+ */
+package com.github.dfa.diaspora_android.web.custom_tab;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.support.customtabs.CustomTabsService;
+import android.text.TextUtils;
+
+import com.github.dfa.diaspora_android.util.AppLog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class for Custom Tabs. Adapted from https://medium.com/ribot-labs/exploring-chrome-customs-tabs-on-android-ef427effe2f4
+ */
+public class CustomTabsHelper {
+ private static final String TAG = "CustomTabsHelper";
+ static final String STABLE_PACKAGE = "com.android.chrome";
+ static final String BETA_PACKAGE = "com.chrome.beta";
+ static final String DEV_PACKAGE = "com.chrome.dev";
+ static final String LOCAL_PACKAGE = "com.google.android.apps.chrome";
+ static final String CHROMIUM = "org.chromium.chrome";
+ static final String FENNEC = "org.mozilla.fennec_fdroid";
+ static final String KLAR = "org.mozilla.klar";
+ private static final String EXTRA_CUSTOM_TABS_KEEP_ALIVE =
+ "android.support.customtabs.extra.KEEP_ALIVE";
+
+ private static String sPackageNameToUse;
+
+ private CustomTabsHelper() {
+ }
+
+ /**
+ * Goes through all apps that handle VIEW intents and have a warmup service. Picks
+ * the one chosen by the user if there is one, otherwise makes a best effort to return a
+ * valid package name.
+ *
+ * This is not threadsafe.
+ *
+ * @param context {@link Context} to use for accessing {@link PackageManager}.
+ * @return The package name recommended to use for connecting to custom tabs related components.
+ */
+ public static String getPackageNameToUse(Context context) {
+ if (sPackageNameToUse != null) return sPackageNameToUse;
+
+ PackageManager pm = context.getPackageManager();
+ // Get default VIEW intent handler.
+ Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
+ ResolveInfo defaultViewHandlerInfo = pm.resolveActivity(activityIntent, 0);
+ String defaultViewHandlerPackageName = null;
+ if (defaultViewHandlerInfo != null) {
+ defaultViewHandlerPackageName = defaultViewHandlerInfo.activityInfo.packageName;
+ }
+
+ // Get all apps that can handle VIEW intents.
+ List resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);
+ List packagesSupportingCustomTabs = new ArrayList<>();
+ for (ResolveInfo info : resolvedActivityList) {
+ Intent serviceIntent = new Intent();
+ serviceIntent.setAction(CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION);
+ serviceIntent.setPackage(info.activityInfo.packageName);
+ if (pm.resolveService(serviceIntent, 0) != null) {
+ packagesSupportingCustomTabs.add(info.activityInfo.packageName);
+ }
+ }
+
+ // Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents
+ // and service calls.
+ if (packagesSupportingCustomTabs.isEmpty()) {
+ sPackageNameToUse = null;
+ } else if (packagesSupportingCustomTabs.size() == 1) {
+ sPackageNameToUse = packagesSupportingCustomTabs.get(0);
+ } else if (!TextUtils.isEmpty(defaultViewHandlerPackageName)
+ && !hasSpecializedHandlerIntents(context, activityIntent)
+ && packagesSupportingCustomTabs.contains(defaultViewHandlerPackageName)) {
+ sPackageNameToUse = defaultViewHandlerPackageName;
+ } else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
+ sPackageNameToUse = STABLE_PACKAGE;
+ } else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
+ sPackageNameToUse = BETA_PACKAGE;
+ } else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
+ sPackageNameToUse = DEV_PACKAGE;
+ } else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
+ sPackageNameToUse = LOCAL_PACKAGE;
+ } else if (packagesSupportingCustomTabs.contains(CHROMIUM)) {
+ sPackageNameToUse = CHROMIUM;
+ } else if (packagesSupportingCustomTabs.contains(FENNEC)) {
+ sPackageNameToUse = FENNEC;
+ } else if (packagesSupportingCustomTabs.contains(KLAR)) {
+ sPackageNameToUse = KLAR;
+ }
+ return sPackageNameToUse;
+ }
+
+ /**
+ * Used to check whether there is a specialized handler for a given intent.
+ *
+ * @param intent The intent to check with.
+ * @return Whether there is a specialized handler for the given intent.
+ */
+ private static boolean hasSpecializedHandlerIntents(Context context, Intent intent) {
+ try {
+ PackageManager pm = context.getPackageManager();
+ List handlers = pm.queryIntentActivities(
+ intent,
+ PackageManager.GET_RESOLVED_FILTER);
+ if (handlers == null || handlers.size() == 0) {
+ return false;
+ }
+ for (ResolveInfo resolveInfo : handlers) {
+ IntentFilter filter = resolveInfo.filter;
+ if (filter == null) continue;
+ if (filter.countDataAuthorities() == 0 || filter.countDataPaths() == 0) continue;
+ if (resolveInfo.activityInfo == null) continue;
+ return true;
+ }
+ } catch (RuntimeException e) {
+ AppLog.e(TAG, "Runtime exception while getting specialized handlers");
+ }
+ return false;
+ }
+
+ /**
+ * @return All possible chrome package names that provide custom tabs feature.
+ */
+ public static String[] getPackages() {
+ return new String[]{"", STABLE_PACKAGE, BETA_PACKAGE, DEV_PACKAGE, LOCAL_PACKAGE, CHROMIUM, FENNEC, KLAR};
+ }
+}
diff --git a/app/src/main/java/de/baumann/diaspora/FloatingActionsMenuBehavior.java b/app/src/main/java/de/baumann/diaspora/FloatingActionsMenuBehavior.java
deleted file mode 100644
index 1f36ccce5..000000000
--- a/app/src/main/java/de/baumann/diaspora/FloatingActionsMenuBehavior.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package de.baumann.diaspora;
-
-/**
- * Created by juergen on 29.02.16.
- */
-import android.content.Context;
-import android.support.design.widget.CoordinatorLayout;
-import android.support.design.widget.Snackbar.SnackbarLayout;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.getbase.floatingactionbutton.FloatingActionButton;
-import com.getbase.floatingactionbutton.FloatingActionsMenu;
-
-public class FloatingActionsMenuBehavior extends CoordinatorLayout.Behavior {
-
- public FloatingActionsMenuBehavior(Context context, AttributeSet attrs) {
- }
-
- @Override
- public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionsMenu child, View dependency) {
- return dependency instanceof SnackbarLayout;
- }
-
- @Override
- public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionsMenu child, View dependency) {
- float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
- child.setTranslationY(translationY);
- return true;
- }
-}
diff --git a/app/src/main/java/de/baumann/diaspora/MainActivity.java b/app/src/main/java/de/baumann/diaspora/MainActivity.java
deleted file mode 100644
index cae3c8389..000000000
--- a/app/src/main/java/de/baumann/diaspora/MainActivity.java
+++ /dev/null
@@ -1,1149 +0,0 @@
-/*
- This file is part of the Diaspora Native WebApp.
-
- Diaspora Native WebApp 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.
-
- Diaspora Native WebApp 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 the Diaspora Native WebApp.
-
- If not, see .
- */
-
-package de.baumann.diaspora;
-
-import android.Manifest;
-import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Picture;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Handler;
-import android.provider.MediaStore;
-import android.support.annotation.NonNull;
-import android.support.design.widget.NavigationView;
-import android.support.design.widget.Snackbar;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v4.widget.SwipeRefreshLayout;
-import android.support.v7.app.ActionBarDrawerToggle;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.webkit.JavascriptInterface;
-import android.webkit.JsResult;
-import android.webkit.ValueCallback;
-import android.webkit.WebChromeClient;
-import android.webkit.WebSettings;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import android.widget.EditText;
-import android.widget.ProgressBar;
-import android.widget.RadioButton;
-import android.widget.RadioGroup;
-import android.widget.TextView;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import de.baumann.diaspora.utils.Helpers;
-import de.baumann.diaspora.utils.PrefManager;
-
-public class MainActivity extends AppCompatActivity
- implements NavigationView.OnNavigationItemSelectedListener {
-
-
- private static final String URL_MESSAGE = "URL_MESSAGE";
- final Handler myHandler = new Handler();
- WebView webView;
- static final String TAG = "Diaspora Main";
- String podDomain;
- Menu menu;
- int notificationCount = 0;
- int conversationCount = 0;
- ValueCallback mFilePathCallback;
- String mCameraPhotoPath;
- public static final int INPUT_FILE_REQUEST_CODE = 1;
- com.getbase.floatingactionbutton.FloatingActionsMenu fab;
- TextView txtTitle;
- ProgressBar progressBar;
- WebSettings wSettings;
- PrefManager pm;
- private SwipeRefreshLayout swipeView;
- final private int REQUEST_CODE_ASK_PERMISSIONS = 123;
-
- @SuppressLint("SetJavaScriptEnabled")
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if (android.os.Build.VERSION.SDK_INT >= 21)
- WebView.enableSlowWholeDocumentDraw();
-
- setContentView(R.layout.activity_main);
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
- ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
- this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
- drawer.setDrawerListener(toggle);
- toggle.syncState();
-
- NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
- navigationView.setNavigationItemSelectedListener(this);
-
- progressBar = (ProgressBar)findViewById(R.id.progressBar);
- pm = new PrefManager(MainActivity.this);
-
- SharedPreferences config = getSharedPreferences("PodSettings", MODE_PRIVATE);
- podDomain = config.getString("podDomain", null);
-
- fab = (com.getbase.floatingactionbutton.FloatingActionsMenu) findViewById(R.id.multiple_actions);
- fab.setVisibility(View.GONE);
-
- swipeView = (SwipeRefreshLayout) findViewById(R.id.swipe);
- swipeView.setColorSchemeResources(R.color.colorPrimary,
- R.color.fab_big);
-
- webView = (WebView)findViewById(R.id.webView);
- webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
- webView.addJavascriptInterface(new JavaScriptInterface(), "NotificationCounter");
-
- if (savedInstanceState != null) {
- webView.restoreState(savedInstanceState);
- }
-
- wSettings = webView.getSettings();
- wSettings.setJavaScriptEnabled(true);
- wSettings.setUseWideViewPort(true);
- wSettings.setLoadWithOverviewMode(true);
- wSettings.setDomStorageEnabled(true);
- wSettings.setMinimumFontSize(pm.getMinimumFontSize());
- wSettings.setLoadsImagesAutomatically(pm.getLoadImages());
-
- if (android.os.Build.VERSION.SDK_INT >= 21)
- wSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
-
- /*
- * WebViewClient
- */
- webView.setWebViewClient(new WebViewClient() {
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- if (!url.contains(podDomain)) {
- Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- startActivity(i);
- return true;
- }
- return false;
- }
-
- public void onPageFinished(WebView view, String url) {
- swipeView.setRefreshing(false);
- }
- });
-
- swipeView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
- @Override
- public void onRefresh() {
- if (Helpers.isOnline(MainActivity.this)) {
- webView.reload();
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- swipeView.setRefreshing(false);
- }
- }
- });
-
- /*
- * WebChromeClient
- */
- webView.setWebChromeClient(new WebChromeClient() {
-
- public void onProgressChanged(WebView wv, int progress) {
- progressBar.setProgress(progress);
-
- if (progress > 0 && progress <= 60) {
- Helpers.getNotificationCount(wv);
- }
-
- if (progress > 60) {
- Helpers.hideTopBar(wv);
- fab.setVisibility(View.VISIBLE);
- }
-
- if (progress == 100) {
- fab.collapse();
- progressBar.setVisibility(View.GONE);
- } else {
- progressBar.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) {
- if (mFilePathCallback != null) mFilePathCallback.onReceiveValue(null);
-
- mFilePathCallback = filePathCallback;
-
- Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
- if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
- // Create the File where the photo should go
- File photoFile = null;
- try {
- photoFile = createImageFile();
- takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
- } catch (IOException ex) {
- // Error occurred while creating the File
- Snackbar.make(swipeView, R.string.image, Snackbar.LENGTH_LONG).show();
- return false;
- }
-
- // Continue only if the File was successfully created
- if (photoFile != null) {
- mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
- takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
- Uri.fromFile(photoFile));
- } else {
- takePictureIntent = null;
- }
- }
-
- Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
- contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
- contentSelectionIntent.setType("image/*");
-
- Intent[] intentArray;
- if (takePictureIntent != null) {
- intentArray = new Intent[]{takePictureIntent};
- } else {
- intentArray = new Intent[0];
- }
-
- Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
- chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
- chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
- chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
-
- startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);
-
- return true;
- }
-
- public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
- return super.onJsAlert(view, url, message, result);
- }
- });
-
-
- if (savedInstanceState == null) {
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadData("", "text/html", null);
- webView.loadUrl("https://"+podDomain);
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
- }
-
- }
-
- /*
- * Fab button events
- */
-
- public void fab1_click (View v){
- fab.collapse();
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl("https://" + podDomain + "/status_messages/new");
- setTitle(R.string.fab1_title);
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
- }
-
- public void fab2_click(View v){
- fab.collapse();
- if (Helpers.isOnline(MainActivity.this)) {
- final AlertDialog.Builder alert = new AlertDialog.Builder(this);
- final EditText input = new EditText(this);
- alert.setView(input);
- alert.setTitle(R.string.search_alert_title);
- alert.setPositiveButton(R.string.search_alert_people, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- String inputTag = input.getText().toString().trim();
- String cleanTag = inputTag.replaceAll("\\*", "");
- // this validate the input data for tagfind
- if (cleanTag == null || cleanTag.equals("")) {
- dialog.cancel(); // if user don�t have added a tag
- Snackbar.make(swipeView, R.string.search_alert_bypeople_validate_needsomedata, Snackbar.LENGTH_LONG).show();
- } else { // User have added a search tag
- webView.loadUrl("https://" + podDomain + "/people.mobile?q=" + cleanTag);
- setTitle(R.string.fab2_title_person);
- }
- }
- }).setNegativeButton(R.string.search_alert_tag,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- String inputTag = input.getText().toString().trim();
- String cleanTag = inputTag.replaceAll("\\#", "");
- // this validate the input data for tagfind
- if (cleanTag == null || cleanTag.equals("")) {
- dialog.cancel(); // if user hasn't added a tag
- Snackbar.make(swipeView, R.string.search_alert_bytags_validate_needsomedata, Snackbar.LENGTH_LONG).show();
- } else { // User have added a search tag
- webView.loadUrl("https://" + podDomain + "/tags/" + cleanTag);
- setTitle(R.string.fab2_title_tag);
- }
- }
- });
- alert.show();
- }
- else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
- }
-
- public void fab3_click(View v){
- fab.collapse();
- webView.scrollTo(0,0);
- }
-
- private File createImageFile() throws IOException {
- // Create an image file name
- String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
- String imageFileName = "JPEG_" + timeStamp + "_";
- File storageDir = Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_PICTURES);
- return File.createTempFile(
- imageFileName, /* prefix */
- ".jpg", /* suffix */
- storageDir /* directory */
- );
- }
-
-
- @Override
- public void onActivityResult (int requestCode, int resultCode, Intent data) {
- if(requestCode != INPUT_FILE_REQUEST_CODE || mFilePathCallback == null) {
- super.onActivityResult(requestCode, resultCode, data);
- return;
- }
- Uri[] results = null;
- if(resultCode == Activity.RESULT_OK) {
- if(data == null) {
- if(mCameraPhotoPath != null) {
- results = new Uri[]{Uri.parse(mCameraPhotoPath)};
- }
- } else {
- String dataString = data.getDataString();
- if (dataString != null) {
- results = new Uri[]{Uri.parse(dataString)};
- }
- }
- }
-
- mFilePathCallback.onReceiveValue(results);
- mFilePathCallback = null;
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- webView.saveState(outState);
- }
-
- @Override
- protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
- super.onRestoreInstanceState(savedInstanceState);
- webView.restoreState(savedInstanceState);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- registerReceiver(brLoadUrl, new IntentFilter(URL_MESSAGE));
- }
-
- @Override
- public void onBackPressed() {
- fab.collapse();
- if (webView.canGoBack()) {
- webView.goBack();
- setTitle(R.string.app_name);
- Snackbar snackbar = Snackbar
- .make(swipeView, R.string.confirm_exit, Snackbar.LENGTH_LONG)
- .setAction(R.string.yes, new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- moveTaskToBack(true);
- }
- });
- snackbar.show();
- } else {
- Snackbar snackbar = Snackbar
- .make(swipeView, R.string.confirm_exit, Snackbar.LENGTH_LONG)
- .setAction(R.string.yes, new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- moveTaskToBack(true);
- }
- });
- snackbar.show();
- }
- }
-
- private BroadcastReceiver brLoadUrl = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String url = intent.getStringExtra("url");
- txtTitle.setText(R.string.app_name);
- webView.loadUrl(url);
- }
- };
-
- @Override
- protected void onPause() {
- unregisterReceiver(brLoadUrl);
- super.onPause();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_main, menu);
- return true;
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- this.menu = menu;
- MenuItem itemNotification = menu.findItem(R.id.notifications);
- if (itemNotification != null) {
- if (notificationCount > 0) {
- itemNotification.setIcon(R.drawable.ic_bell_ring_white_24dp);
- } else {
- itemNotification.setIcon(R.drawable.ic_bell_outline_white_24dp);
- }
-
- MenuItem itemConversation = menu.findItem(R.id.conversations);
- if (conversationCount > 0) {
- itemConversation.setIcon(R.drawable.ic_message_text_white_24dp);
- } else {
- itemConversation.setIcon(R.drawable.ic_message_text_outline_white_24dp);
- }
- }
- return super.onPrepareOptionsMenu(menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- int id = item.getItemId();
-
- if (id == R.id.notifications) {
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl("https://" + podDomain + "/notifications");
- setTitle(R.string.jb_notifications);
- return true;
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- return false;
- }
- }
-
- if (id == R.id.conversations) {
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl("https://" + podDomain + "/conversations");
- setTitle(R.string.jb_conversations);
- return true;
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- return false;
- }
- }
-
- if (id == R.id.exit) {
- Snackbar snackbar = Snackbar
- .make(swipeView, R.string.confirm_exit, Snackbar.LENGTH_LONG)
- .setAction(R.string.yes, new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- moveTaskToBack(true);
- }
- });
- snackbar.show();
- }
-
- if (id == R.id.help_license) {
- final CharSequence[] options = { getString(R.string.help_license), getString(R.string.help_help), getString(R.string.help_donate) };
- new AlertDialog.Builder(MainActivity.this)
- .setItems(options, new DialogInterface.OnClickListener() {
-
- @Override
-
- public void onClick(DialogInterface dialog, int item) {
-
- if (options[item].equals(getString(R.string.help_license)))
-
- {
- new AlertDialog.Builder(MainActivity.this)
- .setMessage(getString(R.string.about_text))
- .setPositiveButton(getString(R.string.about_yes),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/scoute-dich/Diaspora-Native-WebApp"));
- startActivity(i);
- dialog.cancel();
- }
- })
- .setNegativeButton(getString(R.string.about_no),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- }).show();
- }
-
- if (options[item].equals(getString(R.string.help_help)))
-
- {
- new AlertDialog.Builder(MainActivity.this)
- .setMessage(getString(R.string.markdown_text))
- .setNegativeButton(getString(R.string.about_no),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- }).show();
- }
-
- if (options[item].equals(getString(R.string.help_donate)))
-
- {
- new AlertDialog.Builder(MainActivity.this)
- .setMessage(getString(R.string.donate_text))
- .setPositiveButton(getString(R.string.donate_1),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("http://martinv.tip.me/"));
- startActivity(i);
- dialog.cancel();
- }
- })
- .setNegativeButton(getString(R.string.about_no),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- }).show();
- }
-
- }
-
- }).show();
- }
-
- if (id == R.id.view) {
- final CharSequence[] options = { getString(R.string.settings_font), getString(R.string.settings_view),getString(R.string.settings_image) };
- new AlertDialog.Builder(MainActivity.this)
- .setItems(options, new DialogInterface.OnClickListener() {
-
- @Override
-
- public void onClick(DialogInterface dialog, int item) {
-
- if (options[item].equals(getString(R.string.settings_font)))
-
- {
- if (Helpers.isOnline(MainActivity.this)) {
- alertFormElements();
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
- }
-
- if (options[item].equals(getString(R.string.settings_view)))
-
- {
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl("https://" + podDomain + "/mobile/toggle");
- } else { // No Internet connection
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
- }
-
- if (options[item].equals(getString(R.string.settings_image)))
-
- {
- if (Helpers.isOnline(MainActivity.this)) {
- wSettings.setLoadsImagesAutomatically(!pm.getLoadImages());
- pm.setLoadImages(!pm.getLoadImages());
- webView.loadUrl(webView.getUrl());
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
- }
-
- }
-
- }).show();
- }
-
- if (id == R.id.share) {
- final CharSequence[] options = { getString(R.string.share_link), getString(R.string.share_screenshot),getString(R.string.take_screenshot) };
- new AlertDialog.Builder(MainActivity.this)
- .setItems(options, new DialogInterface.OnClickListener() {
-
- @Override
-
- public void onClick(DialogInterface dialog, int item) {
-
- if (options[item].equals(getString(R.string.share_link)))
-
- {
- Intent sharingIntent = new Intent(Intent.ACTION_SEND);
- sharingIntent.setType("image/png");
- sharingIntent.putExtra(Intent.EXTRA_SUBJECT, webView.getTitle());
- sharingIntent.putExtra(Intent.EXTRA_TEXT, webView.getUrl());
- startActivity(Intent.createChooser(sharingIntent, "Share using"));
- }
-
- if (options[item].equals(getString(R.string.share_screenshot)))
-
- {
- if (android.os.Build.VERSION.SDK_INT >= 23) {
- int hasWRITE_EXTERNAL_STORAGE = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
- if (hasWRITE_EXTERNAL_STORAGE != PackageManager.PERMISSION_GRANTED) {
- if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
- new AlertDialog.Builder(MainActivity.this)
- .setMessage(R.string.permissions)
- .setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (android.os.Build.VERSION.SDK_INT >= 23)
- requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
- REQUEST_CODE_ASK_PERMISSIONS);
- }
- })
- .setNegativeButton(getString(R.string.no), null)
- .show();
- return;
- }
- requestPermissions(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
- REQUEST_CODE_ASK_PERMISSIONS);
- return;
- }
- }
-
- Snackbar.make(swipeView, R.string.toast_screenshot, Snackbar.LENGTH_LONG).show();
- File directory = new File(Environment.getExternalStorageDirectory() + "/Pictures/Diaspora/");
- if (!directory.exists()) {
- directory.mkdirs();
- }
-
- Date date = new Date();
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH-mm");
-
- Picture picture = webView.capturePicture();
- Bitmap b = Bitmap.createBitmap(picture.getWidth(), picture.getHeight(), Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(b);
-
- File screen = new File(Environment.getExternalStorageDirectory() + "/Pictures/Diaspora/"
- + dateFormat.format(date) + ".jpg");
- if (screen.exists())
- screen.delete();
-
- picture.draw(c);
-
- FileOutputStream fos = null;
- try {
-
- fos = new FileOutputStream(screen);
- if (fos != null) {
- b.compress(Bitmap.CompressFormat.JPEG, 90, fos);
-
- fos.close();
- }
- } catch (Exception e) {
- e.getMessage();
-
- }
-
- Intent sharingIntent = new Intent(Intent.ACTION_SEND);
- sharingIntent.setType("image/png");
- sharingIntent.putExtra(Intent.EXTRA_SUBJECT, webView.getTitle());
- sharingIntent.putExtra(Intent.EXTRA_TEXT, webView.getUrl());
- Uri bmpUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory() + "/Pictures/Diaspora/"
- + dateFormat.format(date) + ".jpg"));
- sharingIntent.putExtra(Intent.EXTRA_STREAM, bmpUri);
- startActivity(Intent.createChooser(sharingIntent, "Share using"));
-
- File file = new File(Environment.getExternalStorageDirectory() + "/Pictures/Diaspora/"
- + dateFormat.format(date) + ".jpg");
- Uri uri = Uri.fromFile(file);
- Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
- sendBroadcast(intent);
- }
-
- if (options[item].equals(getString(R.string.take_screenshot)))
-
- {
- if (android.os.Build.VERSION.SDK_INT >= 23) {
- int hasWRITE_EXTERNAL_STORAGE = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
- if (hasWRITE_EXTERNAL_STORAGE != PackageManager.PERMISSION_GRANTED) {
- if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
- new AlertDialog.Builder(MainActivity.this)
- .setMessage(R.string.permissions)
- .setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (android.os.Build.VERSION.SDK_INT >= 23)
- requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
- REQUEST_CODE_ASK_PERMISSIONS);
- }
- })
- .setNegativeButton(getString(R.string.no), null)
- .show();
- return;
- }
- requestPermissions(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
- REQUEST_CODE_ASK_PERMISSIONS);
- return;
- }
- }
-
- Snackbar.make(swipeView, R.string.toast_screenshot, Snackbar.LENGTH_LONG).show();
-
- File directory = new File(Environment.getExternalStorageDirectory() + "/Pictures/Diaspora/");
- if (!directory.exists()) {
- directory.mkdirs();
- }
-
- Date date = new Date();
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH-mm");
-
- Picture picture = webView.capturePicture();
- Bitmap b = Bitmap.createBitmap(picture.getWidth(), picture.getHeight(), Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(b);
-
- File screen = new File(Environment.getExternalStorageDirectory() + "/Pictures/Diaspora/"
- + dateFormat.format(date) + ".jpg");
- if (screen.exists())
- screen.delete();
-
- picture.draw(c);
-
- FileOutputStream fos = null;
- try {
-
- fos = new FileOutputStream(screen);
- if (fos != null) {
- b.compress(Bitmap.CompressFormat.JPEG, 90, fos);
-
- fos.close();
- }
- } catch (Exception e) {
- e.getMessage();
-
- }
-
- File file = new File(Environment.getExternalStorageDirectory() + "/Pictures/Diaspora/"
- + dateFormat.format(date) + ".jpg");
- Uri uri = Uri.fromFile(file);
- Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
- sendBroadcast(intent);
- }
-
- }
-
- }).show();
- }
- return super.onOptionsItemSelected(item);
- }
-
-
-
- public void alertFormElements() {
-
- /*
- * Inflate the XML view. activity_main is in
- * res/layout/form_elements.xml
- */
- LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- final View formElementsView = inflater.inflate(R.layout.font_size_chooser,
- null, false);
-
- final RadioGroup rgFontSize = (RadioGroup) formElementsView
- .findViewById(R.id.genderRadioGroup);
-
- // the alert dialog
- new AlertDialog.Builder(MainActivity.this).setView(formElementsView)
- .setTitle("Set Font Size")
- .setNegativeButton("OK", new DialogInterface.OnClickListener() {
- @TargetApi(11)
- public void onClick(DialogInterface dialog, int id) {
-
- int selectedId = rgFontSize
- .getCheckedRadioButtonId();
-
- // find the radiobutton by returned id
- RadioButton selectedRadioButton = (RadioButton) formElementsView
- .findViewById(selectedId);
-
- if (selectedRadioButton.getId() == R.id.radNormal) {
- pm.setMinimumFontSize(8);
- } else if (selectedRadioButton.getId() == R.id.radLarge) {
- pm.setMinimumFontSize(16);
- } else if (selectedRadioButton.getId() == R.id.radLarger) {
- pm.setMinimumFontSize(20);
- }
-
- wSettings.setMinimumFontSize(pm.getMinimumFontSize());
-
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl(webView.getUrl());
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
- dialog.cancel();
- }
- }).show();
- }
-
- public class JavaScriptInterface {
- @JavascriptInterface
- public void setNotificationCount(final String webMessage){
- myHandler.post(new Runnable() {
- @Override
- public void run() {
- notificationCount = Integer.valueOf(webMessage);
-
- MenuItem item = menu.findItem(R.id.notifications);
-
- if (item != null) {
- if (notificationCount > 0) {
- item.setIcon(R.drawable.ic_bell_ring_white_24dp);
- } else {
- item.setIcon(R.drawable.ic_bell_outline_white_24dp);
- }
- }
-
-
- }
- });
- }
-
- @JavascriptInterface
- public void setConversationCount(final String webMessage){
- myHandler.post(new Runnable() {
- @Override
- public void run() {
- conversationCount = Integer.valueOf(webMessage);
-
- MenuItem item = menu.findItem(R.id.conversations);
-
- if (item != null) {
- if (conversationCount > 0) {
- item.setIcon(R.drawable.ic_message_text_white_24dp);
- } else {
- item.setIcon(R.drawable.ic_message_text_outline_white_24dp);
- }
- }
-
- }
- });
- }
-
- }
-
- @SuppressWarnings("StatementWithEmptyBody")
- @Override
- public boolean onNavigationItemSelected(MenuItem item) {
- // Handle navigation view item clicks here.
- int id = item.getItemId();
-
- if (id == R.id.jb_stream) {
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl("https://" + podDomain + "/stream");
- setTitle(R.string.jb_stream);
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
-
- } else if (id == R.id.jb_followed_tags) {
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl("https://" + podDomain + "/followed_tags");
- setTitle(R.string.jb_followed_tags);
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
-
- } else if (id == R.id.jb_aspects) {
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl("https://" + podDomain + "/aspects");
- setTitle(R.string.jb_aspects);
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
-
- } else if (id == R.id.jb_activities) {
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl("https://" + podDomain + "/activity");
- setTitle(R.string.jb_activities);
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
-
- } else if (id == R.id.jb_liked) {
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl("https://" + podDomain + "/liked");
- setTitle(R.string.jb_liked);
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
-
- } else if (id == R.id.jb_commented) {
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl("https://" + podDomain + "/commented");
- setTitle(R.string.jb_commented);
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
-
- } else if (id == R.id.jb_mentions) {
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl("https://" + podDomain + "/mentions");
- setTitle(R.string.jb_mentions);
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
-
- } else if (id == R.id.jb_public) {
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl("https://" + podDomain + "/public");
- setTitle(R.string.jb_public);
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
-
- } else if (id == R.id.jb_settings_view) {
- final CharSequence[] options = { getString(R.string.settings_font), getString(R.string.settings_view),getString(R.string.settings_image) };
- new AlertDialog.Builder(MainActivity.this)
- .setItems(options, new DialogInterface.OnClickListener() {
-
- @Override
-
- public void onClick(DialogInterface dialog, int item) {
-
- if (options[item].equals(getString(R.string.settings_font)))
-
- {
- if (Helpers.isOnline(MainActivity.this)) {
- alertFormElements();
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
- }
-
- if (options[item].equals(getString(R.string.settings_view)))
-
- {
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl("https://" + podDomain + "/mobile/toggle");
- } else { // No Internet connection
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
- }
-
- if (options[item].equals(getString(R.string.settings_image)))
-
- {
- if (Helpers.isOnline(MainActivity.this)) {
- wSettings.setLoadsImagesAutomatically(!pm.getLoadImages());
- pm.setLoadImages(!pm.getLoadImages());
- webView.loadUrl(webView.getUrl());
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
- }
-
- }
-
- }).show();
-
- } else if (id == R.id.jb_settings_diaspora) {
- final CharSequence[] options2 = { getString(R.string.jb_settings), getString(R.string.jb_manage_tags),
- getString(R.string.jb_contacts), getString(R.string.jb_pod) };
- new AlertDialog.Builder(MainActivity.this)
- .setItems(options2, new DialogInterface.OnClickListener() {
-
- @Override
-
- public void onClick(DialogInterface dialog, int item) {
-
- if (options2[item].equals(getString(R.string.jb_settings)))
-
- {
- setTitle(R.string.jb_settings);
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl("https://" + podDomain + "/user/edit");
- } else { // No Internet connection
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
- }
-
- if (options2[item].equals(getString(R.string.jb_manage_tags)))
-
- {
- setTitle(R.string.jb_manage_tags);
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl("https://" + podDomain + "/tag_followings/manage");
- } else { // No Internet connection
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
- }
-
- if (options2[item].equals(getString(R.string.jb_contacts)))
-
- {
- setTitle(R.string.jb_contacts);
- if (Helpers.isOnline(MainActivity.this)) {
- webView.loadUrl("https://" + podDomain + "/contacts");
- } else { // No Internet connection
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
- }
-
- if (options2[item].equals(getString(R.string.jb_pod)))
-
- {
- if (Helpers.isOnline(MainActivity.this)) {
- new AlertDialog.Builder(MainActivity.this)
- .setTitle(getString(R.string.confirmation))
- .setMessage(getString(R.string.change_pod_warning))
- .setPositiveButton(getString(R.string.yes),
- new DialogInterface.OnClickListener() {
- @TargetApi(11)
- public void onClick(DialogInterface dialog, int id) {
- webView.clearCache(true);
- dialog.cancel();
- Intent i = new Intent(MainActivity.this, PodsActivity.class);
- startActivity(i);
- finish();
- }
- })
- .setNegativeButton(getString(R.string.no), new DialogInterface.OnClickListener() {
- @TargetApi(11)
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- }).show();
- } else {
- Snackbar.make(swipeView, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
- }
-
- }
-
- }).show();
-
- } else if (id == R.id.jb_license_help) {
- final CharSequence[] options = { getString(R.string.help_license), getString(R.string.help_help), getString(R.string.help_donate) };
- new AlertDialog.Builder(MainActivity.this)
- .setItems(options, new DialogInterface.OnClickListener() {
-
- @Override
-
- public void onClick(DialogInterface dialog, int item) {
-
- if (options[item].equals(getString(R.string.help_license)))
-
- {
- new AlertDialog.Builder(MainActivity.this)
- .setTitle(R.string.about_title)
- .setMessage(getString(R.string.about_text))
- .setPositiveButton(getString(R.string.about_yes),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/scoute-dich/Diaspora-Native-WebApp"));
- startActivity(i);
- dialog.cancel();
- }
- })
- .setNegativeButton(getString(R.string.about_no),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- }).show();
- }
-
- if (options[item].equals(getString(R.string.help_help)))
-
- {
- new AlertDialog.Builder(MainActivity.this)
- .setMessage(getString(R.string.markdown_text))
- .setNegativeButton(getString(R.string.about_no),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- }).show();
- }
-
- if (options[item].equals(getString(R.string.help_donate)))
-
- {
- new AlertDialog.Builder(MainActivity.this)
- .setMessage(getString(R.string.donate_text))
- .setPositiveButton(getString(R.string.donate_1),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("http://martinv.tip.me/"));
- startActivity(i);
- dialog.cancel();
- }
- })
- .setNegativeButton(getString(R.string.about_no),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- }).show();
- }
-
- }
-
- }).show();
- }
-
- DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
- drawer.closeDrawer(GravityCompat.START);
- return true;
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/de/baumann/diaspora/PodsActivity.java b/app/src/main/java/de/baumann/diaspora/PodsActivity.java
deleted file mode 100644
index 15340cffc..000000000
--- a/app/src/main/java/de/baumann/diaspora/PodsActivity.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- This file is part of the Diaspora Native WebApp.
-
- Diaspora Native WebApp 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.
-
- Diaspora Native WebApp 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 the Diaspora Native WebApp.
-
- If not, see .
- */
-
-package de.baumann.diaspora;
-
-import android.annotation.TargetApi;
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.design.widget.Snackbar;
-import android.support.v7.app.ActionBarActivity;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.webkit.CookieManager;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
-import de.baumann.diaspora.services.GetPodsService;
-import de.baumann.diaspora.utils.Helpers;
-
-
-public class PodsActivity extends ActionBarActivity {
-
- BroadcastReceiver podListReceiver;
- EditText filter;
- ListView lv;
- ImageView imgSelectPod;
- ProgressDialog progressDialog;
- private static final String TAG = "Diaspora Pods";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_pods);
-
- filter = (EditText) findViewById(R.id.edtFilter);
- lv = (ListView) findViewById(R.id.lstPods);
- lv.setTextFilterEnabled(true);
-
- imgSelectPod = (ImageView) findViewById(R.id.imgSelectPod);
- imgSelectPod.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (filter.getText().length() > 4 && filter.getText().toString().contains("."))
- askConfirmation(filter.getText().toString());
- else
- Snackbar.make(lv, R.string.valid_pod, Snackbar.LENGTH_LONG).show();
- }
- });
-
- podListReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.hasExtra("pods")) {
- Bundle extras = intent.getExtras();
- String[] pods = extras.getStringArray("pods");
-
- if (progressDialog != null)
- progressDialog.dismiss();
-
- if (pods != null && pods.length>0)
- updateListview(pods);
- else {
- Snackbar.make(lv, R.string.podlist_error, Snackbar.LENGTH_LONG).show();
- }
- } else {
- // List of pods empty
- }
- }
- };
-
- registerReceiver(podListReceiver, new IntentFilter(GetPodsService.MESSAGE));
-
- progressDialog = new ProgressDialog(PodsActivity.this);
- progressDialog.setCancelable(false);
- progressDialog.setIndeterminate(true);
- progressDialog.setMessage(getString(R.string.loading_podlist));
-
- if (Helpers.isOnline(PodsActivity.this)) {
- progressDialog.show();
- } else {
- Snackbar.make(lv, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
-
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- Intent i= new Intent(PodsActivity.this, GetPodsService.class);
- startService(i);
- }
-
-
- private void updateListview(String[] source) {
- final ArrayList podList = new ArrayList<>();
-
- for (int i = 0 ; i < source.length ; i++) {
- podList.add(source[i].toLowerCase());
- }
- Collections.sort(podList);
-
- final ArrayAdapter adapter = new ArrayAdapter(
- PodsActivity.this,
- android.R.layout.simple_list_item_1,
- podList);
- lv.setAdapter(adapter);
- lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView> parent, View view, int position, long id) {
- askConfirmation(((TextView) view).getText().toString());
- }
- });
-
- adapter.getFilter().filter(filter.getText());
- filter.addTextChangedListener(new TextWatcher() {
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- (adapter).getFilter().filter(s.toString());
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- }
- });
-
- }
-
- public void askConfirmation(final String podDomain) {
- if (Helpers.isOnline(PodsActivity.this)) {
- new AlertDialog.Builder(PodsActivity.this)
- .setTitle(getString(R.string.confirmation))
- .setMessage(getString(R.string.confirm_pod)+podDomain+"?")
- .setPositiveButton("YES",
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
-
- SharedPreferences sp = getSharedPreferences("PodSettings", MODE_PRIVATE);
- SharedPreferences.Editor editor = sp.edit();
- editor.putString("podDomain", podDomain);
- editor.apply();
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- try {
- CookieManager.getInstance().removeAllCookies(null);
- CookieManager.getInstance().removeSessionCookies(null);
- } catch (Exception e) {
- e.printStackTrace();
- }
- } else {
- try {
- CookieManager.getInstance().removeAllCookie();
- CookieManager.getInstance().removeSessionCookie();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- Intent i = new Intent(PodsActivity.this, MainActivity.class);
- dialog.cancel();
- startActivity(i);
- finish();
- }
- })
- .setNegativeButton("NO", new DialogInterface.OnClickListener() {
- @TargetApi(11)
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- }).show();
-
- } else {
- Snackbar.make(lv, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- }
- }
-
-
- @Override
- public void onBackPressed() {
- Snackbar snackbar = Snackbar
- .make(lv, R.string.confirm_exit, Snackbar.LENGTH_LONG)
- .setAction(R.string.yes, new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- moveTaskToBack(true);
- }
- });
- snackbar.show();
- }
-
- @Override
- protected void onDestroy() {
- unregisterReceiver(podListReceiver);
- super.onDestroy();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_pods, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- int id = item.getItemId();
-
- if (id == R.id.reload) {
- if (Helpers.isOnline(PodsActivity.this)) {
- progressDialog.show();
- Intent i= new Intent(PodsActivity.this, GetPodsService.class);
- startService(i);
- return true;
- } else {
- Snackbar.make(lv, R.string.no_internet, Snackbar.LENGTH_LONG).show();
- return false;
- }
- }
-
- return super.onOptionsItemSelected(item);
- }
-
-
-}
-
-
diff --git a/app/src/main/java/de/baumann/diaspora/ShareActivity.java b/app/src/main/java/de/baumann/diaspora/ShareActivity.java
deleted file mode 100644
index 553f2502d..000000000
--- a/app/src/main/java/de/baumann/diaspora/ShareActivity.java
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- This file is part of the Diaspora Native WebApp.
-
- Diaspora Native WebApp 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.
-
- Diaspora Native WebApp 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 the Diaspora Native WebApp.
-
- If not, see .
- */
-
-package de.baumann.diaspora;
-
-import android.annotation.SuppressLint;
-import android.app.AlertDialog;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Environment;
-import android.provider.MediaStore;
-import android.support.design.widget.Snackbar;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.webkit.JsResult;
-import android.webkit.ValueCallback;
-import android.webkit.WebChromeClient;
-import android.webkit.WebSettings;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import java.io.File;
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import de.baumann.diaspora.utils.Helpers;
-
-
-public class ShareActivity extends MainActivity {
-
- private WebView webView;
- private static final String TAG = "Diaspora Share";
- private String podDomain;
- private ValueCallback mFilePathCallback;
- private String mCameraPhotoPath;
- private com.getbase.floatingactionbutton.FloatingActionsMenu fab;
- private TextView txtTitle;
- private ProgressBar progressBar;
-
- @SuppressLint("SetJavaScriptEnabled")
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- progressBar = (ProgressBar)findViewById(R.id.progressBar);
-
- txtTitle.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (Helpers.isOnline(ShareActivity.this)) {
- txtTitle.setText(R.string.jb_stream);
- Intent i = new Intent(ShareActivity.this, MainActivity.class);
- i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(i);
- finish();
- } else {
- Snackbar.make(v, R.string.no_internet, Snackbar.LENGTH_SHORT).show();
- }
- }
- });
-
-
- SharedPreferences config = getSharedPreferences("PodSettings", MODE_PRIVATE);
- podDomain = config.getString("podDomain", null);
-
- fab = (com.getbase.floatingactionbutton.FloatingActionsMenu) findViewById(R.id.multiple_actions);
- fab.setVisibility(View.GONE);
-
- webView = (WebView)findViewById(R.id.webView);
- webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
-
- WebSettings wSettings = webView.getSettings();
- wSettings.setJavaScriptEnabled(true);
- wSettings.setBuiltInZoomControls(true);
-
- if (Build.VERSION.SDK_INT >= 21)
- wSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
-
- /*
- * WebViewClient
- */
- webView.setWebViewClient(new WebViewClient() {
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- Log.d(TAG, url);
- if (!url.contains(podDomain)) {
- Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- startActivity(i);
- return true;
- }
- return false;
-
- }
-
- public void onPageFinished(WebView view, String url) {
- Log.i(TAG, "Finished loading URL: " + url);
- }
-
- public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
- Log.e(TAG, "Error: " + description);
-
- new AlertDialog.Builder(ShareActivity.this)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(description)
- .setPositiveButton("CLOSE", null)
- .show();
- }
- });
-
-
- /*
- * WebChromeClient
- */
- webView.setWebChromeClient(new WebChromeClient() {
-
- public void onProgressChanged(WebView wv, int progress) {
- progressBar.setProgress(progress);
-
- if (progress > 0 && progress <= 60) {
- Helpers.getNotificationCount(wv);
- }
-
- if (progress > 60) {
- Helpers.hideTopBar(wv);
- }
-
- if (progress == 100) {
- progressBar.setVisibility(View.GONE);
- } else {
- progressBar.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) {
- if (mFilePathCallback != null) mFilePathCallback.onReceiveValue(null);
-
- mFilePathCallback = filePathCallback;
-
- Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
- if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
- // Create the File where the photo should go
- File photoFile = null;
- try {
- photoFile = createImageFile();
- takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
- } catch (IOException ex) {
- // Error occurred while creating the File
- Snackbar.make(getWindow().findViewById(R.id.drawer_layout), "Unable to get image", Snackbar.LENGTH_SHORT).show();
- }
-
- // Continue only if the File was successfully created
- if (photoFile != null) {
- mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
- takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
- Uri.fromFile(photoFile));
- } else {
- takePictureIntent = null;
- }
- }
-
- Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
- contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
- contentSelectionIntent.setType("image/*");
-
- Intent[] intentArray;
- if (takePictureIntent != null) {
- intentArray = new Intent[]{takePictureIntent};
- } else {
- intentArray = new Intent[0];
- }
-
- Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
- chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
- chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
- chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
-
- startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);
-
- return true;
- }
-
- public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
- return super.onJsAlert(view, url, message, result);
- }
- });
-
-
-
-
- Intent intent = getIntent();
- final Bundle extras = intent.getExtras();
- String action = intent.getAction();
-
- if (Intent.ACTION_SEND.equals(action)) {
- webView.setWebViewClient(new WebViewClient() {
-
- public void onPageFinished(WebView view, String url) {
-
- if (extras.containsKey(Intent.EXTRA_TEXT) && extras.containsKey(Intent.EXTRA_SUBJECT)) {
- final String extraText = (String) extras.get(Intent.EXTRA_TEXT);
- final String extraSubject = (String) extras.get(Intent.EXTRA_SUBJECT);
-
- webView.setWebViewClient(new WebViewClient() {
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
-
- finish();
-
- Snackbar.make(webView, R.string.please_reload, Snackbar.LENGTH_LONG).show();
-
- Intent i = new Intent(ShareActivity.this, MainActivity.class);
- i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(i);
-
- return false;
- }
- });
-
- webView.loadUrl("javascript:(function() { " +
- "document.getElementsByTagName('textarea')[0].style.height='110px'; " +
- "document.getElementsByTagName('textarea')[0].innerHTML = '[" + extraSubject + "](" + extraText + ") #ViaDiasporaNativeWebApp'; " +
- " if(document.getElementById(\"main_nav\")) {" +
- " document.getElementById(\"main_nav\").parentNode.removeChild(" +
- " document.getElementById(\"main_nav\"));" +
- " } else if (document.getElementById(\"main-nav\")) {" +
- " document.getElementById(\"main-nav\").parentNode.removeChild(" +
- " document.getElementById(\"main-nav\"));" +
- " }" +
- "})();");
-
- }
- }
- });
- }
-
- if (savedInstanceState == null) {
- if (Helpers.isOnline(ShareActivity.this)) {
- webView.loadUrl("https://"+podDomain+"/status_messages/new");
- } else {
- Snackbar.make(getWindow().findViewById(R.id.drawer_layout), R.string.no_internet, Snackbar.LENGTH_SHORT).show();
- }
- }
-
- }
-
- private File createImageFile() throws IOException {
- String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
- String imageFileName = "JPEG_" + timeStamp + "_";
- File storageDir = Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_PICTURES);
- return File.createTempFile(
- imageFileName, /* prefix */
- ".jpg", /* suffix */
- storageDir /* directory */
- );
- }
-
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_compose, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- int id = item.getItemId();
-
- if (id == R.id.reload) {
- if (Helpers.isOnline(ShareActivity.this)) {
- webView.reload();
- return true;
- } else {
- Snackbar.make(getWindow().findViewById(R.id.drawer_layout), R.string.no_internet, Snackbar.LENGTH_SHORT).show();
- return false;
- }
- }
-
- return super.onOptionsItemSelected(item);
- }
-
-}
diff --git a/app/src/main/java/de/baumann/diaspora/SplashActivity.java b/app/src/main/java/de/baumann/diaspora/SplashActivity.java
deleted file mode 100644
index 7bb03d096..000000000
--- a/app/src/main/java/de/baumann/diaspora/SplashActivity.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- This file is part of the Diaspora Native WebApp.
-
- Diaspora Native WebApp 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.
-
- Diaspora Native WebApp 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 the Diaspora Native WebApp.
-
- If not, see .
- */
-
-package de.baumann.diaspora;
-
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.TypedArray;
-import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.view.WindowManager;
-import android.widget.ImageView;
-
-import java.util.Timer;
-import java.util.TimerTask;
-
-
-public class SplashActivity extends AppCompatActivity {
-
- ImageView imgSplash;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
- WindowManager.LayoutParams.FLAG_FULLSCREEN);
- setContentView(R.layout.activity_splash);
-
- imgSplash = (ImageView) findViewById(R.id.imgSplash);
-
- TypedArray images = getResources().obtainTypedArray(R.array.splash_images);
- int choice = (int) (Math.random() * images.length());
- imgSplash.setImageResource(images.getResourceId(choice, R.drawable.splashscreen1));
- images.recycle();
-
- final SharedPreferences config = getSharedPreferences("PodSettings", MODE_PRIVATE);
-
- Timer timer = new Timer();
- timer.schedule(new TimerTask() {
- @Override
- public void run() {
- Intent i;
- if (config.getString("podDomain", null) != null) {
- i = new Intent(SplashActivity.this, MainActivity.class);
- } else {
- i = new Intent(SplashActivity.this, PodsActivity.class);
- }
- startActivity(i);
- finish();
- }
- }, 2000);
-
- }
-
-}
diff --git a/app/src/main/java/de/baumann/diaspora/services/GetPodsService.java b/app/src/main/java/de/baumann/diaspora/services/GetPodsService.java
deleted file mode 100644
index 6a0ab86c0..000000000
--- a/app/src/main/java/de/baumann/diaspora/services/GetPodsService.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- This file is part of the Diaspora Native WebApp.
-
- Diaspora Native WebApp 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.
-
- Diaspora Native WebApp 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 the Diaspora Native WebApp.
-
- If not, see .
- */
-
-package de.baumann.diaspora.services;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.IBinder;
-import android.util.Log;
-
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.StatusLine;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.json.JSONArray;
-import org.json.JSONObject;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.List;
-
-public class GetPodsService extends Service {
- public static final String MESSAGE = "de.baumann.diaspora.podsreceived";
-
- private static final String TAG = "Diaspora Pod Service";
-
- public GetPodsService() { }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- getPods();
- return super.onStartCommand(intent, flags, startId);
- }
-
- private void getPods() {
- /*
- * Most of the code in this AsyncTask is from the file getPodlistTask.java
- * from the app "Diaspora Webclient".
- * A few modifications and adaptations were made by me.
- * Source:
- * https://github.com/voidcode/Diaspora-Webclient/blob/master/src/com/voidcode/diasporawebclient/getPodlistTask.java
- * Thanks to Terkel Sørensen
- */
- AsyncTask getPodsAsync = new AsyncTask() {
- @Override
- protected String[] doInBackground(Void... params) {
-
- // TODO: Update deprecated code
-
- StringBuilder builder = new StringBuilder();
- HttpClient client = new DefaultHttpClient();
- List list = null;
- try {
- HttpGet httpGet = new HttpGet("http://podupti.me/api.php?key=4r45tg&format=json");
- HttpResponse response = client.execute(httpGet);
- StatusLine statusLine = response.getStatusLine();
- int statusCode = statusLine.getStatusCode();
- if (statusCode == 200) {
- HttpEntity entity = response.getEntity();
- InputStream content = entity.getContent();
- BufferedReader reader = new BufferedReader(
- new InputStreamReader(content));
- String line;
- while ((line = reader.readLine()) != null) {
- builder.append(line);
- }
- } else {
- //TODO Notify User about failure
- Log.e(TAG, "Failed to download list of pods");
- }
- } catch (IOException e) {
- //TODO handle json buggy feed
- e.printStackTrace();
- }
- //Parse the JSON Data
- try {
- JSONObject j = new JSONObject(builder.toString());
- JSONArray jr = j.getJSONArray("pods");
- Log.d(TAG, "Number of entries " + jr.length());
- list = new ArrayList();
- for (int i = 0; i < jr.length(); i++) {
- JSONObject jo = jr.getJSONObject(i);
- Log.d(TAG, jo.getString("domain"));
- String secure = jo.getString("secure");
- if (secure.equals("true"))
- list.add(jo.getString("domain"));
- }
-
- } catch (Exception e) {
- //TODO Handle Parsing errors here
- e.printStackTrace();
- }
- if (list != null)
- return list.toArray(new String[list.size()]);
- else
- return null;
- }
-
- @Override
- protected void onPostExecute(String[] strings) {
- Intent broadcastIntent = new Intent(MESSAGE);
- if (strings != null)
- broadcastIntent.putExtra("pods", strings);
- sendBroadcast(broadcastIntent);
- stopSelf();
- }
- };
- getPodsAsync.execute();
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- // TODO: Return the communication channel to the service.
- throw new UnsupportedOperationException("Not yet implemented");
- }
-
-}
diff --git a/app/src/main/java/de/baumann/diaspora/utils/Helpers.java b/app/src/main/java/de/baumann/diaspora/utils/Helpers.java
deleted file mode 100644
index 489cefc32..000000000
--- a/app/src/main/java/de/baumann/diaspora/utils/Helpers.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- This file is part of the Diaspora Native WebApp.
-
- Diaspora Native WebApp 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.
-
- Diaspora Native WebApp 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 the Diaspora Native WebApp.
-
- If not, see .
- */
-
-package de.baumann.diaspora.utils;
-
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.webkit.WebView;
-
-public class Helpers {
-
- public static boolean isOnline(Context context){
- ConnectivityManager cnm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo ni = cnm.getActiveNetworkInfo();
- return ni != null && ni.isConnectedOrConnecting();
- }
-
- public static void hideTopBar(WebView wv) {
- wv.loadUrl("javascript: ( function() {" +
- " if(document.getElementById('main_nav')) {" +
- " document.getElementById('main_nav').parentNode.removeChild(" +
- " document.getElementById('main_nav'));" +
- " } else if (document.getElementById('main-nav')) {" +
- " document.getElementById('main-nav').parentNode.removeChild(" +
- " document.getElementById('main-nav'));" +
- " }" +
- "})();");
- }
-
- public static void getNotificationCount(WebView wv) {
- wv.loadUrl("javascript: ( function() {" +
- " if (document.getElementById('notification')) {" +
- " var count = document.getElementById('notification').innerHTML;" +
- " NotificationCounter.setNotificationCount(count.replace(/(\\r\\n|\\n|\\r)/gm, \"\"));" +
- " } else {" +
- " NotificationCounter.setNotificationCount('0');" +
- " }" +
- " if (document.getElementById('conversation')) {" +
- " var count = document.getElementById('conversation').innerHTML;" +
- " NotificationCounter.setConversationCount(count.replace(/(\\r\\n|\\n|\\r)/gm, \"\"));" +
- " } else {" +
- " NotificationCounter.setConversationCount('0');" +
- " }" +
- "})();");
- }
-}
diff --git a/app/src/main/java/de/baumann/diaspora/utils/PrefManager.java b/app/src/main/java/de/baumann/diaspora/utils/PrefManager.java
deleted file mode 100644
index 9cceb3e38..000000000
--- a/app/src/main/java/de/baumann/diaspora/utils/PrefManager.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package de.baumann.diaspora.utils;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.preference.PreferenceManager;
-
-public class PrefManager {
-
- private final Context context;
- private boolean loadImages = true;
- private int minimumFontSize = 0;
-
- public PrefManager(Context ctx) {
- SharedPreferences sp = null;
- this.context = ctx;
- try {
- sp = PreferenceManager.getDefaultSharedPreferences(context);
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- if (sp != null) {
- loadImages = sp.getBoolean("loadImages", true);
- minimumFontSize = sp.getInt("minimumFontSize", 8);
- }
- }
-
- public boolean getLoadImages() {
- return loadImages;
- }
-
- public void setLoadImages(boolean loadImages) {
- this.loadImages = loadImages;
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
- Editor edit = sp.edit();
- edit.putBoolean("loadImages", loadImages);
- edit.commit();
- }
-
- public int getMinimumFontSize() {
- return minimumFontSize;
- }
-
- public void setMinimumFontSize(int minimumFontSize) {
- this.minimumFontSize = minimumFontSize;
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
- Editor edit = sp.edit();
- edit.putInt("minimumFontSize", minimumFontSize);
- edit.commit();
- }
-}
diff --git a/app/src/main/java/net/gsantner/opoc/activity/GsFragmentBase.java b/app/src/main/java/net/gsantner/opoc/activity/GsFragmentBase.java
new file mode 100644
index 000000000..93019ce93
--- /dev/null
+++ b/app/src/main/java/net/gsantner/opoc/activity/GsFragmentBase.java
@@ -0,0 +1,129 @@
+/*#######################################################
+ *
+ * Maintained by Gregor Santner, 2017-
+ * https://gsantner.net/
+ *
+ * License: Apache 2.0
+ * https://github.com/gsantner/opoc/#licensing
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+#########################################################*/
+package net.gsantner.opoc.activity;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import net.gsantner.opoc.util.ContextUtils;
+
+import butterknife.ButterKnife;
+
+/**
+ * A common base fragment to extend from
+ */
+public abstract class GsFragmentBase extends Fragment {
+ private boolean _fragmentFirstTimeVisible = true;
+ private final Object _fragmentFirstTimeVisibleSync = new Object();
+
+ protected ContextUtils _cu;
+ protected Bundle _savedInstanceState = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ /**
+ * Inflate the fragments layout. Don't override this method, just supply the needed
+ * {@link LayoutRes} via abstract method {@link #getLayoutResId()}, super does the rest
+ */
+ @Deprecated
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ _cu = new ContextUtils(inflater.getContext());
+ _cu.setAppLanguage(getAppLanguage());
+ _savedInstanceState = savedInstanceState;
+ View view = inflater.inflate(getLayoutResId(), container, false);
+ ButterKnife.bind(this, view);
+ return view;
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ view.postDelayed(() -> {
+ synchronized (_fragmentFirstTimeVisibleSync) {
+ if (getUserVisibleHint() && isVisible() && _fragmentFirstTimeVisible) {
+ _fragmentFirstTimeVisible = false;
+ onFragmentFirstTimeVisible();
+ }
+ }
+ }, 1);
+ }
+
+ /**
+ * Get a tag from the fragment, allows faster distinction
+ *
+ * @return This fragments tag
+ */
+ public abstract String getFragmentTag();
+
+
+ /**
+ * Get the layout to be inflated in the fragment
+ *
+ * @return Layout resource id
+ */
+ @LayoutRes
+ protected abstract int getLayoutResId();
+
+ /**
+ * Event to be called when the back button was pressed
+ * True should be returned when this was handled by the fragment
+ * and no further handling in the view hierarchy is needed
+ *
+ * @return True if back handled by fragment
+ */
+ public boolean onBackPressed() {
+ return false;
+ }
+
+ /**
+ * Set the language to be used in this fragment
+ * Defaults to resolve the language from sharedpreferences: pref_key__language
+ *
+ * @return Empty string for system language, or an android locale code
+ */
+ public String getAppLanguage() {
+ if (getContext() != null) {
+ return getContext().getSharedPreferences("app", Context.MODE_PRIVATE)
+ .getString("pref_key__language", "");
+ }
+ return "";
+ }
+
+ /**
+ * This will be called when this fragment gets the first time visible
+ */
+ public void onFragmentFirstTimeVisible() {
+ }
+
+ @Override
+ public void setUserVisibleHint(boolean isVisibleToUser) {
+ super.setUserVisibleHint(isVisibleToUser);
+ synchronized (_fragmentFirstTimeVisibleSync) {
+ if (isVisibleToUser && _fragmentFirstTimeVisible) {
+ _fragmentFirstTimeVisible = false;
+ onFragmentFirstTimeVisible();
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/net/gsantner/opoc/format/markdown/SimpleMarkdownParser.java b/app/src/main/java/net/gsantner/opoc/format/markdown/SimpleMarkdownParser.java
new file mode 100644
index 000000000..adaafa3b1
--- /dev/null
+++ b/app/src/main/java/net/gsantner/opoc/format/markdown/SimpleMarkdownParser.java
@@ -0,0 +1,228 @@
+/*#######################################################
+ *
+ * Maintained by Gregor Santner, 2016-
+ * https://gsantner.net/
+ *
+ * License: Apache 2.0
+ * https://github.com/gsantner/opoc/#licensing
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+#########################################################*/
+
+/*
+ * Parses most common markdown tags. Only inline tags are supported, multiline/block syntax
+ * is not supported (citation, multiline code, ..). This is intended to stay as easy as possible.
+ *
+ * You can e.g. apply a accent color by replacing #000001 with your accentColor string.
+ *
+ * FILTER_ANDROID_TEXTVIEW output is intended to be used at simple Android TextViews,
+ * were a limited set of _html tags is supported. This allow to still display e.g. a simple
+ * CHANGELOG.md file without including a WebView for showing HTML, or other additional UI-libraries.
+ *
+ * FILTER_WEB is intended to be used at engines understanding most common HTML tags.
+ */
+
+package net.gsantner.opoc.format.markdown;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+/**
+ * Simple Markdown Parser
+ */
+@SuppressWarnings({"WeakerAccess", "CaughtExceptionImmediatelyRethrown", "SameParameterValue", "unused", "SpellCheckingInspection", "RepeatedSpace", "SingleCharAlternation", "Convert2Lambda"})
+public class SimpleMarkdownParser {
+ //########################
+ //## Statics
+ //########################
+ public interface SmpFilter {
+ String filter(String text);
+ }
+
+ public final static SmpFilter FILTER_ANDROID_TEXTVIEW = new SmpFilter() {
+ @Override
+ public String filter(String text) {
+ // TextView supports a limited set of html tags, most notably
+ // a href, b, big, font size&color, i, li, small, u
+
+ // Don't start new line if 2 empty lines and heading
+ while (text.contains("\n\n#")) {
+ text = text.replace("\n\n#", "\n#");
+ }
+
+ return text
+ .replaceAll("(?s)", "") // HTML comments
+ .replace("\n\n", "\n \n") // Start new line if 2 empty lines
+ .replace("~°", " ") // double space/half tab
+ .replaceAll("(?m)^### (.*)$", "$1 ") // h3
+ .replaceAll("(?m)^## (.*)$", "$1 ") // h2 (DEP: h3)
+ .replaceAll("(?m)^# (.*)$", "$1 ") // h1 (DEP: h2,h3)
+ .replaceAll("!\\[(.*?)\\]\\((.*?)\\)", "$1 ") // img
+ .replaceAll("\\[(.*?)\\]\\((.*?)\\)", "$1 ") // a href (DEP: img)
+ .replaceAll("<(http|https):\\/\\/(.*)>", "$1://$2 ") // a href (DEP: img)
+ .replaceAll("(?m)^([-*] )(.*)$", "• $2 ") // unordered list + end line
+ .replaceAll("(?m)^ (-|\\*) ([^<]*)$", " • $2 ") // unordered list2 + end line
+ .replaceAll("`([^<]*)`", "$1 ") // code
+ .replace("\\*", "●") // temporary replace escaped star symbol
+ .replaceAll("(?m)\\*\\*(.*)\\*\\*", "$1 ") // bold (DEP: temp star)
+ .replaceAll("(?m)\\*(.*)\\*", "$1 ") // italic (DEP: temp star code)
+ .replace("●", "*") // restore escaped star symbol (DEP: b,i)
+ .replaceAll("(?m) $", " ") // new line (DEP: ul)
+ ;
+ }
+ };
+
+ public final static SmpFilter FILTER_WEB = new SmpFilter() {
+ @Override
+ public String filter(String text) {
+ // Don't start new line if 2 empty lines and heading
+ while (text.contains("\n\n#")) {
+ text = text.replace("\n\n#", "\n#");
+ }
+
+ text = text
+ .replaceAll("(?s)", "") // HTML comments
+ .replace("\n\n", "\n \n") // Start new line if 2 empty lines
+ .replaceAll("~°", " ") // double space/half tab
+ .replaceAll("(?m)^### (.*)$", "$1 ") // h3
+ .replaceAll("(?m)^## (.*)$", "$1 ") /// h2 (DEP: h3)
+ .replaceAll("(?m)^# (.*)$", "$1 ") // h1 (DEP: h2,h3)
+ .replaceAll("!\\[(.*?)\\]\\((.*?)\\)", " ") // img
+ .replaceAll("<(http|https):\\/\\/(.*)>", "$1://$2 ") // a href (DEP: img)
+ .replaceAll("\\[(.*?)\\]\\((.*?)\\)", "$1 ") // a href (DEP: img)
+ .replaceAll("(?m)^([-*] )(.*)$", "• $2 ") // unordered list + end line
+ .replaceAll("(?m)^ (-|\\*) ([^<]*)$", " • $2 ") // unordered list2 + end line
+ .replaceAll("`([^<]*)`", "$1
") // code
+ .replace("\\*", "●") // temporary replace escaped star symbol
+ .replaceAll("(?m)\\*\\*(.*)\\*\\*", "$1 ") // bold (DEP: temp star)
+ .replaceAll("(?m)\\*(.*)\\*", "$1 ") // italic (DEP: temp star code)
+ .replace("●", "*") // restore escaped star symbol (DEP: b,i)
+ .replaceAll("(?m) $", " ") // new line (DEP: ul)
+ ;
+ return text;
+ }
+ };
+
+ public final static SmpFilter FILTER_CHANGELOG = new SmpFilter() {
+ @Override
+ public String filter(String text) {
+ text = text
+ .replace("New:", "New: ")
+ .replace("Added:", "Added: ")
+ .replace("Add:", "Add: ")
+ .replace("Fixed:", "Fixed: ")
+ .replace("Fix:", "Fix: ")
+ .replace("Removed:", "Removed: ")
+ .replace("Updated:", "Updated: ")
+ .replace("Improved:", "Improved: ")
+ .replace("Modified:", "Modified: ")
+ .replace("Mod:", "Mod: ")
+ ;
+ return text;
+ }
+ };
+
+ //########################
+ //## Singleton
+ //########################
+ private static SimpleMarkdownParser __instance;
+
+ public static SimpleMarkdownParser get() {
+ if (__instance == null) {
+ __instance = new SimpleMarkdownParser();
+ }
+ return __instance;
+ }
+
+ //########################
+ //## Members, Constructors
+ //########################
+ private SmpFilter _defaultSmpFilter;
+ private String _html;
+
+ public SimpleMarkdownParser() {
+ setDefaultSmpFilter(FILTER_WEB);
+ }
+
+ //########################
+ //## Methods
+ //########################
+ public SimpleMarkdownParser setDefaultSmpFilter(SmpFilter defaultSmpFilter) {
+ _defaultSmpFilter = defaultSmpFilter;
+ return this;
+ }
+
+ public SimpleMarkdownParser parse(String filepath, SmpFilter... smpFilters) throws IOException {
+ return parse(new FileInputStream(filepath), "", smpFilters);
+ }
+
+ public SimpleMarkdownParser parse(InputStream inputStream, String lineMdPrefix, SmpFilter... smpFilters) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ BufferedReader br = null;
+ String line;
+
+ try {
+ br = new BufferedReader(new InputStreamReader(inputStream));
+ while ((line = br.readLine()) != null) {
+ sb.append(lineMdPrefix);
+ sb.append(line);
+ sb.append("\n");
+ }
+ } catch (IOException rethrow) {
+ _html = "";
+ throw rethrow;
+ } finally {
+ if (br != null) {
+ try {
+ br.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+ _html = parse(sb.toString(), "", smpFilters).getHtml();
+ return this;
+ }
+
+ public SimpleMarkdownParser parse(String markdown, String lineMdPrefix, SmpFilter... smpFilters) throws IOException {
+ _html = markdown;
+ if (smpFilters.length == 0) {
+ smpFilters = new SmpFilter[]{_defaultSmpFilter};
+ }
+ for (SmpFilter smpFilter : smpFilters) {
+ _html = smpFilter.filter(_html).trim();
+ }
+ return this;
+ }
+
+ public String getHtml() {
+ return _html;
+ }
+
+ public SimpleMarkdownParser setHtml(String html) {
+ _html = html;
+ return this;
+ }
+
+ public SimpleMarkdownParser removeMultiNewlines() {
+ _html = _html.replace("\n", "").replaceAll("( ){3,}", " ");
+ return this;
+ }
+
+ public SimpleMarkdownParser replaceBulletCharacter(String replacment) {
+ _html = _html.replace("•", replacment);
+ return this;
+ }
+
+ public SimpleMarkdownParser replaceColor(String hexColor, int newIntColor) {
+ _html = _html.replace(hexColor, String.format("#%06X", 0xFFFFFF & newIntColor));
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return _html != null ? _html : "";
+ }
+}
diff --git a/app/src/main/java/net/gsantner/opoc/preference/PropertyBackend.java b/app/src/main/java/net/gsantner/opoc/preference/PropertyBackend.java
new file mode 100644
index 000000000..c27d9bce9
--- /dev/null
+++ b/app/src/main/java/net/gsantner/opoc/preference/PropertyBackend.java
@@ -0,0 +1,49 @@
+/*#######################################################
+ *
+ * Maintained by Gregor Santner, 2018-
+ * https://gsantner.net/
+ *
+ * License: Apache 2.0
+ * https://github.com/gsantner/opoc/#licensing
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+#########################################################*/
+package net.gsantner.opoc.preference;
+
+import java.util.List;
+
+@SuppressWarnings({"UnusedReturnValue", "SpellCheckingInspection", "unused", "SameParameterValue"})
+public interface PropertyBackend {
+ String getString(TKEY key, String defaultValue);
+
+ int getInt(TKEY key, int defaultValue);
+
+ long getLong(TKEY key, long defaultValue);
+
+ boolean getBool(TKEY key, boolean defaultValue);
+
+ float getFloat(TKEY key, float defaultValue);
+
+ double getDouble(TKEY key, double defaultValue);
+
+ List getIntList(TKEY key);
+
+ List getStringList(TKEY key);
+
+ TTHIS setString(TKEY key, String value);
+
+ TTHIS setInt(TKEY key, int value);
+
+ TTHIS setLong(TKEY key, long value);
+
+ TTHIS setBool(TKEY key, boolean value);
+
+ TTHIS setFloat(TKEY key, float value);
+
+ TTHIS setDouble(TKEY key, double value);
+
+ TTHIS setIntList(TKEY key, List value);
+
+ TTHIS setStringList(TKEY key, List value);
+
+}
diff --git a/app/src/main/java/net/gsantner/opoc/preference/SharedPreferencesPropertyBackend.java b/app/src/main/java/net/gsantner/opoc/preference/SharedPreferencesPropertyBackend.java
new file mode 100644
index 000000000..275d88a59
--- /dev/null
+++ b/app/src/main/java/net/gsantner/opoc/preference/SharedPreferencesPropertyBackend.java
@@ -0,0 +1,499 @@
+/*#######################################################
+ *
+ * Maintained by Gregor Santner, 2016-
+ * https://gsantner.net/
+ *
+ * License: Apache 2.0
+ * https://github.com/gsantner/opoc/#licensing
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+#########################################################*/
+
+/*
+ * This is a wrapper for settings based on SharedPreferences
+ * with keys in resources. Extend from this class and add
+ * getters/setters for the app's settings.
+ * Example:
+ public boolean isAppFirstStart(boolean doSet) {
+ int value = getInt(R.string.pref_key__app_first_start, -1);
+ if (doSet) {
+ setBool(true);
+ }
+ return value;
+ }
+
+ public boolean isAppCurrentVersionFirstStart(boolean doSet) {
+ int value = getInt(R.string.pref_key__app_first_start_current_version, -1);
+ if (doSet) {
+ setInt(R.string.pref_key__app_first_start_current_version, BuildConfig.VERSION_CODE);
+ }
+ return value != BuildConfig.VERSION_CODE;
+ }
+ */
+
+package net.gsantner.opoc.preference;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.support.annotation.ColorRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
+import android.support.v4.content.ContextCompat;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+
+/**
+ * Wrapper for settings based on SharedPreferences, optionally with keys in resources
+ * Default SharedPreference (_prefApp) will be taken if no SP is specified, else the first one
+ */
+@SuppressWarnings({"WeakerAccess", "unused", "SpellCheckingInspection", "SameParameterValue"})
+public class SharedPreferencesPropertyBackend implements PropertyBackend {
+ protected static final String ARRAY_SEPARATOR = "%%%";
+ protected static final String ARRAY_SEPARATOR_SUBSTITUTE = "§§§";
+ public static final String SHARED_PREF_APP = "app";
+
+ //
+ // Members, Constructors
+ //
+ protected final SharedPreferences _prefApp;
+ protected final String _prefAppName;
+ protected final Context _context;
+
+ public SharedPreferencesPropertyBackend(final Context context) {
+ this(context, SHARED_PREF_APP);
+ }
+
+ public SharedPreferencesPropertyBackend(final Context context, final String prefAppName) {
+ _context = context.getApplicationContext();
+ _prefAppName = TextUtils.isEmpty(prefAppName) ?
+ _context.getPackageName() + "_preferences" : prefAppName;
+ _prefApp = _context.getSharedPreferences(_prefAppName, Context.MODE_PRIVATE);
+ }
+
+ //
+ // Methods
+ //
+ public Context getContext() {
+ return _context;
+ }
+
+ public boolean isKeyEqual(String key, int stringKeyResourceId) {
+ return key.equals(rstr(stringKeyResourceId));
+ }
+
+ public void resetSettings() {
+ resetSettings(_prefApp);
+ }
+
+ @SuppressLint("ApplySharedPref")
+ public void resetSettings(final SharedPreferences pref) {
+ pref.edit().clear().commit();
+ }
+
+ public boolean isPrefSet(@StringRes int stringKeyResourceId) {
+ return isPrefSet(_prefApp, stringKeyResourceId);
+ }
+
+ public boolean isPrefSet(final SharedPreferences pref, @StringRes int stringKeyResourceId) {
+ return pref.contains(rstr(stringKeyResourceId));
+ }
+
+ public void registerPreferenceChangedListener(SharedPreferences.OnSharedPreferenceChangeListener value) {
+ registerPreferenceChangedListener(_prefApp, value);
+ }
+
+ public void registerPreferenceChangedListener(final SharedPreferences pref, SharedPreferences.OnSharedPreferenceChangeListener value) {
+ pref.registerOnSharedPreferenceChangeListener(value);
+ }
+
+ public void unregisterPreferenceChangedListener(SharedPreferences.OnSharedPreferenceChangeListener value) {
+ unregisterPreferenceChangedListener(_prefApp, value);
+ }
+
+ public void unregisterPreferenceChangedListener(final SharedPreferences pref, SharedPreferences.OnSharedPreferenceChangeListener value) {
+ pref.unregisterOnSharedPreferenceChangeListener(value);
+ }
+
+ public SharedPreferences getDefaultPreferences() {
+ return _prefApp;
+ }
+
+ public SharedPreferences.Editor getDefaultPreferencesEditor() {
+ return _prefApp.edit();
+ }
+
+ public String getDefaultPreferencesName() {
+ return _prefAppName;
+ }
+
+
+ private SharedPreferences gp(final SharedPreferences... pref) {
+ return (pref != null && pref.length > 0 ? pref[0] : _prefApp);
+ }
+
+ //
+ // Getter for resources
+ //
+ public String rstr(@StringRes int stringKeyResourceId) {
+ return _context.getString(stringKeyResourceId);
+ }
+
+ public int rcolor(@ColorRes int resColorId) {
+ return ContextCompat.getColor(_context, resColorId);
+ }
+
+
+ //
+ // Getter & Setter for String
+ //
+ public void setString(@StringRes int keyResourceId, String value, final SharedPreferences... pref) {
+ gp(pref).edit().putString(rstr(keyResourceId), value).apply();
+ }
+
+ public void setString(String key, String value, final SharedPreferences... pref) {
+ gp(pref).edit().putString(key, value).apply();
+ }
+
+ public void setString(@StringRes int keyResourceId, @StringRes int defaultValueResourceId, final SharedPreferences... pref) {
+ gp(pref).edit().putString(rstr(keyResourceId), rstr(defaultValueResourceId)).apply();
+ }
+
+ public String getString(@StringRes int keyResourceId, String defaultValue, final SharedPreferences... pref) {
+ return gp(pref).getString(rstr(keyResourceId), defaultValue);
+ }
+
+ public String getString(@StringRes int keyResourceId, @StringRes int defaultValueResourceId, final SharedPreferences... pref) {
+ return gp(pref).getString(rstr(keyResourceId), rstr(defaultValueResourceId));
+ }
+
+ public String getString(String key, String defaultValue, final SharedPreferences... pref) {
+ return gp(pref).getString(key, defaultValue);
+ }
+
+ public String getString(@StringRes int keyResourceId, String defaultValue, @StringRes int keyResourceIdDefaultValue, final SharedPreferences... pref) {
+ return gp(pref).getString(rstr(keyResourceId), rstr(keyResourceIdDefaultValue));
+ }
+
+ private void setStringListOne(String key, List values, final SharedPreferences pref) {
+ StringBuilder sb = new StringBuilder();
+ for (String value : values) {
+ sb.append(ARRAY_SEPARATOR);
+ sb.append(value.replace(ARRAY_SEPARATOR, ARRAY_SEPARATOR_SUBSTITUTE));
+ }
+ setString(key, sb.toString().replaceFirst(ARRAY_SEPARATOR, ""), pref);
+ }
+
+ private ArrayList getStringListOne(String key, final SharedPreferences pref) {
+ ArrayList ret = new ArrayList<>();
+ String value = pref
+ .getString(key, ARRAY_SEPARATOR)
+ .replace(ARRAY_SEPARATOR_SUBSTITUTE, ARRAY_SEPARATOR);
+ if (value.equals(ARRAY_SEPARATOR)) {
+ return ret;
+ }
+ ret.addAll(Arrays.asList(value.split(ARRAY_SEPARATOR)));
+ return ret;
+ }
+
+ public void setStringArray(@StringRes int keyResourceId, String[] values, final SharedPreferences... pref) {
+ setStringArray(rstr(keyResourceId), values, pref);
+ }
+
+ public void setStringArray(String key, String[] values, final SharedPreferences... pref) {
+ setStringListOne(key, Arrays.asList(values), gp(pref));
+ }
+
+ public void setStringList(@StringRes int keyResourceId, List values, final SharedPreferences... pref) {
+ setStringArray(rstr(keyResourceId), values.toArray(new String[values.size()]), pref);
+ }
+
+ public void setStringList(String key, List values, final SharedPreferences... pref) {
+ setStringArray(key, values.toArray(new String[values.size()]), pref);
+ }
+
+ @NonNull
+ public String[] getStringArray(@StringRes int keyResourceId, final SharedPreferences... pref) {
+ return getStringArray(rstr(keyResourceId), pref);
+ }
+
+ @NonNull
+ public String[] getStringArray(String key, final SharedPreferences... pref) {
+ List list = getStringListOne(key, gp(pref));
+ return list.toArray(new String[list.size()]);
+ }
+
+ public ArrayList getStringList(@StringRes int keyResourceId, final SharedPreferences... pref) {
+ return getStringListOne(rstr(keyResourceId), gp(pref));
+ }
+
+ public ArrayList getStringList(String key, final SharedPreferences... pref) {
+ return getStringListOne(key, gp(pref));
+ }
+
+ //
+ // Getter & Setter for integer
+ //
+ public void setInt(@StringRes int keyResourceId, int value, final SharedPreferences... pref) {
+ gp(pref).edit().putInt(rstr(keyResourceId), value).apply();
+ }
+
+ public void setInt(String key, int value, final SharedPreferences... pref) {
+ gp(pref).edit().putInt(key, value).apply();
+ }
+
+ public int getInt(@StringRes int keyResourceId, int defaultValue, final SharedPreferences... pref) {
+ return gp(pref).getInt(rstr(keyResourceId), defaultValue);
+ }
+
+ public int getInt(String key, int defaultValue, final SharedPreferences... pref) {
+ return gp(pref).getInt(key, defaultValue);
+ }
+
+ public int getIntOfStringPref(@StringRes int keyResId, int defaultValue, final SharedPreferences... pref) {
+ return getIntOfStringPref(rstr(keyResId), defaultValue, gp(pref));
+ }
+
+ public int getIntOfStringPref(String key, int defaultValue, final SharedPreferences... pref) {
+ String strNum = getString(key, Integer.toString(defaultValue), gp(pref));
+ return Integer.valueOf(strNum);
+ }
+
+ private void setIntListOne(String key, List values, final SharedPreferences pref) {
+ StringBuilder sb = new StringBuilder();
+ for (Integer value : values) {
+ sb.append(ARRAY_SEPARATOR);
+ sb.append(value.toString());
+ }
+ setString(key, sb.toString().replaceFirst(ARRAY_SEPARATOR, ""), pref);
+ }
+
+ private ArrayList getIntListOne(String key, final SharedPreferences pref) {
+ ArrayList ret = new ArrayList<>();
+ String value = pref.getString(key, ARRAY_SEPARATOR);
+ if (value.equals(ARRAY_SEPARATOR)) {
+ return ret;
+ }
+ for (String s : value.split(ARRAY_SEPARATOR)) {
+ ret.add(Integer.parseInt(s));
+ }
+ return ret;
+ }
+
+ public void setIntArray(@StringRes int keyResourceId, Integer[] values, final SharedPreferences... pref) {
+ setIntArray(rstr(keyResourceId), values, gp(pref));
+ }
+
+ public void setIntArray(String key, Integer[] values, final SharedPreferences... pref) {
+ setIntListOne(key, Arrays.asList(values), gp(pref));
+ }
+
+ public Integer[] getIntArray(@StringRes int keyResourceId, final SharedPreferences... pref) {
+ return getIntArray(rstr(keyResourceId), gp(pref));
+ }
+
+ public Integer[] getIntArray(String key, final SharedPreferences... pref) {
+ List data = getIntListOne(key, gp(pref));
+ return data.toArray(new Integer[data.size()]);
+ }
+
+
+ public void setIntList(@StringRes int keyResourceId, List values, final SharedPreferences... pref) {
+ setIntListOne(rstr(keyResourceId), values, gp(pref));
+ }
+
+ public void setIntList(String key, List values, final SharedPreferences... pref) {
+ setIntListOne(key, values, gp(pref));
+ }
+
+ public ArrayList getIntList(@StringRes int keyResourceId, final SharedPreferences... pref) {
+ return getIntListOne(rstr(keyResourceId), gp(pref));
+ }
+
+ public ArrayList getIntList(String key, final SharedPreferences... pref) {
+ return getIntListOne(key, gp(pref));
+ }
+
+
+ //
+ // Getter & Setter for Long
+ //
+ public void setLong(@StringRes int keyResourceId, long value, final SharedPreferences... pref) {
+ gp(pref).edit().putLong(rstr(keyResourceId), value).apply();
+ }
+
+ public void setLong(String key, long value, final SharedPreferences... pref) {
+ gp(pref).edit().putLong(key, value).apply();
+ }
+
+ public long getLong(@StringRes int keyResourceId, long defaultValue, final SharedPreferences... pref) {
+ return gp(pref).getLong(rstr(keyResourceId), defaultValue);
+ }
+
+ public long getLong(String key, long defaultValue, final SharedPreferences... pref) {
+ return gp(pref).getLong(key, defaultValue);
+ }
+
+ //
+ // Getter & Setter for Float
+ //
+ public void setFloat(@StringRes int keyResourceId, float value, final SharedPreferences... pref) {
+ gp(pref).edit().putFloat(rstr(keyResourceId), value).apply();
+ }
+
+ public void setFloat(String key, float value, final SharedPreferences... pref) {
+ gp(pref).edit().putFloat(key, value).apply();
+ }
+
+ public float getFloat(@StringRes int keyResourceId, float defaultValue, final SharedPreferences... pref) {
+ return gp(pref).getFloat(rstr(keyResourceId), defaultValue);
+ }
+
+ public float getFloat(String key, float defaultValue, final SharedPreferences... pref) {
+ return gp(pref).getFloat(key, defaultValue);
+ }
+
+ //
+ // Getter & Setter for Double
+ //
+ public void setDouble(@StringRes int keyResourceId, double value, final SharedPreferences... pref) {
+ setLong(rstr(keyResourceId), Double.doubleToRawLongBits(value));
+ }
+
+ public void setDouble(String key, double value, final SharedPreferences... pref) {
+ setLong(key, Double.doubleToRawLongBits(value));
+ }
+
+ public double getDouble(@StringRes int keyResourceId, double defaultValue, final SharedPreferences... pref) {
+ return getDouble(rstr(keyResourceId), defaultValue, gp(pref));
+ }
+
+ public double getDouble(String key, double defaultValue, final SharedPreferences... pref) {
+ return Double.longBitsToDouble(getLong(key, Double.doubleToRawLongBits(defaultValue), gp(pref)));
+ }
+
+ //
+ // Getter & Setter for boolean
+ //
+ public void setBool(@StringRes int keyResourceId, boolean value, final SharedPreferences... pref) {
+ gp(pref).edit().putBoolean(rstr(keyResourceId), value).apply();
+ }
+
+ public void setBool(String key, boolean value, final SharedPreferences... pref) {
+ gp(pref).edit().putBoolean(key, value).apply();
+ }
+
+ public boolean getBool(@StringRes int keyResourceId, boolean defaultValue, final SharedPreferences... pref) {
+ return gp(pref).getBoolean(rstr(keyResourceId), defaultValue);
+ }
+
+ public boolean getBool(String key, boolean defaultValue, final SharedPreferences... pref) {
+ return gp(pref).getBoolean(key, defaultValue);
+ }
+
+ //
+ // Getter & Setter for Color
+ //
+ public int getColor(String key, @ColorRes int defaultColor, final SharedPreferences... pref) {
+ return gp(pref).getInt(key, rcolor(defaultColor));
+ }
+
+ public int getColor(@StringRes int keyResourceId, @ColorRes int defaultColor, final SharedPreferences... pref) {
+ return gp(pref).getInt(rstr(keyResourceId), rcolor(defaultColor));
+ }
+
+ //
+ // PropertyBackend implementations
+ //
+ @Override
+ public String getString(String key, String defaultValue) {
+ return getString(key, defaultValue, _prefApp);
+ }
+
+ @Override
+ public int getInt(String key, int defaultValue) {
+ return getInt(key, defaultValue, _prefApp);
+ }
+
+ @Override
+ public long getLong(String key, long defaultValue) {
+ return getLong(key, defaultValue, _prefApp);
+ }
+
+ @Override
+ public boolean getBool(String key, boolean defaultValue) {
+ return getBool(key, defaultValue, _prefApp);
+ }
+
+ @Override
+ public float getFloat(String key, float defaultValue) {
+ return getFloat(key, defaultValue, _prefApp);
+ }
+
+ @Override
+ public double getDouble(String key, double defaultValue) {
+ return getDouble(key, defaultValue, _prefApp);
+ }
+
+ @Override
+ public ArrayList getIntList(String key) {
+ return getIntList(key, _prefApp);
+ }
+
+ @Override
+ public ArrayList getStringList(String key) {
+ return getStringList(key, _prefApp);
+ }
+
+ @Override
+ public SharedPreferencesPropertyBackend setString(String key, String value) {
+ setString(key, value, _prefApp);
+ return this;
+ }
+
+ @Override
+ public SharedPreferencesPropertyBackend setInt(String key, int value) {
+ setInt(key, value, _prefApp);
+ return this;
+ }
+
+ @Override
+ public SharedPreferencesPropertyBackend setLong(String key, long value) {
+ setLong(key, value, _prefApp);
+ return this;
+ }
+
+ @Override
+ public SharedPreferencesPropertyBackend setBool(String key, boolean value) {
+ setBool(key, value, _prefApp);
+ return this;
+ }
+
+ @Override
+ public SharedPreferencesPropertyBackend setFloat(String key, float value) {
+ setFloat(key, value, _prefApp);
+ return this;
+ }
+
+ @Override
+ public SharedPreferencesPropertyBackend setDouble(String key, double value) {
+ setDouble(key, value, _prefApp);
+ return this;
+ }
+
+ @Override
+ public SharedPreferencesPropertyBackend setIntList(String key, List value) {
+ setIntListOne(key, value, _prefApp);
+ return this;
+ }
+
+ @Override
+ public SharedPreferencesPropertyBackend setStringList(String key, List value) {
+ setStringListOne(key, value, _prefApp);
+ return this;
+ }
+}
diff --git a/app/src/main/java/net/gsantner/opoc/preference/nonsupport/LanguagePreference.java b/app/src/main/java/net/gsantner/opoc/preference/nonsupport/LanguagePreference.java
new file mode 100644
index 000000000..76284a11f
--- /dev/null
+++ b/app/src/main/java/net/gsantner/opoc/preference/nonsupport/LanguagePreference.java
@@ -0,0 +1,188 @@
+/*#######################################################
+ *
+ * Maintained by Gregor Santner, 2017-
+ * https://gsantner.net/
+ *
+ * License: Apache 2.0
+ * https://github.com/gsantner/opoc/#licensing
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+#########################################################*/
+
+/*
+ * Requires:
+ The BuildConfig field "APPLICATION_LANGUAGES" which is a array of all available languages
+ opoc/ContextUtils
+ * BuildConfig field can be defined by using the method below
+
+buildConfigField "String[]", "APPLICATION_LANGUAGES", "${getUsedAndroidLanguages()}"
+
+@SuppressWarnings(["UnnecessaryQualifiedReference", "SpellCheckingInspection", "GroovyUnusedDeclaration"])
+// Returns used android languages as a buildConfig array: {'de', 'it', ..}"
+static String getUsedAndroidLanguages() {
+ Set langs = new HashSet<>()
+ new File('.').eachFileRecurse(groovy.io.FileType.DIRECTORIES) {
+ final foldername = it.name
+ if (foldername.startsWith('values-') && !it.canonicalPath.contains("build" + File.separator + "intermediates")) {
+ new File(it.toString()).eachFileRecurse(groovy.io.FileType.FILES) {
+ if (it.name.toLowerCase().endsWith(".xml") && it.getCanonicalFile().getText('UTF-8').contains("
+ */
+package net.gsantner.opoc.preference.nonsupport;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+import android.preference.ListPreference;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+
+import net.gsantner.opoc.util.ContextUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * A {@link android.preference.ListPreference} that displays a list of languages to select from
+ */
+@SuppressWarnings({"unused", "SpellCheckingInspection", "WeakerAccess"})
+public class LanguagePreference extends ListPreference {
+ private static final String SYSTEM_LANGUAGE_CODE = "";
+
+ // The language of res/values/ -> (usually English)
+ public String _systemLanguageName = "System";
+ public String _defaultLanguageCode = "en";
+
+ public LanguagePreference(Context context) {
+ super(context);
+ loadLangs(context, null);
+ }
+
+ public LanguagePreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ loadLangs(context, attrs);
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public LanguagePreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ loadLangs(context, attrs);
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public LanguagePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ loadLangs(context, attrs);
+ }
+
+ @Override
+ public boolean callChangeListener(Object newValue) {
+ if (newValue instanceof String) {
+ // Does not apply to existing UI, use recreate()
+ new ContextUtils(getContext()).setAppLanguage((String) newValue);
+ }
+ return super.callChangeListener(newValue);
+ }
+
+
+ private void loadLangs(Context context) {
+ loadLangs(context, null);
+ }
+
+ private void loadLangs(Context context, @Nullable AttributeSet attrs) {
+ setDefaultValue(SYSTEM_LANGUAGE_CODE);
+
+ // Fetch readable details
+ ContextUtils contextUtils = new ContextUtils(context);
+ List languages = new ArrayList<>();
+ Object bcof = contextUtils.getBuildConfigValue("DETECTED_ANDROID_LOCALES");
+ if (bcof instanceof String[]) {
+ for (String langId : (String[]) bcof) {
+ Locale locale = contextUtils.getLocaleByAndroidCode(langId);
+ languages.add(summarizeLocale(locale, langId) + ";" + langId);
+ }
+ }
+
+ // Sort languages naturally
+ Collections.sort(languages);
+
+ // Show in UI
+ String[] entries = new String[languages.size() + 2];
+ String[] entryval = new String[languages.size() + 2];
+ for (int i = 0; i < languages.size(); i++) {
+ entries[i + 2] = languages.get(i).split(";")[0];
+ entryval[i + 2] = languages.get(i).split(";")[1];
+ }
+ entryval[0] = SYSTEM_LANGUAGE_CODE;
+ entries[0] = _systemLanguageName + " » " + summarizeLocale(context.getResources().getConfiguration().locale, "");
+ entryval[1] = _defaultLanguageCode;
+ entries[1] = summarizeLocale(contextUtils.getLocaleByAndroidCode(_defaultLanguageCode), _defaultLanguageCode);
+
+ setEntries(entries);
+ setEntryValues(entryval);
+ }
+
+ // Concat english and localized language name
+ // Append country if country specific (e.g. Portuguese Brazil)
+ private String summarizeLocale(final Locale locale, final String localeAndroidCode) {
+ String country = locale.getDisplayCountry(locale);
+ String language = locale.getDisplayLanguage(locale);
+ String ret = locale.getDisplayLanguage(Locale.ENGLISH)
+ + " (" + language.substring(0, 1).toUpperCase(Locale.getDefault()) + language.substring(1)
+ + ((!country.isEmpty() && !country.toLowerCase(Locale.getDefault()).equals(language.toLowerCase(Locale.getDefault()))) ? (", " + country) : "")
+ + ")";
+
+ if (localeAndroidCode.equals("zh-rCN")) {
+ ret = ret.substring(0, ret.indexOf(" ") + 1) + "Simplified" + ret.substring(ret.indexOf(" "));
+ } else if (localeAndroidCode.equals("zh-rTW")) {
+ ret = ret.substring(0, ret.indexOf(" ") + 1) + "Traditional" + ret.substring(ret.indexOf(" "));
+ }
+
+ return ret;
+ }
+
+ // Add current language to summary
+ @Override
+ public CharSequence getSummary() {
+ Locale locale = new ContextUtils(getContext()).getLocaleByAndroidCode(getValue());
+ String prefix = TextUtils.isEmpty(super.getSummary())
+ ? "" : super.getSummary() + "\n\n";
+ return prefix + summarizeLocale(locale, getValue());
+ }
+
+ public String getSystemLanguageName() {
+ return _systemLanguageName;
+ }
+
+ public void setSystemLanguageName(String systemLanguageName) {
+ _systemLanguageName = systemLanguageName;
+ loadLangs(getContext());
+ }
+
+ public String getDefaultLanguageCode() {
+ return _defaultLanguageCode;
+ }
+
+ public void setDefaultLanguageCode(String defaultLanguageCode) {
+ _defaultLanguageCode = defaultLanguageCode;
+ loadLangs(getContext());
+ }
+}
diff --git a/app/src/main/java/net/gsantner/opoc/util/ActivityUtils.java b/app/src/main/java/net/gsantner/opoc/util/ActivityUtils.java
new file mode 100644
index 000000000..53a86cc1d
--- /dev/null
+++ b/app/src/main/java/net/gsantner/opoc/util/ActivityUtils.java
@@ -0,0 +1,166 @@
+/*#######################################################
+ *
+ * Maintained by Gregor Santner, 2016-
+ * https://gsantner.net/
+ *
+ * License: Apache 2.0
+ * https://github.com/gsantner/opoc/#licensing
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+#########################################################*/
+package net.gsantner.opoc.util;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.support.annotation.StringRes;
+import android.support.design.widget.Snackbar;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.widget.AppCompatTextView;
+import android.text.Html;
+import android.text.SpannableString;
+import android.text.method.LinkMovementMethod;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+import android.webkit.WebView;
+
+
+@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "SpellCheckingInspection"})
+public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils {
+ //########################
+ //## Members, Constructors
+ //########################
+ protected Activity _activity;
+
+ public ActivityUtils(final Activity activity) {
+ super(activity);
+ _activity = activity;
+ }
+
+ //########################
+ //## Methods
+ //########################
+
+ /**
+ * Animate to specified Activity
+ *
+ * @param to The class of the activity
+ * @param finishFromActivity true: Finish the current activity
+ * @param requestCode Request code for stating the activity, not waiting for result if null
+ */
+ public void animateToActivity(Class to, Boolean finishFromActivity, Integer requestCode) {
+ animateToActivity(new Intent(_activity, to), finishFromActivity, requestCode);
+ }
+
+ /**
+ * Animate to Activity specified in intent
+ * Requires animation resources
+ *
+ * @param intent Intent to open start an activity
+ * @param finishFromActivity true: Finish the current activity
+ * @param requestCode Request code for stating the activity, not waiting for result if null
+ */
+ public void animateToActivity(Intent intent, Boolean finishFromActivity, Integer requestCode) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ if (requestCode != null) {
+ _activity.startActivityForResult(intent, requestCode);
+ } else {
+ _activity.startActivity(intent);
+
+ }
+ _activity.overridePendingTransition(getResId(ResType.DIMEN, "fadein"), getResId(ResType.DIMEN, "fadeout"));
+ if (finishFromActivity != null && finishFromActivity) {
+ _activity.finish();
+ }
+ }
+
+
+ public void showSnackBar(@StringRes int stringResId, boolean showLong) {
+ Snackbar.make(_activity.findViewById(android.R.id.content), stringResId,
+ showLong ? Snackbar.LENGTH_LONG : Snackbar.LENGTH_SHORT).show();
+ }
+
+ public void showSnackBar(@StringRes int stringResId, boolean showLong, @StringRes int actionResId, View.OnClickListener listener) {
+ Snackbar.make(_activity.findViewById(android.R.id.content), stringResId,
+ showLong ? Snackbar.LENGTH_LONG : Snackbar.LENGTH_SHORT)
+ .setAction(actionResId, listener)
+ .show();
+ }
+
+ public void hideSoftKeyboard() {
+ InputMethodManager imm = (InputMethodManager) _activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
+ if (imm != null && _activity.getCurrentFocus() != null && _activity.getCurrentFocus().getWindowToken() != null) {
+ imm.hideSoftInputFromWindow(_activity.getCurrentFocus().getWindowToken(), 0);
+ }
+ }
+
+ public void showSoftKeyboard() {
+ InputMethodManager imm = (InputMethodManager) _activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
+ if (imm != null && _activity.getCurrentFocus() != null && _activity.getCurrentFocus().getWindowToken() != null) {
+ imm.showSoftInput(_activity.getCurrentFocus(), InputMethodManager.SHOW_FORCED);
+ }
+ }
+
+ public void showDialogWithHtmlTextView(@StringRes int resTitleId, String html) {
+ showDialogWithHtmlTextView(resTitleId, html, true, null);
+ }
+
+ public void showDialogWithHtmlTextView(@StringRes int resTitleId, String text, boolean isHtml, DialogInterface.OnDismissListener dismissedListener) {
+ AppCompatTextView textView = new AppCompatTextView(_context);
+ int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16,
+ _context.getResources().getDisplayMetrics());
+ textView.setMovementMethod(new LinkMovementMethod());
+ textView.setPadding(padding, 0, padding, 0);
+
+ textView.setText(isHtml ? new SpannableString(Html.fromHtml(text)) : text);
+ AlertDialog.Builder dialog = new AlertDialog.Builder(_context)
+ .setPositiveButton(android.R.string.ok, null)
+ .setOnDismissListener(dismissedListener)
+ .setTitle(resTitleId)
+ .setView(textView);
+ dialog.show();
+ }
+
+ public void showDialogWithRawFileInWebView(String fileInRaw, @StringRes int resTitleId) {
+ WebView wv = new WebView(_context);
+ wv.loadUrl("file:///android_res/raw/" + fileInRaw);
+ AlertDialog.Builder dialog = new AlertDialog.Builder(_context)
+ .setPositiveButton(android.R.string.ok, null)
+ .setTitle(resTitleId)
+ .setView(wv);
+ dialog.show();
+ }
+
+ // Toggle with no param, else set visibility according to first bool
+ public void toggleStatusbarVisibility(boolean... optionalForceVisible) {
+ WindowManager.LayoutParams attrs = _activity.getWindow().getAttributes();
+ int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN;
+ if (optionalForceVisible.length == 0) {
+ attrs.flags ^= flag;
+ } else if (optionalForceVisible.length == 1 && optionalForceVisible[0]) {
+ attrs.flags &= ~flag;
+ } else {
+ attrs.flags |= flag;
+ }
+ _activity.getWindow().setAttributes(attrs);
+ }
+
+ public void showGooglePlayEntryForThisApp() {
+ String pkgId = "details?id=" + _activity.getPackageName();
+ Intent goToMarket = new Intent(Intent.ACTION_VIEW, Uri.parse("market://" + pkgId));
+ goToMarket.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY |
+ (Build.VERSION.SDK_INT >= 21 ? Intent.FLAG_ACTIVITY_NEW_DOCUMENT : Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) |
+ Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ try {
+ _activity.startActivity(goToMarket);
+ } catch (ActivityNotFoundException e) {
+ _activity.startActivity(new Intent(Intent.ACTION_VIEW,
+ Uri.parse("http://play.google.com/store/apps/" + pkgId)));
+ }
+ }
+}
diff --git a/app/src/main/java/net/gsantner/opoc/util/AdBlock.java b/app/src/main/java/net/gsantner/opoc/util/AdBlock.java
new file mode 100644
index 000000000..304a4b852
--- /dev/null
+++ b/app/src/main/java/net/gsantner/opoc/util/AdBlock.java
@@ -0,0 +1,175 @@
+/*#######################################################
+ *
+ * Maintained by Gregor Santner, 2017-
+ * https://gsantner.net/
+ *
+ * License: Apache 2.0
+ * https://github.com/gsantner/opoc/#licensing
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+#########################################################*/
+
+/*
+ * Place adblock hosts file in raw: src/main/res/raw/adblock_domains__xyz.txt
+ * Always blocks both, www and non www
+
+ * Load hosts (call e.g. via Application-Object)
+AdBlock.getInstance().loadHostsFromRawAssetsAsync(context);
+
+ * Override inside a WebViewClient:
+public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
+ return AdBlock.getInstance().isAdHost(url)
+ ? AdBlock.createEmptyResponse()
+ : super.shouldInterceptRequest(view, url);
+}
+*/
+package net.gsantner.opoc.util;
+
+import android.content.Context;
+import android.util.Log;
+import android.webkit.WebResourceResponse;
+
+import com.github.dfa.diaspora_android.R;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.Field;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Simple Host-Based AdBlocker
+ */
+@SuppressWarnings({"WeakerAccess", "SpellCheckingInspection", "unused"})
+public class AdBlock {
+ private static final AdBlock instance = new AdBlock();
+
+ public static AdBlock getInstance() {
+ return instance;
+ }
+
+ //########################
+ //##
+ //## Members
+ //##
+ //########################
+ private final Set _adblockHostsFromRaw = new HashSet<>();
+ private final Set _adblockHosts = new HashSet<>();
+ private boolean _isLoaded;
+
+ //########################
+ //##
+ //## Methods
+ //##
+ //########################
+ private AdBlock() {
+ }
+
+ public boolean isAdHost(String urlS) {
+ if (urlS != null && !urlS.isEmpty() && urlS.startsWith("http")) {
+ try {
+ URI url = new URI(urlS);
+ String host = url.getHost().trim();
+ if (host.startsWith("www.") && host.length() >= 4) {
+ host = host.substring(4);
+ }
+ return _adblockHosts.contains(host) || _adblockHosts.contains("www." + host);
+ } catch (URISyntaxException e) {
+ e.printStackTrace();
+ }
+
+ }
+ return false;
+ }
+
+ public AdBlock reset() {
+ _adblockHosts.clear();
+ _adblockHosts.addAll(_adblockHostsFromRaw);
+ return this;
+ }
+
+ public boolean isLoaded() {
+ return _isLoaded;
+ }
+
+ public static WebResourceResponse createEmptyResponse() {
+ return new WebResourceResponse("text/plain", "utf-8", new ByteArrayInputStream("".getBytes()));
+ }
+
+ public void addBlockedHosts(String... hosts) {
+ for (String host : hosts) {
+ if (host != null) {
+ host = host.trim();
+ if (host.startsWith("www.") && host.length() >= 4) {
+ host = host.substring(4);
+ }
+ if (!host.startsWith("#") && !host.startsWith("\"")) {
+ _adblockHosts.add(host);
+ }
+ }
+ }
+
+ }
+
+ public void loadHostsFromRawAssetsAsync(final Context context) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ loadHostsFromRawAssets(context);
+ _isLoaded = true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }).start();
+ }
+
+ private void loadHostsFromRawAssets(Context context) throws IOException {
+ BufferedReader br = null;
+ String host;
+
+ _adblockHosts.clear();
+ for (int rawId : getAdblockIdsInRaw()) {
+ try {
+ br = new BufferedReader(new InputStreamReader(context.getResources().openRawResource(rawId)));
+ while ((host = br.readLine()) != null) {
+ addBlockedHosts(host);
+ }
+ } catch (Exception e) {
+ Log.d(AdBlock.class.getName(), "Error: Cannot read adblock res " + rawId);
+ } finally {
+ if (br != null) {
+ try {
+ br.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+ }
+ _adblockHostsFromRaw.clear();
+ _adblockHostsFromRaw.addAll(_adblockHosts);
+ }
+
+ private List getAdblockIdsInRaw() {
+ ArrayList adblockResIds = new ArrayList<>();
+ Field[] fields = R.raw.class.getFields();
+ for (Field field : fields) {
+ try {
+ int resId = field.getInt(field);
+ String resFilename = field.getName();
+ if (resFilename.startsWith("adblock_domains__")) {
+ adblockResIds.add(resId);
+ }
+ } catch (IllegalAccessException | IllegalArgumentException ignored) {
+ }
+ }
+ return adblockResIds;
+ }
+}
diff --git a/app/src/main/java/net/gsantner/opoc/util/Callback.java b/app/src/main/java/net/gsantner/opoc/util/Callback.java
new file mode 100644
index 000000000..a08ff682a
--- /dev/null
+++ b/app/src/main/java/net/gsantner/opoc/util/Callback.java
@@ -0,0 +1,34 @@
+/*#######################################################
+ *
+ * Maintained by Gregor Santner, 2018-
+ * https://gsantner.net/
+ *
+ * License: Apache 2.0
+ * https://github.com/gsantner/opoc/#licensing
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+#########################################################*/
+package net.gsantner.opoc.util;
+
+@SuppressWarnings("unused")
+public class Callback {
+ public interface a1 {
+ void callback(A arg1);
+ }
+
+ public interface a2 {
+ void callback(A arg1, B arg2);
+ }
+
+ public interface a3 {
+ void callback(A arg1, B arg2, C arg3);
+ }
+
+ public interface a4 {
+ void callback(A arg1, B arg2, C arg3, D arg4);
+ }
+
+ public interface a5 {
+ void callback(A arg1, B arg2, C arg3, D arg4, E arg5);
+ }
+}
diff --git a/app/src/main/java/net/gsantner/opoc/util/ContextUtils.java b/app/src/main/java/net/gsantner/opoc/util/ContextUtils.java
new file mode 100644
index 000000000..17d64424e
--- /dev/null
+++ b/app/src/main/java/net/gsantner/opoc/util/ContextUtils.java
@@ -0,0 +1,675 @@
+/*#######################################################
+ *
+ * Maintained by Gregor Santner, 2016-
+ * https://gsantner.net/
+ *
+ * License: Apache 2.0
+ * https://github.com/gsantner/opoc/#licensing
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+#########################################################*/
+package net.gsantner.opoc.util;
+
+import android.annotation.SuppressLint;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.VectorDrawable;
+import android.media.MediaScannerConnection;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.support.annotation.ColorInt;
+import android.support.annotation.ColorRes;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.Nullable;
+import android.support.annotation.RawRes;
+import android.support.annotation.StringRes;
+import android.support.graphics.drawable.VectorDrawableCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.drawable.DrawableCompat;
+import android.text.Html;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.method.LinkMovementMethod;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import net.gsantner.opoc.format.markdown.SimpleMarkdownParser;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.Method;
+import java.util.Locale;
+
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.graphics.Bitmap.CompressFormat;
+
+@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "ObsoleteSdkInt", "deprecation", "SpellCheckingInspection"})
+public class ContextUtils {
+ //
+ // Members, Constructors
+ //
+ protected Context _context;
+
+ public ContextUtils(Context context) {
+ _context = context;
+ }
+
+ public Context context() {
+ return _context;
+ }
+
+
+ //
+ // Class Methods
+ //
+ public enum ResType {
+ ID, BOOL, INTEGER, COLOR, STRING, ARRAY, DRAWABLE, PLURALS,
+ ANIM, ATTR, DIMEN, LAYOUT, MENU, RAW, STYLE, XML,
+ }
+
+ /**
+ * Find out the nuermical ressource id by given {@link ResType}
+ *
+ * @return A valid id if the id could be found, else 0
+ */
+ public int getResId(ResType resType, final String name) {
+ return _context.getResources().getIdentifier(name, resType.name().toLowerCase(), _context.getPackageName());
+ }
+
+ /**
+ * Get String by given string ressource id (nuermic)
+ */
+ public String rstr(@StringRes int strResId) {
+ return _context.getString(strResId);
+ }
+
+ /**
+ * Get String by given string ressource identifier (textual)
+ */
+ public String rstr(String strResKey) {
+ try {
+ return rstr(getResId(ResType.STRING, strResKey));
+ } catch (Resources.NotFoundException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Get drawable from given ressource identifier
+ */
+ public Drawable rdrawable(@DrawableRes int resId) {
+ return ContextCompat.getDrawable(_context, resId);
+ }
+
+ /**
+ * Get color by given color ressource id
+ */
+ public int rcolor(@ColorRes int resId) {
+ return ContextCompat.getColor(_context, resId);
+ }
+
+ /**
+ * Checks if all given (textual) ressource ids are available
+ *
+ * @param resType A {@link ResType}
+ * @param resIdsTextual A (textual) identifier to be awaited at R.restype.resIdsTextual
+ * @return True if all given ids are available
+ */
+ public boolean areRessourcesAvailable(final ResType resType, final String... resIdsTextual) {
+ for (String name : resIdsTextual) {
+ if (getResId(resType, name) == 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Convert an int color to a hex string. Optionally including alpha value.
+ *
+ * @param intColor The color coded in int
+ * @param withAlpha Optional; Set first bool parameter to true to also include alpha value
+ */
+ public String colorToHexString(int intColor, boolean... withAlpha) {
+ boolean a = withAlpha != null && withAlpha.length >= 1 && withAlpha[0];
+ return String.format(a ? "#%08X" : "#%06X", (a ? 0xFFFFFFFF : 0xFFFFFF) & intColor);
+ }
+
+ public String getAppVersionName() {
+ try {
+ PackageManager manager = _context.getPackageManager();
+ PackageInfo info = manager.getPackageInfo(_context.getPackageName(), 0);
+ return info.versionName;
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ return "?";
+ }
+ }
+
+ /**
+ * Send a {@link Intent#ACTION_VIEW} Intent with given paramter
+ * If the parameter is an string a browser will get triggered
+ */
+ public void openWebpageInExternalBrowser(final String url) {
+ Uri uri = Uri.parse(url);
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+ try {
+ _context.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Get field from ${applicationId}.BuildConfig
+ * May be helpful in libraries, where a access to
+ * BuildConfig would only get values of the library
+ * rather than the app ones. It awaits a string resource
+ * of the package set in manifest (root element).
+ * Falls back to applicationId of the app which may differ from manifest.
+ */
+ public Object getBuildConfigValue(String fieldName) {
+ String pkg = rstr("manifest_package_id");
+ pkg = (pkg != null ? pkg : _context.getPackageName()) + ".BuildConfig";
+ try {
+ Class> c = Class.forName(pkg);
+ return c.getField(fieldName).get(null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Get a BuildConfig bool value
+ */
+ public Boolean bcbool(String fieldName, Boolean defaultValue) {
+ Object field = getBuildConfigValue(fieldName);
+ if (field != null && field instanceof Boolean) {
+ return (Boolean) field;
+ }
+ return defaultValue;
+ }
+
+ /**
+ * Get a BuildConfig string value
+ */
+ public String bcstr(String fieldName, String defaultValue) {
+ Object field = getBuildConfigValue(fieldName);
+ if (field != null && field instanceof String) {
+ return (String) field;
+ }
+ return defaultValue;
+ }
+
+ /**
+ * Check if this is a gplay build (requires BuildConfig field)
+ */
+ public boolean isGooglePlayBuild() {
+ return bcbool("IS_GPLAY_BUILD", true);
+ }
+
+ /**
+ * Check if this is a foss build (requires BuildConfig field)
+ */
+ public boolean isFossBuild() {
+ return bcbool("IS_FOSS_BUILD", false);
+ }
+
+ /**
+ * Request a bitcoin donation with given details.
+ * All parameters are awaited as string resource ids
+ */
+ public void showDonateBitcoinRequest(@StringRes final int srBitcoinId, @StringRes final int srBitcoinAmount, @StringRes final int srBitcoinMessage, @StringRes final int srAlternativeDonateUrl) {
+ if (!isGooglePlayBuild()) {
+ String btcUri = String.format("bitcoin:%s?amount=%s&label=%s&message=%s",
+ rstr(srBitcoinId), rstr(srBitcoinAmount),
+ rstr(srBitcoinMessage), rstr(srBitcoinMessage));
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(btcUri));
+ intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+ try {
+ _context.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ openWebpageInExternalBrowser(rstr(srAlternativeDonateUrl));
+ }
+ }
+ }
+
+ public String readTextfileFromRawRes(@RawRes int rawResId, String linePrefix, String linePostfix) {
+ StringBuilder sb = new StringBuilder();
+ BufferedReader br = null;
+ String line;
+
+ linePrefix = linePrefix == null ? "" : linePrefix;
+ linePostfix = linePostfix == null ? "" : linePostfix;
+
+ try {
+ br = new BufferedReader(new InputStreamReader(_context.getResources().openRawResource(rawResId)));
+ while ((line = br.readLine()) != null) {
+ sb.append(linePrefix);
+ sb.append(line);
+ sb.append(linePostfix);
+ sb.append("\n");
+ }
+ } catch (Exception ignored) {
+ } finally {
+ if (br != null) {
+ try {
+ br.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Get internet connection state - the permission ACCESS_NETWORK_STATE is required
+ *
+ * @return True if internet connection available
+ */
+ public boolean isConnectedToInternet() {
+ try {
+ ConnectivityManager con = (ConnectivityManager) _context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ @SuppressLint("MissingPermission") NetworkInfo activeNetInfo =
+ con == null ? null : con.getActiveNetworkInfo();
+ return activeNetInfo != null && activeNetInfo.isConnectedOrConnecting();
+ } catch (Exception ignored) {
+ throw new RuntimeException("Error: Developer forgot to declare a permission");
+ }
+ }
+
+ /**
+ * Check if app with given {@code packageName} is installed
+ */
+ public boolean isAppInstalled(String packageName) {
+ PackageManager pm = _context.getApplicationContext().getPackageManager();
+ try {
+ pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
+ return true;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Restart the current app. Supply the class to start on startup
+ */
+ public void restartApp(Class classToStart) {
+ Intent inte = new Intent(_context, classToStart);
+ PendingIntent inteP = PendingIntent.getActivity(_context, 555, inte, PendingIntent.FLAG_CANCEL_CURRENT);
+ AlarmManager mgr = (AlarmManager) _context.getSystemService(Context.ALARM_SERVICE);
+ if (mgr != null) {
+ mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, inteP);
+ } else {
+ inte.addFlags(FLAG_ACTIVITY_NEW_TASK);
+ _context.startActivity(inte);
+ }
+ Runtime.getRuntime().exit(0);
+ }
+
+ /**
+ * Load a markdown file from a {@link RawRes}, prepend each line with {@code prepend} text
+ * and convert markdown to html using {@link SimpleMarkdownParser}
+ */
+ public String loadMarkdownForTextViewFromRaw(@RawRes int rawMdFile, String prepend) {
+ try {
+ return new SimpleMarkdownParser()
+ .parse(_context.getResources().openRawResource(rawMdFile),
+ prepend, SimpleMarkdownParser.FILTER_ANDROID_TEXTVIEW)
+ .replaceColor("#000001", rcolor(getResId(ResType.COLOR, "accent")))
+ .removeMultiNewlines().replaceBulletCharacter("*").getHtml();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return "";
+ }
+ }
+
+ /**
+ * Load html into a {@link Spanned} object and set the
+ * {@link TextView}'s text using {@link TextView#setText(CharSequence)}
+ */
+ public void setHtmlToTextView(TextView textView, String html) {
+ textView.setMovementMethod(LinkMovementMethod.getInstance());
+ textView.setText(new SpannableString(htmlToSpanned(html)));
+ }
+
+ /**
+ * Estimate this device's screen diagonal size in inches
+ */
+ public double getEstimatedScreenSizeInches() {
+ DisplayMetrics dm = _context.getResources().getDisplayMetrics();
+
+ double calc = dm.density * 160d;
+ double x = Math.pow(dm.widthPixels / calc, 2);
+ double y = Math.pow(dm.heightPixels / calc, 2);
+ calc = Math.sqrt(x + y) * 1.16; // 1.16 = est. Nav/Statusbar
+ return Math.min(12, Math.max(4, calc));
+ }
+
+ /**
+ * Check if the device is currently in portrait orientation
+ */
+ public boolean isInPortraitMode() {
+ return _context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
+ }
+
+ /**
+ * Get an {@link Locale} out of a android language code
+ * The {@code androidLC} may be in any of the forms: de, en, de-rAt
+ */
+ public Locale getLocaleByAndroidCode(String androidLC) {
+ if (!TextUtils.isEmpty(androidLC)) {
+ return androidLC.contains("-r")
+ ? new Locale(androidLC.substring(0, 2), androidLC.substring(4, 6)) // de-rAt
+ : new Locale(androidLC); // de
+ }
+ return Resources.getSystem().getConfiguration().locale;
+ }
+
+ /**
+ * Set the apps language
+ * {@code androidLC} may be in any of the forms: en, de, de-rAt
+ * If given an empty string, the default (system) locale gets loaded
+ */
+ public void setAppLanguage(String androidLC) {
+ Locale locale = getLocaleByAndroidCode(androidLC);
+ Configuration config = _context.getResources().getConfiguration();
+ config.locale = (locale != null && !androidLC.isEmpty())
+ ? locale : Resources.getSystem().getConfiguration().locale;
+ _context.getResources().updateConfiguration(config, null);
+ }
+
+ /**
+ * Try to guess if the color on top of the given {@code colorOnBottomInt}
+ * should be light or dark. Returns true if top color should be light
+ */
+ public boolean shouldColorOnTopBeLight(@ColorInt int colorOnBottomInt) {
+ return 186 > (((0.299 * Color.red(colorOnBottomInt))
+ + ((0.587 * Color.green(colorOnBottomInt))
+ + (0.114 * Color.blue(colorOnBottomInt)))));
+ }
+
+ /**
+ * Convert a html string to an android {@link Spanned} object
+ */
+ public Spanned htmlToSpanned(String html) {
+ Spanned result;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
+ result = Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY);
+ } else {
+ result = Html.fromHtml(html);
+ }
+ return result;
+ }
+
+ /**
+ * Convert pixel unit do android dp unit
+ */
+ public float convertPxToDp(final float px) {
+ return px / _context.getResources().getDisplayMetrics().density;
+ }
+
+ /**
+ * Convert android dp unit to pixel unit
+ */
+ public float convertDpToPx(final float dp) {
+ return dp * _context.getResources().getDisplayMetrics().density;
+ }
+
+ /**
+ * Request the givens paths to be scanned by MediaScanner
+ *
+ * @param files Files and folders to scan
+ */
+ public void mediaScannerScanFile(File... files) {
+ if (android.os.Build.VERSION.SDK_INT > 19) {
+ String[] paths = new String[files.length];
+ for (int i = 0; i < files.length; i++) {
+ paths[i] = files[i].getAbsolutePath();
+ }
+ MediaScannerConnection.scanFile(_context, paths, null, null);
+ } else {
+ for (File file : files) {
+ _context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
+ }
+ }
+ }
+
+ /**
+ * Load an image into a {@link ImageView} and apply a color filter
+ */
+ public static void setDrawableWithColorToImageView(ImageView imageView, @DrawableRes int drawableResId, @ColorRes int colorResId) {
+ imageView.setImageResource(drawableResId);
+ imageView.setColorFilter(ContextCompat.getColor(imageView.getContext(), colorResId));
+ }
+
+ /**
+ * Get a {@link Bitmap} out of a {@link Drawable}
+ */
+ public Bitmap drawableToBitmap(Drawable drawable) {
+ Bitmap bitmap = null;
+ if (drawable instanceof VectorDrawableCompat
+ || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && drawable instanceof VectorDrawable)
+ || ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && drawable instanceof AdaptiveIconDrawable))) {
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ drawable = (DrawableCompat.wrap(drawable)).mutate();
+ }
+
+ bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+ } else if (drawable instanceof BitmapDrawable) {
+ bitmap = ((BitmapDrawable) drawable).getBitmap();
+ }
+ return bitmap;
+ }
+
+ /**
+ * Get a {@link Bitmap} out of a {@link DrawableRes}
+ */
+ public Bitmap drawableToBitmap(@DrawableRes int drawableId) {
+ return drawableToBitmap(ContextCompat.getDrawable(_context, drawableId));
+ }
+
+ /**
+ * Get a {@link Bitmap} from a given {@code imagePath} on the filesystem
+ * Specifying a {@code maxDimen} is also possible and a value below 2000
+ * is recommended, otherwise a {@link OutOfMemoryError} may occur
+ */
+ public Bitmap loadImageFromFilesystem(File imagePath, int maxDimen) {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(imagePath.getAbsolutePath(), options);
+ options.inSampleSize = calculateInSampleSize(options, maxDimen);
+ options.inJustDecodeBounds = false;
+ return BitmapFactory.decodeFile(imagePath.getAbsolutePath(), options);
+ }
+
+ /**
+ * Calculates the scaling factor so the bitmap is maximal as big as the maxDimen
+ *
+ * @param options Bitmap-options that contain the current dimensions of the bitmap
+ * @param maxDimen Max size of the Bitmap (width or height)
+ * @return the scaling factor that needs to be applied to the bitmap
+ */
+ public int calculateInSampleSize(BitmapFactory.Options options, int maxDimen) {
+ // Raw height and width of image
+ int height = options.outHeight;
+ int width = options.outWidth;
+ int inSampleSize = 1;
+
+ if (Math.max(height, width) > maxDimen) {
+ inSampleSize = Math.round(1f * Math.max(height, width) / maxDimen);
+ }
+ return inSampleSize;
+ }
+
+ /**
+ * Scale the bitmap so both dimensions are lower or equal to {@code maxDimen}
+ * This keeps the aspect ratio
+ */
+ public Bitmap scaleBitmap(Bitmap bitmap, int maxDimen) {
+ int picSize = Math.min(bitmap.getHeight(), bitmap.getWidth());
+ float scale = 1.f * maxDimen / picSize;
+ Matrix matrix = new Matrix();
+ matrix.postScale(scale, scale);
+ return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
+ }
+
+ /**
+ * Write the given {@link Bitmap} to {@code imageFile}, in {@link CompressFormat#JPEG} format
+ */
+ public boolean writeImageToFileJpeg(File imageFile, Bitmap image) {
+ return writeImageToFile(imageFile, image, Bitmap.CompressFormat.JPEG, 95);
+ }
+
+ /**
+ * Write the given {@link Bitmap} to filesystem
+ *
+ * @param targetFile The file to be written in
+ * @param image The image as android {@link Bitmap}
+ * @param format One format of {@link CompressFormat}, null will determine based on filename
+ * @param quality Quality level, defaults to 95
+ * @return True if writing was successful
+ */
+ public boolean writeImageToFile(File targetFile, Bitmap image, CompressFormat format, Integer quality) {
+ File folder = new File(targetFile.getParent());
+ if (quality == null || quality < 0 || quality > 100) {
+ quality = 95;
+ }
+ if (format == null) {
+ format = CompressFormat.JPEG;
+ String lc = targetFile.getAbsolutePath().toLowerCase(Locale.ROOT);
+ if (lc.endsWith(".png")) {
+ format = CompressFormat.PNG;
+ }
+ if (lc.endsWith(".webp")) {
+ format = CompressFormat.WEBP;
+ }
+ }
+ if (folder.exists() || folder.mkdirs()) {
+ FileOutputStream stream = null;
+ try {
+ stream = new FileOutputStream(targetFile); // overwrites this image every time
+ image.compress(format, quality, stream);
+ return true;
+ } catch (FileNotFoundException ignored) {
+ } finally {
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException ignored) {
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Draw text in the center of the given {@link DrawableRes}
+ * This may be useful for e.g. badge counts
+ */
+ public Bitmap drawTextOnDrawable(@DrawableRes int drawableRes, String text, int textSize) {
+ Resources resources = _context.getResources();
+ float scale = resources.getDisplayMetrics().density;
+ Bitmap bitmap = drawableToBitmap(drawableRes);
+
+ bitmap = bitmap.copy(bitmap.getConfig(), true);
+ Canvas canvas = new Canvas(bitmap);
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ paint.setColor(Color.rgb(61, 61, 61));
+ paint.setTextSize((int) (textSize * scale));
+ paint.setShadowLayer(1f, 0f, 1f, Color.WHITE);
+
+ Rect bounds = new Rect();
+ paint.getTextBounds(text, 0, text.length(), bounds);
+ int x = (bitmap.getWidth() - bounds.width()) / 2;
+ int y = (bitmap.getHeight() + bounds.height()) / 2;
+ canvas.drawText(text, x, y, paint);
+
+ return bitmap;
+ }
+
+ /**
+ * Try to tint all {@link Menu}s {@link MenuItem}s with given color
+ */
+ @SuppressWarnings("ConstantConditions")
+ public void tintMenuItems(Menu menu, boolean recurse, @ColorInt int iconColor) {
+ for (int i = 0; i < menu.size(); i++) {
+ MenuItem item = menu.getItem(i);
+ tintDrawable(item.getIcon(), iconColor);
+ if (item.hasSubMenu() && recurse) {
+ tintMenuItems(item.getSubMenu(), recurse, iconColor);
+ }
+ }
+ }
+
+ /**
+ * Loads {@link Drawable} by given {@link DrawableRes} and applies a color
+ */
+ public Drawable tintDrawable(@DrawableRes int drawableRes, @ColorInt int color) {
+ return tintDrawable(rdrawable(drawableRes), color);
+ }
+
+ /**
+ * Tint a {@link Drawable} with given {@code color}
+ */
+ public Drawable tintDrawable(@Nullable Drawable drawable, @ColorInt int color) {
+ if (drawable != null) {
+ drawable = DrawableCompat.wrap(drawable);
+ DrawableCompat.setTint(drawable.mutate(), color);
+ }
+ return drawable;
+ }
+
+ /**
+ * Try to make icons in Toolbar/ActionBars SubMenus visible
+ * This may not work on some devices and it maybe won't work on future android updates
+ */
+ public void setSubMenuIconsVisiblity(Menu menu, boolean visible) {
+ if (menu.getClass().getSimpleName().equals("MenuBuilder")) {
+ try {
+ @SuppressLint("PrivateApi") Method m = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE);
+ m.setAccessible(true);
+ m.invoke(menu, visible);
+ } catch (Exception ignored) {
+ Log.d(getClass().getName(), "Error: 'setSubMenuIconsVisiblity' not supported on this device");
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/net/gsantner/opoc/util/DownloadTask.java b/app/src/main/java/net/gsantner/opoc/util/DownloadTask.java
new file mode 100644
index 000000000..aec5b868e
--- /dev/null
+++ b/app/src/main/java/net/gsantner/opoc/util/DownloadTask.java
@@ -0,0 +1,57 @@
+/*
+ This file is part of the dandelion*.
+
+ dandelion* 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.
+
+ dandelion* 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 the dandelion*.
+
+ If not, see .
+ */
+package net.gsantner.opoc.util;
+
+import android.os.AsyncTask;
+import android.support.annotation.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import info.guardianproject.netcipher.NetCipher;
+
+public class DownloadTask extends AsyncTask {
+ private File _targetFile;
+ private Callback.a2 _callback;
+
+ public DownloadTask(File targetFile, @Nullable Callback.a2 callback) {
+ _targetFile = targetFile;
+ _callback = callback;
+ }
+
+ protected Boolean doInBackground(String... urls) {
+ if (urls != null && urls.length > 0 && urls[0] != null) {
+ try {
+ HttpsURLConnection connection = NetCipher.getHttpsURLConnection(urls[0]);
+ return NetworkUtils.downloadFile(null, _targetFile, connection, null);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return false;
+ }
+
+ protected void onPostExecute(Boolean result) {
+ if (_callback != null) {
+ _callback.callback(result, _targetFile);
+ }
+ }
+}
diff --git a/app/src/main/java/net/gsantner/opoc/util/FileUtils.java b/app/src/main/java/net/gsantner/opoc/util/FileUtils.java
new file mode 100644
index 000000000..13cb66d5a
--- /dev/null
+++ b/app/src/main/java/net/gsantner/opoc/util/FileUtils.java
@@ -0,0 +1,340 @@
+/*#######################################################
+ *
+ * Maintained by Gregor Santner, 2017-
+ * https://gsantner.net/
+ *
+ * License: Apache 2.0
+ * https://github.com/gsantner/opoc/#licensing
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+#########################################################*/
+package net.gsantner.opoc.util;
+
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.UUID;
+import java.util.regex.Pattern;
+
+@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "SpellCheckingInspection", "deprecation"})
+public class FileUtils {
+ // Used on methods like copyFile(src, dst)
+ private static final int BUFFER_SIZE = 4096;
+
+ public static String readTextFile(final File file) {
+ try {
+ return readCloseTextStream(new FileInputStream(file));
+ } catch (FileNotFoundException e) {
+ System.err.println("readTextFile: File " + file + " not found.");
+ }
+
+ return "";
+ }
+
+ public static String readCloseTextStream(final InputStream stream) {
+ return readCloseTextStream(stream, true).get(0);
+ }
+
+ public static List readCloseTextStream(final InputStream stream, boolean concatToOneString) {
+ final ArrayList lines = new ArrayList<>();
+ BufferedReader reader = null;
+ String line = "";
+ try {
+ StringBuilder sb = new StringBuilder();
+ reader = new BufferedReader(new InputStreamReader(stream));
+
+ while ((line = reader.readLine()) != null) {
+ if (concatToOneString) {
+ sb.append(line).append('\n');
+ } else {
+ lines.add(line);
+ }
+ }
+ line = sb.toString();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ if (concatToOneString) {
+ lines.clear();
+ lines.add(line);
+ }
+ return lines;
+ }
+
+ public static byte[] readBinaryFile(final File file) {
+ try {
+ return readCloseBinaryStream(new FileInputStream(file), (int) file.length());
+ } catch (FileNotFoundException e) {
+ System.err.println("readBinaryFile: File " + file + " not found.");
+ }
+
+ return new byte[0];
+ }
+
+ public static byte[] readCloseBinaryStream(final InputStream stream, int byteCount) {
+ final ArrayList lines = new ArrayList<>();
+ BufferedInputStream reader = null;
+ byte[] buf = new byte[byteCount];
+ int totalBytesRead = 0;
+ try {
+ reader = new BufferedInputStream(stream);
+ while (totalBytesRead < byteCount) {
+ int bytesRead = reader.read(buf, totalBytesRead, byteCount - totalBytesRead);
+ if (bytesRead > 0) {
+ totalBytesRead = totalBytesRead + bytesRead;
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return buf;
+ }
+
+ // Read binary stream (of unknown conf size)
+ public static byte[] readCloseBinaryStream(final InputStream stream) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int read;
+ while ((read = stream.read(buffer)) != -1) {
+ baos.write(buffer, 0, read);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return baos.toByteArray();
+ }
+
+ public static boolean writeFile(final File file, byte[] data) {
+ try {
+ OutputStream output = null;
+ try {
+ output = new BufferedOutputStream(new FileOutputStream(file));
+ output.write(data);
+ output.flush();
+ return true;
+ } finally {
+ if (output != null) {
+ output.close();
+ }
+ }
+ } catch (Exception ex) {
+ return false;
+ }
+ }
+
+ public static boolean writeFile(final File file, final String content) {
+ BufferedWriter writer = null;
+ try {
+ if (!file.getParentFile().isDirectory() && !file.getParentFile().mkdirs())
+ return false;
+
+ writer = new BufferedWriter(new FileWriter(file));
+ writer.write(content);
+ writer.flush();
+ return true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public static boolean copyFile(final File src, final File dst) {
+ // Just touch file if src is empty
+ if (src.length() == 0) {
+ return touch(dst);
+ }
+
+ InputStream is = null;
+ FileOutputStream os = null;
+ try {
+ try {
+ is = new FileInputStream(src);
+ os = new FileOutputStream(dst);
+ byte[] buf = new byte[BUFFER_SIZE];
+ int len;
+ while ((len = is.read(buf)) > 0) {
+ os.write(buf, 0, len);
+ }
+ return true;
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ if (os != null) {
+ os.close();
+ }
+ }
+ } catch (IOException ex) {
+ return false;
+ }
+ }
+
+ // Returns -1 if the file did not contain any of the needles, otherwise,
+ // the index of which needle was found in the contents of the file.
+ //
+ // Needless MUST be in lower-case.
+ public static int fileContains(File file, String... needles) {
+ try {
+ FileInputStream in = new FileInputStream(file);
+
+ int i;
+ String line;
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ while ((line = reader.readLine()) != null) {
+ for (i = 0; i != needles.length; ++i)
+ if (line.toLowerCase(Locale.ROOT).contains(needles[i])) {
+ return i;
+ }
+ }
+
+ in.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return -1;
+ }
+
+ public static boolean deleteRecursive(final File file) {
+ boolean ok = true;
+ if (file.exists()) {
+ if (file.isDirectory()) {
+ for (File child : file.listFiles())
+ ok &= deleteRecursive(child);
+ }
+ ok &= file.delete();
+ }
+ return ok;
+ }
+
+ // Example: Check if this is maybe a conf: (str, "jpg", "png", "jpeg")
+ public static boolean hasExtension(String str, String... extensions) {
+ String lc = str.toLowerCase(Locale.ROOT);
+ for (String extension : extensions) {
+ if (lc.endsWith("." + extension.toLowerCase(Locale.ROOT))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean renameFile(File srcFile, File destFile) {
+ if (srcFile.getAbsolutePath().equals(destFile.getAbsolutePath())) {
+ return false;
+ }
+
+ // renameTo will fail in case of case-changed filename in same dir.Even on case-sensitive FS!!!
+ if (srcFile.getParent().equals(destFile.getParent()) && srcFile.getName().toLowerCase(Locale.getDefault()).equals(destFile.getName().toLowerCase(Locale.getDefault()))) {
+ File tmpFile = new File(destFile.getParent(), UUID.randomUUID().getLeastSignificantBits() + ".tmp");
+ if (!tmpFile.exists()) {
+ renameFile(srcFile, tmpFile);
+ srcFile = tmpFile;
+ }
+ }
+
+ if (!srcFile.renameTo(destFile)) {
+ if (copyFile(srcFile, destFile) && !srcFile.delete()) {
+ if (!destFile.delete()) {
+ return false;
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ public static boolean renameFileInSameFolder(File srcFile, String destFilename) {
+ return renameFile(srcFile, new File(srcFile.getParent(), destFilename));
+ }
+
+ public static boolean touch(File file) {
+ try {
+ if (!file.exists()) {
+ new FileOutputStream(file).close();
+ }
+ return file.setLastModified(System.currentTimeMillis());
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ // Get relative path to specified destination
+ public static String relativePath(File src, File dest) {
+ try {
+ String[] srcSplit = (src.isDirectory() ? src : src.getParentFile()).getCanonicalPath().split(Pattern.quote(File.separator));
+ String[] destSplit = dest.getCanonicalPath().split(Pattern.quote(File.separator));
+ StringBuilder sb = new StringBuilder();
+ int i = 0;
+
+ for (; i < destSplit.length && i < srcSplit.length; ++i) {
+ if (!destSplit[i].equals(srcSplit[i]))
+ break;
+ }
+ if (i != srcSplit.length) {
+ for (int iUpperDir = i; iUpperDir < srcSplit.length; ++iUpperDir) {
+ sb.append("..");
+ sb.append(File.separator);
+ }
+ }
+ for (; i < destSplit.length; ++i) {
+ sb.append(destSplit[i]);
+ sb.append(File.separator);
+ }
+ if (!dest.getPath().endsWith("/") && !dest.getPath().endsWith("\\")) {
+ sb.delete(sb.length() - File.separator.length(), sb.length());
+ }
+ return sb.toString();
+ } catch (IOException | NullPointerException exception) {
+ return null;
+ }
+ }
+}
diff --git a/app/src/main/java/net/gsantner/opoc/util/NetworkUtils.java b/app/src/main/java/net/gsantner/opoc/util/NetworkUtils.java
new file mode 100644
index 000000000..5212e5b72
--- /dev/null
+++ b/app/src/main/java/net/gsantner/opoc/util/NetworkUtils.java
@@ -0,0 +1,223 @@
+/*#######################################################
+ *
+ * Maintained by Gregor Santner, 2017-
+ * https://gsantner.net/
+ *
+ * License: Apache 2.0
+ * https://github.com/gsantner/opoc/#licensing
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+#########################################################*/
+package net.gsantner.opoc.util;
+
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Map;
+
+@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "SpellCheckingInspection", "deprecation"})
+public class NetworkUtils {
+ private static final String UTF8 = "UTF-8";
+ public static final String GET = "GET";
+ public static final String POST = "POST";
+ public static final String PATCH = "PATCH";
+
+ private final static int BUFFER_SIZE = 4096;
+
+ // Downloads a file from the give url to the output file
+ // Creates the file's parent directory if it doesn't exist
+ public static boolean downloadFile(final String url, final File out) {
+ return downloadFile(url, out, null);
+ }
+
+ public static boolean downloadFile(final String url, final File out, final Callback.a1 progressCallback) {
+ try {
+ return downloadFile(new URL(url), out, progressCallback);
+ } catch (MalformedURLException e) {
+ // Won't happen
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ public static boolean downloadFile(final URL url, final File outFile, final Callback.a1 progressCallback) {
+ return downloadFile(url, outFile, null, progressCallback);
+ }
+
+ public static boolean downloadFile(final URL url, final File outFile, HttpURLConnection connection, final Callback.a1 progressCallback) {
+ InputStream input = null;
+ OutputStream output = null;
+ try {
+ if (connection == null) {
+ connection = (HttpURLConnection) url.openConnection();
+ }
+ connection.connect();
+ input = connection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST
+ ? connection.getInputStream() : connection.getErrorStream();
+
+
+ if (!outFile.getParentFile().isDirectory())
+ if (!outFile.getParentFile().mkdirs())
+ return false;
+ output = new FileOutputStream(outFile);
+
+ int count;
+ int written = 0;
+ final float invLength = 1f / connection.getContentLength();
+
+ byte data[] = new byte[BUFFER_SIZE];
+ while ((count = input.read(data)) != -1) {
+ output.write(data, 0, count);
+ if (invLength != -1f && progressCallback != null) {
+ written += count;
+ progressCallback.callback(written * invLength);
+ }
+ }
+
+ return true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ } finally {
+ try {
+ if (output != null)
+ output.close();
+ if (input != null)
+ input.close();
+ } catch (IOException ignored) {
+ }
+ if (connection != null)
+ connection.disconnect();
+ }
+ }
+
+ // No parameters, method can be GET, POST, etc.
+ public static String performCall(final String url, final String method) {
+ try {
+ return performCall(new URL(url), method, "");
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+ public static String performCall(final String url, final String method, final String data) {
+ try {
+ return performCall(new URL(url), method, data);
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+ // URL encoded parameters
+ public static String performCall(final String url, final String method, final HashMap params) {
+ try {
+ return performCall(new URL(url), method, encodeQuery(params));
+ } catch (UnsupportedEncodingException | MalformedURLException e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+ // Defaults to POST
+ public static String performCall(final String url, final JSONObject json) {
+ return performCall(url, POST, json);
+ }
+
+ public static String performCall(final String url, final String method, final JSONObject json) {
+ try {
+ return performCall(new URL(url), method, json.toString());
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+ private static String performCall(final URL url, final String method, final String data) {
+ return performCall(url, method, data, null);
+ }
+
+ private static String performCall(final URL url, final String method, final String data, final HttpURLConnection existingConnection) {
+ try {
+ final HttpURLConnection connection = existingConnection != null
+ ? existingConnection : (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod(method);
+ connection.setDoInput(true);
+
+ if (data != null && !data.isEmpty()) {
+ connection.setDoOutput(true);
+ final OutputStream output = connection.getOutputStream();
+ output.write(data.getBytes(Charset.forName(UTF8)));
+ output.flush();
+ output.close();
+ }
+
+ InputStream input = connection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST
+ ? connection.getInputStream() : connection.getErrorStream();
+
+ return FileUtils.readCloseTextStream(connection.getInputStream());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+ private static String encodeQuery(final HashMap params) throws UnsupportedEncodingException {
+ final StringBuilder result = new StringBuilder();
+ boolean first = true;
+ for (Map.Entry entry : params.entrySet()) {
+ if (first) first = false;
+ else result.append("&");
+
+ result.append(URLEncoder.encode(entry.getKey(), UTF8));
+ result.append("=");
+ result.append(URLEncoder.encode(entry.getValue(), UTF8));
+ }
+
+ return result.toString();
+ }
+
+ public static HashMap getDataMap(final String query) {
+ final HashMap result = new HashMap<>();
+ final StringBuilder sb = new StringBuilder();
+ String name = "";
+
+ try {
+ for (int i = 0; i < query.length(); i++) {
+ char c = query.charAt(i);
+ switch (c) {
+ case '=':
+ name = URLDecoder.decode(sb.toString(), UTF8);
+ sb.setLength(0);
+ break;
+ case '&':
+ result.put(name, URLDecoder.decode(sb.toString(), UTF8));
+ sb.setLength(0);
+ break;
+ default:
+ sb.append(c);
+ break;
+ }
+ }
+ if (!name.isEmpty())
+ result.put(name, URLDecoder.decode(sb.toString(), UTF8));
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+
+ return result;
+ }
+}
diff --git a/app/src/main/java/net/gsantner/opoc/util/PermissionChecker.java b/app/src/main/java/net/gsantner/opoc/util/PermissionChecker.java
new file mode 100644
index 000000000..524c30651
--- /dev/null
+++ b/app/src/main/java/net/gsantner/opoc/util/PermissionChecker.java
@@ -0,0 +1,70 @@
+/*#######################################################
+ *
+ * Maintained by Gregor Santner, 2017-
+ * https://gsantner.net/
+ *
+ * License: Apache 2.0
+ * https://github.com/gsantner/opoc/#licensing
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+#########################################################*/
+package net.gsantner.opoc.util;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.pm.PackageManager;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
+
+import java.io.File;
+
+@SuppressWarnings({"unused", "WeakerAccess"})
+public class PermissionChecker {
+ private static final int CODE_PERMISSION_EXTERNAL_STORAGE = 4000;
+
+ private Activity _activity;
+
+ public PermissionChecker(Activity activity) {
+ _activity = activity;
+ }
+
+ public boolean doIfExtStoragePermissionGranted(String... optionalToastMessageForKnowingWhyNeeded) {
+ if (ContextCompat.checkSelfPermission(_activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+
+ if (optionalToastMessageForKnowingWhyNeeded != null && optionalToastMessageForKnowingWhyNeeded.length > 0 && optionalToastMessageForKnowingWhyNeeded[0] != null) {
+ new AlertDialog.Builder(_activity)
+ .setMessage(optionalToastMessageForKnowingWhyNeeded[0])
+ .setCancelable(false)
+ .setNegativeButton(android.R.string.no, null)
+ .setPositiveButton(android.R.string.yes, (dialog, which) -> {
+ if (android.os.Build.VERSION.SDK_INT >= 23) {
+ ActivityCompat.requestPermissions(_activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, CODE_PERMISSION_EXTERNAL_STORAGE);
+ }
+ })
+ .show();
+ return false;
+ }
+ ActivityCompat.requestPermissions(_activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, CODE_PERMISSION_EXTERNAL_STORAGE);
+ return false;
+ }
+ return true;
+ }
+
+ public boolean checkPermissionResult(int requestCode, String[] permissions, int[] grantResults) {
+ if (grantResults.length > 0) {
+ switch (requestCode) {
+ case CODE_PERMISSION_EXTERNAL_STORAGE: {
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean mkdirIfStoragePermissionGranted(File dir) {
+ return doIfExtStoragePermissionGranted() && (dir.exists() || dir.mkdirs());
+ }
+}
diff --git a/app/src/main/java/net/gsantner/opoc/util/ShareUtil.java b/app/src/main/java/net/gsantner/opoc/util/ShareUtil.java
new file mode 100644
index 000000000..21932cd6c
--- /dev/null
+++ b/app/src/main/java/net/gsantner/opoc/util/ShareUtil.java
@@ -0,0 +1,453 @@
+/*#######################################################
+ *
+ * Maintained by Gregor Santner, 2017-
+ * https://gsantner.net/
+ *
+ * License: Apache 2.0
+ * https://github.com/gsantner/opoc/#licensing
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+#########################################################*/
+package net.gsantner.opoc.util;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Handler;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintJob;
+import android.print.PrintManager;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.v4.content.FileProvider;
+import android.support.v4.content.pm.ShortcutInfoCompat;
+import android.support.v4.content.pm.ShortcutManagerCompat;
+import android.support.v4.graphics.drawable.IconCompat;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.webkit.WebView;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Random;
+
+/**
+ * A utility class to ease information sharing on Android
+ * Also allows to parse/fetch information out of shared information
+ */
+@SuppressWarnings({"UnusedReturnValue", "WeakerAccess", "SameParameterValue", "unused", "deprecation", "ConstantConditions", "ObsoleteSdkInt", "SpellCheckingInspection"})
+public class ShareUtil {
+ public final static String EXTRA_FILEPATH = "real_file_path_2";
+ public final static SimpleDateFormat SDF_RFC3339_ISH = new SimpleDateFormat("yyyy-MM-dd'T'HH-mm", Locale.getDefault());
+ public final static SimpleDateFormat SDF_SHORT = new SimpleDateFormat("yyMMdd-HHmm", Locale.getDefault());
+
+
+ protected Context _context;
+ protected String _fileProviderAuthority;
+ protected String _chooserTitle;
+
+ public ShareUtil(Context context) {
+ _context = context;
+ _chooserTitle = "➥";
+ }
+
+ public String getFileProviderAuthority() {
+ if (TextUtils.isEmpty(_fileProviderAuthority)) {
+ throw new RuntimeException("Error at ShareUtil.getFileProviderAuthority(): No FileProvider authority provided");
+ }
+ return _fileProviderAuthority;
+ }
+
+ public ShareUtil setFileProviderAuthority(String fileProviderAuthority) {
+ _fileProviderAuthority = fileProviderAuthority;
+ return this;
+ }
+
+
+ public ShareUtil setChooserTitle(String title) {
+ _chooserTitle = title;
+ return this;
+ }
+
+ /**
+ * Convert a {@link File} to an {@link Uri}
+ *
+ * @param file the file
+ * @return Uri for this file
+ */
+ public Uri getUriByFileProviderAuthority(File file) {
+ return FileProvider.getUriForFile(_context, getFileProviderAuthority(), file);
+ }
+
+ /**
+ * Allow to choose a handling app for given intent
+ *
+ * @param intent Thing to be shared
+ * @param chooserText The title text for the chooser, or null for default
+ */
+ public void showChooser(Intent intent, String chooserText) {
+ _context.startActivity(Intent.createChooser(intent,
+ chooserText != null ? chooserText : _chooserTitle));
+ }
+
+ /**
+ * Try to create a new desktop shortcut on the launcher. Add permissions:
+ *
+ *
+ *
+ * @param intent The intent to be invoked on tap
+ * @param iconRes Icon resource for the item
+ * @param title Title of the item
+ */
+ public void createLauncherDesktopShortcut(Intent intent, @DrawableRes int iconRes, String title) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ if (intent.getAction() == null) {
+ intent.setAction(Intent.ACTION_VIEW);
+ }
+
+ ShortcutInfoCompat shortcut = new ShortcutInfoCompat.Builder(_context, Long.toString(new Random().nextLong()))
+ .setIntent(intent)
+ .setIcon(IconCompat.createWithResource(_context, iconRes))
+ .setShortLabel(title)
+ .setLongLabel(title)
+ .build();
+ ShortcutManagerCompat.requestPinShortcut(_context, shortcut, null);
+ }
+
+ /**
+ * Try to create a new desktop shortcut on the launcher. This will not work on Api > 25. Add permissions:
+ *
+ *
+ *
+ * @param intent The intent to be invoked on tap
+ * @param iconRes Icon resource for the item
+ * @param title Title of the item
+ */
+ public void createLauncherDesktopShortcutLegacy(Intent intent, @DrawableRes int iconRes, String title) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ if (intent.getAction() == null) {
+ intent.setAction(Intent.ACTION_VIEW);
+ }
+
+ Intent creationIntent = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");
+ creationIntent.putExtra("duplicate", true);
+ creationIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);
+ creationIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
+ creationIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(_context, iconRes));
+ _context.sendBroadcast(creationIntent);
+ }
+
+ /**
+ * Share text with given mime-type
+ *
+ * @param text The text to share
+ * @param mimeType MimeType or null (uses text/plain)
+ */
+ public void shareText(String text, @Nullable String mimeType) {
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.putExtra(Intent.EXTRA_TEXT, text);
+ intent.setType(mimeType != null ? mimeType : "text/plain");
+ showChooser(intent, null);
+ }
+
+ /**
+ * Share the given file as stream with given mime-type
+ *
+ * @param file The file to share
+ * @param mimeType The files mime type
+ */
+ public void shareStream(File file, String mimeType) {
+ Uri fileUri = FileProvider.getUriForFile(_context, getFileProviderAuthority(), file);
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.putExtra(Intent.EXTRA_STREAM, fileUri);
+ intent.putExtra(EXTRA_FILEPATH, file.getAbsolutePath());
+ intent.setType(mimeType);
+ showChooser(intent, null);
+ }
+
+ /**
+ * Share the given bitmap with given format
+ *
+ * @param bitmap Image
+ * @param format A {@link Bitmap.CompressFormat}, supporting JPEG,PNG,WEBP
+ * @return if success, true
+ */
+ public boolean shareImage(Bitmap bitmap, Bitmap.CompressFormat format) {
+ return shareImage(bitmap, format, 95, "SharedImage");
+ }
+
+ /**
+ * Share the given bitmap with given format
+ *
+ * @param bitmap Image
+ * @param format A {@link Bitmap.CompressFormat}, supporting JPEG,PNG,WEBP
+ * @param imageName Filename without extension
+ * @param quality Quality of the exported image [0-100]
+ * @return if success, true
+ */
+ public boolean shareImage(Bitmap bitmap, Bitmap.CompressFormat format, int quality, String imageName) {
+ try {
+ String ext = format.name().toLowerCase();
+ File file = File.createTempFile(imageName, "." + ext.replace("jpeg", "jpg"), _context.getExternalCacheDir());
+ if (bitmap != null && new ContextUtils(_context).writeImageToFile(file, bitmap, format, quality)) {
+ shareStream(file, "image/" + ext);
+ return true;
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ /**
+ * Print a {@link WebView}'s contents, also allows to create a PDF
+ *
+ * @param webview WebView
+ * @param jobName Name of the job (affects PDF name too)
+ * @return {{@link PrintJob}} or null
+ */
+ @RequiresApi(api = Build.VERSION_CODES.KITKAT)
+ @SuppressWarnings("deprecation")
+ public PrintJob print(WebView webview, String jobName) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ PrintDocumentAdapter printAdapter;
+ PrintManager printManager = (PrintManager) webview.getContext().getSystemService(Context.PRINT_SERVICE);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ printAdapter = webview.createPrintDocumentAdapter(jobName);
+ } else {
+ printAdapter = webview.createPrintDocumentAdapter();
+ }
+ if (printManager != null) {
+ return printManager.print(jobName, printAdapter, new PrintAttributes.Builder().build());
+ }
+ } else {
+ Log.e(getClass().getName(), "ERROR: Method called on too low Android API version");
+ }
+ return null;
+ }
+
+
+ /**
+ * See {@link #print(WebView, String) print method}
+ */
+ @RequiresApi(api = Build.VERSION_CODES.KITKAT)
+ @SuppressWarnings("deprecation")
+ public PrintJob createPdf(WebView webview, String jobName) {
+ return print(webview, jobName);
+ }
+
+
+ /**
+ * Create a picture out of {@link WebView}'s whole content
+ *
+ * @param webView The WebView to get contents from
+ * @return A {@link Bitmap} or null
+ */
+ @Nullable
+ public static Bitmap getBitmapFromWebView(WebView webView) {
+ try {
+ //Measure WebView's content
+ int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+ webView.measure(widthMeasureSpec, heightMeasureSpec);
+ webView.layout(0, 0, webView.getMeasuredWidth(), webView.getMeasuredHeight());
+
+ //Build drawing cache and store its size
+ webView.buildDrawingCache();
+ int measuredWidth = webView.getMeasuredWidth();
+ int measuredHeight = webView.getMeasuredHeight();
+
+ //Creates the bitmap and draw WebView's content on in
+ Bitmap bitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ canvas.drawBitmap(bitmap, 0, bitmap.getHeight(), new Paint());
+
+ webView.draw(canvas);
+ webView.destroyDrawingCache();
+
+ return bitmap;
+ } catch (Exception | OutOfMemoryError e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+
+ /***
+ * Replace (primary) clipboard contents with given {@code text}
+ * @param text Text to be set
+ */
+ public boolean setClipboard(CharSequence text) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+ android.text.ClipboardManager cm = ((android.text.ClipboardManager) _context.getSystemService(Context.CLIPBOARD_SERVICE));
+ if (cm != null) {
+ cm.setText(text);
+ return true;
+ }
+ } else {
+ android.content.ClipboardManager cm = ((android.content.ClipboardManager) _context.getSystemService(Context.CLIPBOARD_SERVICE));
+ if (cm != null) {
+ ClipData clip = ClipData.newPlainText(_context.getPackageName(), text);
+ cm.setPrimaryClip(clip);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get clipboard contents, very failsafe and compat to older android versions
+ */
+ public List getClipboard() {
+ List clipper = new ArrayList<>();
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+ android.text.ClipboardManager cm = ((android.text.ClipboardManager) _context.getSystemService(Context.CLIPBOARD_SERVICE));
+ if (cm != null && !TextUtils.isEmpty(cm.getText())) {
+ clipper.add(cm.getText().toString());
+ }
+ } else {
+ android.content.ClipboardManager cm = ((android.content.ClipboardManager) _context.getSystemService(Context.CLIPBOARD_SERVICE));
+ if (cm != null && cm.hasPrimaryClip()) {
+ ClipData data = cm.getPrimaryClip();
+ for (int i = 0; data != null && i < data.getItemCount() && i < data.getItemCount(); i++) {
+ ClipData.Item item = data.getItemAt(i);
+ if (item != null && !TextUtils.isEmpty(item.getText())) {
+ clipper.add(data.getItemAt(i).getText().toString());
+ }
+ }
+ }
+ }
+ return clipper;
+ }
+
+ /**
+ * Share given text on a hastebin compatible server
+ * (https://github.com/seejohnrun/haste-server)
+ * Permission needed: Internet
+ * Pastes will be deleted after 30 days without access
+ *
+ * @param text The text to paste
+ * @param callback Callback after paste try
+ * @param serverOrNothing Supply one or no hastebin server. If empty, the default gets taken
+ */
+ public void pasteOnHastebin(final String text, final Callback.a2 callback, String... serverOrNothing) {
+ final Handler handler = new Handler();
+ final String server = (serverOrNothing != null && serverOrNothing.length > 0 && serverOrNothing[0] != null)
+ ? serverOrNothing[0] : "https://hastebin.com";
+ new Thread() {
+ public void run() {
+ // Returns a simple result, handleable without json parser {"key":"feediyujiq"}
+ String ret = NetworkUtils.performCall(server + "/documents", NetworkUtils.POST, text);
+ final String key = (ret.length() > 15) ? ret.split("\"")[3] : "";
+ handler.post(() -> callback.callback(!key.isEmpty(), server + "/" + key));
+ }
+ }.start();
+ }
+
+ /**
+ * Draft an email with given data. Unknown data can be supplied as null.
+ * This will open a chooser with installed mail clients where the mail can be sent from
+ *
+ * @param subject Subject (top/title) text to be prefilled in the mail
+ * @param body Body (content) text to be prefilled in the mail
+ * @param to recipients to be prefilled in the mail
+ */
+ public void draftEmail(String subject, String body, String... to) {
+ Intent intent = new Intent(Intent.ACTION_SENDTO);
+ intent.setData(Uri.parse("mailto:"));
+ if (subject != null) {
+ intent.putExtra(Intent.EXTRA_SUBJECT, subject);
+ }
+ if (body != null) {
+ intent.putExtra(Intent.EXTRA_TEXT, body);
+ }
+ if (to != null && to.length > 0 && to[0] != null) {
+ intent.putExtra(Intent.EXTRA_EMAIL, to);
+ }
+ showChooser(intent, null);
+ }
+
+ /**
+ * Try to force extract a absolute filepath from an intent
+ *
+ * @param receivingIntent The intent from {@link Activity#getIntent()}
+ * @return A file or null if extraction did not succeed
+ */
+ public File extractFileFromIntent(Intent receivingIntent) {
+ String action = receivingIntent.getAction();
+ String type = receivingIntent.getType();
+ File tmpf;
+ String tmps;
+ String fileStr;
+
+ if ((Intent.ACTION_VIEW.equals(action) || Intent.ACTION_EDIT.equals(action))) {
+ // Markor, S.M.T FileManager
+ if (receivingIntent.hasExtra((tmps = EXTRA_FILEPATH))) {
+ return new File(receivingIntent.getStringExtra(tmps));
+ }
+
+ // Analyze data/Uri
+ Uri fileUri = receivingIntent.getData();
+ if (fileUri != null && (fileStr = fileUri.toString()) != null) {
+ // Uri contains file
+ if (fileStr.startsWith("file://")) {
+ return new File(fileUri.getPath());
+ }
+ if (fileStr.startsWith((tmps = "content://"))) {
+ fileStr = fileStr.substring(tmps.length());
+ String fileProvider = fileStr.substring(0, fileStr.indexOf("/"));
+ fileStr = fileStr.substring(fileProvider.length() + 1);
+
+ // Some file managers dont add leading slash
+ if (fileStr.startsWith("storage/")) {
+ fileStr = "/" + fileStr;
+ }
+ // Some do add some custom prefix
+ for (String prefix : new String[]{"file", "document", "root_files"}) {
+ if (fileStr.startsWith(prefix)) {
+ fileStr = fileStr.substring(prefix.length());
+ }
+ }
+ // Next/OwnCloud Fileprovider
+ for (String fp : new String[]{"org.nextcloud.files", "org.nextcloud.beta.files", "org.owncloud.files"}) {
+ if (fileProvider.equals(fp) && fileStr.startsWith(tmps = "external_files/")) {
+ return new File(Uri.decode("/storage/" + fileStr.substring(tmps.length())));
+ }
+ }
+ // AOSP File Manager/Documents
+ if (fileProvider.equals("com.android.externalstorage.documents") && fileStr.startsWith(tmps = "/primary%3A")) {
+ return new File(Uri.decode(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + fileStr.substring(tmps.length())));
+ }
+ // Mi File Explorer
+ if (fileProvider.equals("com.mi.android.globalFileexplorer.myprovider") && fileStr.startsWith(tmps = "external_files")) {
+ return new File(Uri.decode(Environment.getExternalStorageDirectory().getAbsolutePath() + fileStr.substring(tmps.length())));
+ }
+ // URI Encoded paths with full path after content://package/
+ if (fileStr.startsWith("/") || fileStr.startsWith("%2F")) {
+ tmpf = new File(Uri.decode(fileStr));
+ if (tmpf.exists()) {
+ return tmpf;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/app/src/main/res/drawable-anydpi-v26/ic_launcher.xml b/app/src/main/res/drawable-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..bbd3e0212
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/drawable-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..bbd3e0212
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-anydpi-v26/ic_launcher_test.xml b/app/src/main/res/drawable-anydpi-v26/ic_launcher_test.xml
new file mode 100644
index 000000000..f1b3aaa2a
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi-v26/ic_launcher_test.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-anydpi-v26/ic_launcher_test_round.xml b/app/src/main/res/drawable-anydpi-v26/ic_launcher_test_round.xml
new file mode 100644
index 000000000..f1b3aaa2a
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi-v26/ic_launcher_test_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-hdpi/chrome_custom_tab__back.png b/app/src/main/res/drawable-hdpi/chrome_custom_tab__back.png
new file mode 100644
index 000000000..cd1972677
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/chrome_custom_tab__back.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_bell_outline_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_bell_outline_white_24dp.png
deleted file mode 100644
index b53dde90e..000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_bell_outline_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_bell_ring_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_bell_ring_white_24dp.png
deleted file mode 100644
index 9cc1f069e..000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_bell_ring_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_launcher.png b/app/src/main/res/drawable-hdpi/ic_launcher.png
index d212a834c..6f647c49b 100644
Binary files a/app/src/main/res/drawable-hdpi/ic_launcher.png and b/app/src/main/res/drawable-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_launcher_round.png b/app/src/main/res/drawable-hdpi/ic_launcher_round.png
new file mode 100644
index 000000000..6f647c49b
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_launcher_test.png b/app/src/main/res/drawable-hdpi/ic_launcher_test.png
new file mode 100644
index 000000000..c30acbbc1
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_launcher_test.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_launcher_test_round.png b/app/src/main/res/drawable-hdpi/ic_launcher_test_round.png
new file mode 100644
index 000000000..c30acbbc1
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_launcher_test_round.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_message_text_outline_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_message_text_outline_white_24dp.png
deleted file mode 100644
index 610aa1305..000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_message_text_outline_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_message_text_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_message_text_white_24dp.png
deleted file mode 100644
index a086bcbb4..000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_message_text_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_sync_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_sync_white_24dp.png
deleted file mode 100644
index 013d1c2a2..000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_sync_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-ldpi/ic_launcher.png b/app/src/main/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 000000000..8e89a6a91
Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_launcher.png differ
diff --git a/app/src/main/res/drawable-ldpi/ic_launcher_test.png b/app/src/main/res/drawable-ldpi/ic_launcher_test.png
new file mode 100644
index 000000000..a4c4ac0b2
Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_launcher_test.png differ
diff --git a/app/src/main/res/drawable-mdpi/chrome_custom_tab__back.png b/app/src/main/res/drawable-mdpi/chrome_custom_tab__back.png
new file mode 100644
index 000000000..4ef72eec9
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/chrome_custom_tab__back.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_bell_outline_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_bell_outline_white_24dp.png
deleted file mode 100644
index 0d4b0a1d3..000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_bell_outline_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_bell_ring_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_bell_ring_white_24dp.png
deleted file mode 100644
index 56001f7b9..000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_bell_ring_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_launcher.png b/app/src/main/res/drawable-mdpi/ic_launcher.png
index d212a834c..a9db7c164 100644
Binary files a/app/src/main/res/drawable-mdpi/ic_launcher.png and b/app/src/main/res/drawable-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_launcher_round.png b/app/src/main/res/drawable-mdpi/ic_launcher_round.png
new file mode 100644
index 000000000..a9db7c164
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_launcher_test.png b/app/src/main/res/drawable-mdpi/ic_launcher_test.png
new file mode 100644
index 000000000..f23d1091a
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_launcher_test.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_launcher_test_round.png b/app/src/main/res/drawable-mdpi/ic_launcher_test_round.png
new file mode 100644
index 000000000..f23d1091a
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_launcher_test_round.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_message_text_outline_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_message_text_outline_white_24dp.png
deleted file mode 100644
index 576b52747..000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_message_text_outline_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_message_text_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_message_text_white_24dp.png
deleted file mode 100644
index aee3a1ce4..000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_message_text_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_sync_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_sync_white_24dp.png
deleted file mode 100644
index 91770671c..000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_sync_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/chrome_custom_tab__back.png b/app/src/main/res/drawable-xhdpi/chrome_custom_tab__back.png
new file mode 100644
index 000000000..832f5a361
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/chrome_custom_tab__back.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_bell_outline_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_bell_outline_white_24dp.png
deleted file mode 100644
index 657e69d81..000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_bell_outline_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_bell_ring_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_bell_ring_white_24dp.png
deleted file mode 100644
index be2a70e04..000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_bell_ring_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher.png b/app/src/main/res/drawable-xhdpi/ic_launcher.png
index d212a834c..c97e5b53f 100644
Binary files a/app/src/main/res/drawable-xhdpi/ic_launcher.png and b/app/src/main/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher_round.png b/app/src/main/res/drawable-xhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..c97e5b53f
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher_test.png b/app/src/main/res/drawable-xhdpi/ic_launcher_test.png
new file mode 100644
index 000000000..43025584e
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_launcher_test.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher_test_round.png b/app/src/main/res/drawable-xhdpi/ic_launcher_test_round.png
new file mode 100644
index 000000000..43025584e
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_launcher_test_round.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_message_text_outline_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_message_text_outline_white_24dp.png
deleted file mode 100644
index d66b733af..000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_message_text_outline_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_message_text_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_message_text_white_24dp.png
deleted file mode 100644
index 5155a77a6..000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_message_text_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_sync_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_sync_white_24dp.png
deleted file mode 100644
index 01a969d5a..000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_sync_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/chrome_custom_tab__back.png b/app/src/main/res/drawable-xxhdpi/chrome_custom_tab__back.png
new file mode 100644
index 000000000..32a6d91ce
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/chrome_custom_tab__back.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_bell_outline_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_bell_outline_white_24dp.png
deleted file mode 100644
index 069bb3e5a..000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_bell_outline_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_bell_ring_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_bell_ring_white_24dp.png
deleted file mode 100644
index cdb2c24fe..000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_bell_ring_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxhdpi/ic_launcher.png
index d212a834c..11c9bd14e 100644
Binary files a/app/src/main/res/drawable-xxhdpi/ic_launcher.png and b/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher_round.png b/app/src/main/res/drawable-xxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..11c9bd14e
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher_test.png b/app/src/main/res/drawable-xxhdpi/ic_launcher_test.png
new file mode 100644
index 000000000..3993c4e4c
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_launcher_test.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher_test_round.png b/app/src/main/res/drawable-xxhdpi/ic_launcher_test_round.png
new file mode 100644
index 000000000..3993c4e4c
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_launcher_test_round.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_message_text_outline_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_message_text_outline_white_24dp.png
deleted file mode 100644
index 9fcb86829..000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_message_text_outline_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_message_text_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_message_text_white_24dp.png
deleted file mode 100644
index 1d3d19e7b..000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_message_text_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_sync_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_sync_white_24dp.png
deleted file mode 100644
index 9b0836da8..000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_sync_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxxhdpi/chrome_custom_tab__back.png b/app/src/main/res/drawable-xxxhdpi/chrome_custom_tab__back.png
new file mode 100644
index 000000000..e27034d67
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/chrome_custom_tab__back.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_bell_outline_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_bell_outline_white_24dp.png
deleted file mode 100644
index 2756e2ae5..000000000
Binary files a/app/src/main/res/drawable-xxxhdpi/ic_bell_outline_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_bell_ring_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_bell_ring_white_24dp.png
deleted file mode 100644
index 7ed1a2f27..000000000
Binary files a/app/src/main/res/drawable-xxxhdpi/ic_bell_ring_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxxhdpi/ic_launcher.png
index d212a834c..48f09459f 100644
Binary files a/app/src/main/res/drawable-xxxhdpi/ic_launcher.png and b/app/src/main/res/drawable-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_launcher_round.png b/app/src/main/res/drawable-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..48f09459f
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_launcher_test.png b/app/src/main/res/drawable-xxxhdpi/ic_launcher_test.png
new file mode 100644
index 000000000..72aabea6a
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_launcher_test.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_launcher_test_round.png b/app/src/main/res/drawable-xxxhdpi/ic_launcher_test_round.png
new file mode 100644
index 000000000..72aabea6a
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_launcher_test_round.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_message_text_outline_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_message_text_outline_white_24dp.png
deleted file mode 100644
index 26a104231..000000000
Binary files a/app/src/main/res/drawable-xxxhdpi/ic_message_text_outline_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_message_text_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_message_text_white_24dp.png
deleted file mode 100644
index 3fd53a5ad..000000000
Binary files a/app/src/main/res/drawable-xxxhdpi/ic_message_text_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_sync_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_sync_white_24dp.png
deleted file mode 100644
index 73cf22cac..000000000
Binary files a/app/src/main/res/drawable-xxxhdpi/ic_sync_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable/circle.xml b/app/src/main/res/drawable/circle.xml
new file mode 100644
index 000000000..eeadfaf09
--- /dev/null
+++ b/app/src/main/res/drawable/circle.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/fab_compose.png b/app/src/main/res/drawable/fab_compose.png
deleted file mode 100644
index 5ef731b4c..000000000
Binary files a/app/src/main/res/drawable/fab_compose.png and /dev/null differ
diff --git a/app/src/main/res/drawable/fab_label_background.xml b/app/src/main/res/drawable/fab_label_background.xml
deleted file mode 100644
index 83f55c971..000000000
--- a/app/src/main/res/drawable/fab_label_background.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/fab_search.png b/app/src/main/res/drawable/fab_search.png
deleted file mode 100644
index 78f32f75c..000000000
Binary files a/app/src/main/res/drawable/fab_search.png and /dev/null differ
diff --git a/app/src/main/res/drawable/fab_top.png b/app/src/main/res/drawable/fab_top.png
deleted file mode 100644
index 402daf58f..000000000
Binary files a/app/src/main/res/drawable/fab_top.png and /dev/null differ
diff --git a/app/src/main/res/drawable/header.jpg b/app/src/main/res/drawable/header.jpg
deleted file mode 100644
index 915e324ca..000000000
Binary files a/app/src/main/res/drawable/header.jpg and /dev/null differ
diff --git a/app/src/main/res/drawable/ic_arrow_back_black_24px.xml b/app/src/main/res/drawable/ic_arrow_back_black_24px.xml
new file mode 100644
index 000000000..9b1cf9759
--- /dev/null
+++ b/app/src/main/res/drawable/ic_arrow_back_black_24px.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_arrow_back_white_24px.xml b/app/src/main/res/drawable/ic_arrow_back_white_24px.xml
new file mode 100644
index 000000000..ded32bb2f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_arrow_back_white_24px.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_arrow_upward_white_48px.xml b/app/src/main/res/drawable/ic_arrow_upward_white_48px.xml
new file mode 100644
index 000000000..92bfb15df
--- /dev/null
+++ b/app/src/main/res/drawable/ic_arrow_upward_white_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_atsign.xml b/app/src/main/res/drawable/ic_atsign.xml
new file mode 100644
index 000000000..d68306184
--- /dev/null
+++ b/app/src/main/res/drawable/ic_atsign.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_bug_report_black_24px.xml b/app/src/main/res/drawable/ic_bug_report_black_24px.xml
new file mode 100644
index 000000000..4d83902b8
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bug_report_black_24px.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_cancel_black_48px.xml b/app/src/main/res/drawable/ic_cancel_black_48px.xml
new file mode 100644
index 000000000..789bf6f40
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cancel_black_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_clear_black_24px.xml b/app/src/main/res/drawable/ic_clear_black_24px.xml
new file mode 100644
index 000000000..178a01274
--- /dev/null
+++ b/app/src/main/res/drawable/ic_clear_black_24px.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_color_lens_black_24px.xml b/app/src/main/res/drawable/ic_color_lens_black_24px.xml
new file mode 100644
index 000000000..f75e2fbe3
--- /dev/null
+++ b/app/src/main/res/drawable/ic_color_lens_black_24px.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_comment_black_48px.xml b/app/src/main/res/drawable/ic_comment_black_48px.xml
new file mode 100644
index 000000000..040e3245e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_comment_black_48px.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_contacts_black_24px.xml b/app/src/main/res/drawable/ic_contacts_black_24px.xml
new file mode 100644
index 000000000..674b66b7c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_contacts_black_24px.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_dashboard_black_48px.xml b/app/src/main/res/drawable/ic_dashboard_black_48px.xml
new file mode 100644
index 000000000..e204deb5b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_dashboard_black_48px.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_favorite_black_48px.xml b/app/src/main/res/drawable/ic_favorite_black_48px.xml
new file mode 100644
index 000000000..e3cc36daf
--- /dev/null
+++ b/app/src/main/res/drawable/ic_favorite_black_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_file_download_black_24px.xml b/app/src/main/res/drawable/ic_file_download_black_24px.xml
new file mode 100644
index 000000000..102d49709
--- /dev/null
+++ b/app/src/main/res/drawable/ic_file_download_black_24px.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_format_align_left_black_48px.xml b/app/src/main/res/drawable/ic_format_align_left_black_48px.xml
new file mode 100644
index 000000000..f1e324c58
--- /dev/null
+++ b/app/src/main/res/drawable/ic_format_align_left_black_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_format_list_bulleted_black_24px.xml b/app/src/main/res/drawable/ic_format_list_bulleted_black_24px.xml
new file mode 100644
index 000000000..6cb93c699
--- /dev/null
+++ b/app/src/main/res/drawable/ic_format_list_bulleted_black_24px.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_format_size_black_24px.xml b/app/src/main/res/drawable/ic_format_size_black_24px.xml
new file mode 100644
index 000000000..184d252f4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_format_size_black_24px.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_group_black_48px.xml b/app/src/main/res/drawable/ic_group_black_48px.xml
new file mode 100644
index 000000000..c023a47c7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_group_black_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_history_black_48px.xml b/app/src/main/res/drawable/ic_history_black_48px.xml
new file mode 100644
index 000000000..b70c2146c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_history_black_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_info_black_48px.xml b/app/src/main/res/drawable/ic_info_black_48px.xml
new file mode 100644
index 000000000..a002fad8e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_info_black_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_language_black_48px.xml b/app/src/main/res/drawable/ic_language_black_48px.xml
new file mode 100644
index 000000000..295368470
--- /dev/null
+++ b/app/src/main/res/drawable/ic_language_black_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 000000000..10e5177cf
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 000000000..2610c60ce
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_test_background.xml b/app/src/main/res/drawable/ic_launcher_test_background.xml
new file mode 100644
index 000000000..e7de1de8a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_test_background.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_test_foreground.xml b/app/src/main/res/drawable/ic_launcher_test_foreground.xml
new file mode 100644
index 000000000..2610c60ce
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_test_foreground.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_local_offer_black_48px.xml b/app/src/main/res/drawable/ic_local_offer_black_48px.xml
new file mode 100644
index 000000000..68fdd1b79
--- /dev/null
+++ b/app/src/main/res/drawable/ic_local_offer_black_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_mail_white_48px.xml b/app/src/main/res/drawable/ic_mail_white_48px.xml
new file mode 100644
index 000000000..5382ce54a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_mail_white_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_mail_white_48px__layer.xml b/app/src/main/res/drawable/ic_mail_white_48px__layer.xml
new file mode 100644
index 000000000..983a98bf8
--- /dev/null
+++ b/app/src/main/res/drawable/ic_mail_white_48px__layer.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_mode_edit_black_48px.xml b/app/src/main/res/drawable/ic_mode_edit_black_48px.xml
new file mode 100644
index 000000000..7575f1a57
--- /dev/null
+++ b/app/src/main/res/drawable/ic_mode_edit_black_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_mode_edit_white_48px.xml b/app/src/main/res/drawable/ic_mode_edit_white_48px.xml
new file mode 100644
index 000000000..a7b5fa79c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_mode_edit_white_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_notifications_black_24px.xml b/app/src/main/res/drawable/ic_notifications_black_24px.xml
new file mode 100644
index 000000000..7009a6763
--- /dev/null
+++ b/app/src/main/res/drawable/ic_notifications_black_24px.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_notifications_white_48px.xml b/app/src/main/res/drawable/ic_notifications_white_48px.xml
new file mode 100644
index 000000000..173dea85c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_notifications_white_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_notifications_white_48px__layer.xml b/app/src/main/res/drawable/ic_notifications_white_48px__layer.xml
new file mode 100644
index 000000000..971baea64
--- /dev/null
+++ b/app/src/main/res/drawable/ic_notifications_white_48px__layer.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_open_in_new_black_24px.xml b/app/src/main/res/drawable/ic_open_in_new_black_24px.xml
new file mode 100644
index 000000000..6c4a8cb24
--- /dev/null
+++ b/app/src/main/res/drawable/ic_open_in_new_black_24px.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_people_black_24px.xml b/app/src/main/res/drawable/ic_people_black_24px.xml
new file mode 100644
index 000000000..4cfd86960
--- /dev/null
+++ b/app/src/main/res/drawable/ic_people_black_24px.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_person_add_black_48px.xml b/app/src/main/res/drawable/ic_person_add_black_48px.xml
new file mode 100644
index 000000000..5b1bbcc66
--- /dev/null
+++ b/app/src/main/res/drawable/ic_person_add_black_48px.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_person_black_24px.xml b/app/src/main/res/drawable/ic_person_black_24px.xml
new file mode 100644
index 000000000..ecd54ce83
--- /dev/null
+++ b/app/src/main/res/drawable/ic_person_black_24px.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_person_black_48px.xml b/app/src/main/res/drawable/ic_person_black_48px.xml
new file mode 100644
index 000000000..223f3f268
--- /dev/null
+++ b/app/src/main/res/drawable/ic_person_black_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_person_pin_black_48px.xml b/app/src/main/res/drawable/ic_person_pin_black_48px.xml
new file mode 100644
index 000000000..62a0a5d61
--- /dev/null
+++ b/app/src/main/res/drawable/ic_person_pin_black_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_public_black_48px.xml b/app/src/main/res/drawable/ic_public_black_48px.xml
new file mode 100644
index 000000000..97b21143e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_public_black_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_question_answer_black_48px.xml b/app/src/main/res/drawable/ic_question_answer_black_48px.xml
new file mode 100644
index 000000000..5216a9ae5
--- /dev/null
+++ b/app/src/main/res/drawable/ic_question_answer_black_48px.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_refresh_white_48px.xml b/app/src/main/res/drawable/ic_refresh_white_48px.xml
new file mode 100644
index 000000000..a3ff4f671
--- /dev/null
+++ b/app/src/main/res/drawable/ic_refresh_white_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_repeat_black_48px.xml b/app/src/main/res/drawable/ic_repeat_black_48px.xml
new file mode 100644
index 000000000..b39dca7a0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_repeat_black_48px.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_report_black_48px.xml b/app/src/main/res/drawable/ic_report_black_48px.xml
new file mode 100644
index 000000000..586f1aab9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_report_black_48px.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_screen_rotation_black_24px.xml b/app/src/main/res/drawable/ic_screen_rotation_black_24px.xml
new file mode 100644
index 000000000..b63eaae7a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_screen_rotation_black_24px.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_search_white_48px.xml b/app/src/main/res/drawable/ic_search_white_48px.xml
new file mode 100644
index 000000000..13b7e4841
--- /dev/null
+++ b/app/src/main/res/drawable/ic_search_white_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_settings_black_48px.xml b/app/src/main/res/drawable/ic_settings_black_48px.xml
new file mode 100644
index 000000000..8fbaa9a2f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_settings_black_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_share_white_48px.xml b/app/src/main/res/drawable/ic_share_white_48px.xml
new file mode 100644
index 000000000..1a53d3d94
--- /dev/null
+++ b/app/src/main/res/drawable/ic_share_white_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_star_border_black_48px.xml b/app/src/main/res/drawable/ic_star_border_black_48px.xml
new file mode 100644
index 000000000..3da8c75bd
--- /dev/null
+++ b/app/src/main/res/drawable/ic_star_border_black_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_star_filled_48px.xml b/app/src/main/res/drawable/ic_star_filled_48px.xml
new file mode 100644
index 000000000..2b1fe326f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_star_filled_48px.xml
@@ -0,0 +1,6 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_stream.xml b/app/src/main/res/drawable/ic_stream.xml
new file mode 100644
index 000000000..0050d519b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_stream.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_sync_white_48px.xml b/app/src/main/res/drawable/ic_sync_white_48px.xml
new file mode 100644
index 000000000..0868795df
--- /dev/null
+++ b/app/src/main/res/drawable/ic_sync_white_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_thumb_up_black_24px.xml b/app/src/main/res/drawable/ic_thumb_up_black_24px.xml
new file mode 100644
index 000000000..1fca08b39
--- /dev/null
+++ b/app/src/main/res/drawable/ic_thumb_up_black_24px.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_thumb_up_black_48px.xml b/app/src/main/res/drawable/ic_thumb_up_black_48px.xml
new file mode 100644
index 000000000..1fca08b39
--- /dev/null
+++ b/app/src/main/res/drawable/ic_thumb_up_black_48px.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_timeline_black_48px.xml b/app/src/main/res/drawable/ic_timeline_black_48px.xml
new file mode 100644
index 000000000..9184473cd
--- /dev/null
+++ b/app/src/main/res/drawable/ic_timeline_black_48px.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_touch_app_black_24px.xml b/app/src/main/res/drawable/ic_touch_app_black_24px.xml
new file mode 100644
index 000000000..dda5c85cc
--- /dev/null
+++ b/app/src/main/res/drawable/ic_touch_app_black_24px.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_visibility_black_24dp.xml b/app/src/main/res/drawable/ic_visibility_black_24dp.xml
new file mode 100644
index 000000000..e02f1d191
--- /dev/null
+++ b/app/src/main/res/drawable/ic_visibility_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_visibility_off_black_24px.xml b/app/src/main/res/drawable/ic_visibility_off_black_24px.xml
new file mode 100644
index 000000000..b3bb7460e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_visibility_off_black_24px.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_visibility_on_black_24px.xml b/app/src/main/res/drawable/ic_visibility_on_black_24px.xml
new file mode 100644
index 000000000..b4bf03e57
--- /dev/null
+++ b/app/src/main/res/drawable/ic_visibility_on_black_24px.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_visibility_selector.xml b/app/src/main/res/drawable/ic_visibility_selector.xml
new file mode 100644
index 000000000..891267a1d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_visibility_selector.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_vpn_lock_black_24px.xml b/app/src/main/res/drawable/ic_vpn_lock_black_24px.xml
new file mode 100644
index 000000000..266f1578d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_vpn_lock_black_24px.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_zoom_out_map_black_24px.xml b/app/src/main/res/drawable/ic_zoom_out_map_black_24px.xml
new file mode 100644
index 000000000..8fc5ffb14
--- /dev/null
+++ b/app/src/main/res/drawable/ic_zoom_out_map_black_24px.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/jb_activities.png b/app/src/main/res/drawable/jb_activities.png
deleted file mode 100644
index f4f7d1310..000000000
Binary files a/app/src/main/res/drawable/jb_activities.png and /dev/null differ
diff --git a/app/src/main/res/drawable/jb_aspects.png b/app/src/main/res/drawable/jb_aspects.png
deleted file mode 100644
index 1f2b3a4f1..000000000
Binary files a/app/src/main/res/drawable/jb_aspects.png and /dev/null differ
diff --git a/app/src/main/res/drawable/jb_commented.png b/app/src/main/res/drawable/jb_commented.png
deleted file mode 100644
index 64f27b0b3..000000000
Binary files a/app/src/main/res/drawable/jb_commented.png and /dev/null differ
diff --git a/app/src/main/res/drawable/jb_heart.png b/app/src/main/res/drawable/jb_heart.png
deleted file mode 100644
index ea9685e3e..000000000
Binary files a/app/src/main/res/drawable/jb_heart.png and /dev/null differ
diff --git a/app/src/main/res/drawable/jb_license.png b/app/src/main/res/drawable/jb_license.png
deleted file mode 100644
index b552a1c77..000000000
Binary files a/app/src/main/res/drawable/jb_license.png and /dev/null differ
diff --git a/app/src/main/res/drawable/jb_mentions.png b/app/src/main/res/drawable/jb_mentions.png
deleted file mode 100644
index 726393c00..000000000
Binary files a/app/src/main/res/drawable/jb_mentions.png and /dev/null differ
diff --git a/app/src/main/res/drawable/jb_settings.png b/app/src/main/res/drawable/jb_settings.png
deleted file mode 100644
index 9d65f2a17..000000000
Binary files a/app/src/main/res/drawable/jb_settings.png and /dev/null differ
diff --git a/app/src/main/res/drawable/jb_stream.png b/app/src/main/res/drawable/jb_stream.png
deleted file mode 100644
index a5586dbd4..000000000
Binary files a/app/src/main/res/drawable/jb_stream.png and /dev/null differ
diff --git a/app/src/main/res/drawable/jb_tag2.png b/app/src/main/res/drawable/jb_tag2.png
deleted file mode 100644
index c8711117f..000000000
Binary files a/app/src/main/res/drawable/jb_tag2.png and /dev/null differ
diff --git a/app/src/main/res/drawable/progressbar.xml b/app/src/main/res/drawable/progressbar.xml
new file mode 100644
index 000000000..2a6e82ecd
--- /dev/null
+++ b/app/src/main/res/drawable/progressbar.xml
@@ -0,0 +1,27 @@
+
+
+ -
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/sc_aspect.xml b/app/src/main/res/drawable/sc_aspect.xml
new file mode 100644
index 000000000..3ca0e79fe
--- /dev/null
+++ b/app/src/main/res/drawable/sc_aspect.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/sc_edit.xml b/app/src/main/res/drawable/sc_edit.xml
new file mode 100644
index 000000000..fb9e361a7
--- /dev/null
+++ b/app/src/main/res/drawable/sc_edit.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/sc_history.xml b/app/src/main/res/drawable/sc_history.xml
new file mode 100644
index 000000000..a100d6652
--- /dev/null
+++ b/app/src/main/res/drawable/sc_history.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/sc_tags.xml b/app/src/main/res/drawable/sc_tags.xml
new file mode 100644
index 000000000..ed8ad05f2
--- /dev/null
+++ b/app/src/main/res/drawable/sc_tags.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/splashscreen1.jpg b/app/src/main/res/drawable/splashscreen1.jpg
deleted file mode 100644
index bd2f04d61..000000000
Binary files a/app/src/main/res/drawable/splashscreen1.jpg and /dev/null differ
diff --git a/app/src/main/res/drawable/splashscreen2.jpg b/app/src/main/res/drawable/splashscreen2.jpg
deleted file mode 100644
index 2be7268dd..000000000
Binary files a/app/src/main/res/drawable/splashscreen2.jpg and /dev/null differ
diff --git a/app/src/main/res/drawable/splashscreen3.jpg b/app/src/main/res/drawable/splashscreen3.jpg
deleted file mode 100644
index 8f1a89974..000000000
Binary files a/app/src/main/res/drawable/splashscreen3.jpg and /dev/null differ
diff --git a/app/src/main/res/drawable/splashscreen4.jpg b/app/src/main/res/drawable/splashscreen4.jpg
deleted file mode 100644
index 77ef17d66..000000000
Binary files a/app/src/main/res/drawable/splashscreen4.jpg and /dev/null differ
diff --git a/app/src/main/res/drawable/splashscreen5.jpg b/app/src/main/res/drawable/splashscreen5.jpg
deleted file mode 100644
index e1128818f..000000000
Binary files a/app/src/main/res/drawable/splashscreen5.jpg and /dev/null differ
diff --git a/app/src/main/res/drawable/splashscreen6.jpg b/app/src/main/res/drawable/splashscreen6.jpg
deleted file mode 100644
index 6f97fa4c4..000000000
Binary files a/app/src/main/res/drawable/splashscreen6.jpg and /dev/null differ
diff --git a/app/src/main/res/drawable/tor_onion.xml b/app/src/main/res/drawable/tor_onion.xml
new file mode 100644
index 000000000..7df39ce0a
--- /dev/null
+++ b/app/src/main/res/drawable/tor_onion.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/about__activity.xml b/app/src/main/res/layout/about__activity.xml
new file mode 100644
index 000000000..6f0f20061
--- /dev/null
+++ b/app/src/main/res/layout/about__activity.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/about__fragment_about.xml b/app/src/main/res/layout/about__fragment_about.xml
new file mode 100644
index 000000000..61ecc491d
--- /dev/null
+++ b/app/src/main/res/layout/about__fragment_about.xml
@@ -0,0 +1,237 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/about__fragment_debug.xml b/app/src/main/res/layout/about__fragment_debug.xml
new file mode 100644
index 000000000..b65a3489f
--- /dev/null
+++ b/app/src/main/res/layout/about__fragment_debug.xml
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/about__fragment_license.xml b/app/src/main/res/layout/about__fragment_license.xml
new file mode 100644
index 000000000..eb6a1b302
--- /dev/null
+++ b/app/src/main/res/layout/about__fragment_license.xml
@@ -0,0 +1,214 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_pods.xml b/app/src/main/res/layout/activity_pods.xml
deleted file mode 100644
index 5e10d4091..000000000
--- a/app/src/main/res/layout/activity_pods.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_splash.xml b/app/src/main/res/layout/activity_splash.xml
deleted file mode 100644
index 7d4c9e9a4..000000000
--- a/app/src/main/res/layout/activity_splash.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/layout/app_bar_main.xml b/app/src/main/res/layout/app_bar_main.xml
deleted file mode 100644
index b82ae34fa..000000000
--- a/app/src/main/res/layout/app_bar_main.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/browser__fragment.xml b/app/src/main/res/layout/browser__fragment.xml
new file mode 100644
index 000000000..96daf8b13
--- /dev/null
+++ b/app/src/main/res/layout/browser__fragment.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml
deleted file mode 100644
index b7591b66b..000000000
--- a/app/src/main/res/layout/content_main.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/font_size_chooser.xml b/app/src/main/res/layout/font_size_chooser.xml
deleted file mode 100644
index a5217a0a9..000000000
--- a/app/src/main/res/layout/font_size_chooser.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/main__activity.xml
similarity index 69%
rename from app/src/main/res/layout/activity_main.xml
rename to app/src/main/res/layout/main__activity.xml
index a61d8a630..eeaeebec2 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/main__activity.xml
@@ -2,24 +2,27 @@
+ app:headerLayout="@layout/main__nav_header"
+ app:itemTextColor="@color/primary_text"
+ app:menu="@menu/main__navdrawer"
+ app:paddingEnd="0dp"
+ app:paddingStart="0dp" />
diff --git a/app/src/main/res/layout/main__app_bar.xml b/app/src/main/res/layout/main__app_bar.xml
new file mode 100644
index 000000000..4d42fe9f6
--- /dev/null
+++ b/app/src/main/res/layout/main__app_bar.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/main__nav_header.xml b/app/src/main/res/layout/main__nav_header.xml
new file mode 100644
index 000000000..b19e40fdf
--- /dev/null
+++ b/app/src/main/res/layout/main__nav_header.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/nav_header_main.xml b/app/src/main/res/layout/nav_header_main.xml
deleted file mode 100644
index 81140fb10..000000000
--- a/app/src/main/res/layout/nav_header_main.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/podselection__dialog.xml b/app/src/main/res/layout/podselection__dialog.xml
new file mode 100644
index 000000000..78c555803
--- /dev/null
+++ b/app/src/main/res/layout/podselection__dialog.xml
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/podselection__fragment.xml b/app/src/main/res/layout/podselection__fragment.xml
new file mode 100644
index 000000000..c6d9f45f8
--- /dev/null
+++ b/app/src/main/res/layout/podselection__fragment.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/recycler_list__fragment.xml b/app/src/main/res/layout/recycler_list__fragment.xml
new file mode 100644
index 000000000..7db300f65
--- /dev/null
+++ b/app/src/main/res/layout/recycler_list__fragment.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/recycler_list__list_item_with_fav.xml b/app/src/main/res/layout/recycler_list__list_item_with_fav.xml
new file mode 100644
index 000000000..e72fdbdf1
--- /dev/null
+++ b/app/src/main/res/layout/recycler_list__list_item_with_fav.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/settings__activity.xml b/app/src/main/res/layout/settings__activity.xml
new file mode 100644
index 000000000..129bb7874
--- /dev/null
+++ b/app/src/main/res/layout/settings__activity.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/ui__dialog__color_picker.xml b/app/src/main/res/layout/ui__dialog__color_picker.xml
new file mode 100644
index 000000000..26223ed4a
--- /dev/null
+++ b/app/src/main/res/layout/ui__dialog__color_picker.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/ui__dialog_search__people_tags.xml b/app/src/main/res/layout/ui__dialog_search__people_tags.xml
new file mode 100644
index 000000000..1d3776623
--- /dev/null
+++ b/app/src/main/res/layout/ui__dialog_search__people_tags.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml
deleted file mode 100644
index 8d15fa2c2..000000000
--- a/app/src/main/res/menu/activity_main_drawer.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/menu/main__menu_top.xml b/app/src/main/res/menu/main__menu_top.xml
new file mode 100644
index 000000000..92c17a7a5
--- /dev/null
+++ b/app/src/main/res/menu/main__menu_top.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/main__navdrawer.xml b/app/src/main/res/menu/main__navdrawer.xml
new file mode 100644
index 000000000..9a69c2f54
--- /dev/null
+++ b/app/src/main/res/menu/main__navdrawer.xml
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/menu_compose.xml b/app/src/main/res/menu/menu_compose.xml
deleted file mode 100644
index bf90ed685..000000000
--- a/app/src/main/res/menu/menu_compose.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
deleted file mode 100644
index 819d986de..000000000
--- a/app/src/main/res/menu/menu_main.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/menu/menu_pods.xml b/app/src/main/res/menu/menu_pods.xml
deleted file mode 100644
index 44943da05..000000000
--- a/app/src/main/res/menu/menu_pods.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/menu/podselection__menu.xml b/app/src/main/res/menu/podselection__menu.xml
new file mode 100644
index 000000000..859586240
--- /dev/null
+++ b/app/src/main/res/menu/podselection__menu.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/stream__menu_top.xml b/app/src/main/res/menu/stream__menu_top.xml
new file mode 100644
index 000000000..36a410b82
--- /dev/null
+++ b/app/src/main/res/menu/stream__menu_top.xml
@@ -0,0 +1,46 @@
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_arrow_right_grey600_24dp.png b/app/src/main/res/mipmap-hdpi/ic_arrow_right_grey600_24dp.png
deleted file mode 100644
index 65248d20b..000000000
Binary files a/app/src/main/res/mipmap-hdpi/ic_arrow_right_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_arrow_right_grey600_24dp.png b/app/src/main/res/mipmap-mdpi/ic_arrow_right_grey600_24dp.png
deleted file mode 100644
index dd79fff4a..000000000
Binary files a/app/src/main/res/mipmap-mdpi/ic_arrow_right_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_arrow_right_grey600_24dp.png b/app/src/main/res/mipmap-xhdpi/ic_arrow_right_grey600_24dp.png
deleted file mode 100644
index d2fd86fad..000000000
Binary files a/app/src/main/res/mipmap-xhdpi/ic_arrow_right_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_arrow_right_grey600_24dp.png b/app/src/main/res/mipmap-xxhdpi/ic_arrow_right_grey600_24dp.png
deleted file mode 100644
index 7f3a9f19a..000000000
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_arrow_right_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_arrow_right_grey600_24dp.png b/app/src/main/res/mipmap-xxxhdpi/ic_arrow_right_grey600_24dp.png
deleted file mode 100644
index fd116d1c8..000000000
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_arrow_right_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/raw/adblock_domains__yoyo.txt b/app/src/main/res/raw/adblock_domains__yoyo.txt
new file mode 100644
index 000000000..cecbc9cae
--- /dev/null
+++ b/app/src/main/res/raw/adblock_domains__yoyo.txt
@@ -0,0 +1,2343 @@
+# From https://pgl.yoyo.org/as/
+101com.com
+101order.com
+123found.com
+180hits.de
+180searchassistant.com
+207.net
+247media.com
+24log.com
+24log.de
+24pm-affiliation.com
+2mdn.net
+2o7.net
+360yield.com
+4affiliate.net
+4d5.net
+50websads.com
+518ad.com
+51yes.com
+600z.com
+777partner.com
+77tracking.com
+7bpeople.com
+7search.com
+99count.com
+a-ads.com
+a-counter.kiev.ua
+a.0day.kiev.ua
+a.aproductmsg.com
+a.collective-media.net
+a.consumer.net
+a.mktw.net
+a.sakh.com
+a.ucoz.net
+a.ucoz.ru
+a.xanga.com
+a32.g.a.yimg.com
+aaddzz.com
+abacho.net
+abc-ads.com
+absoluteclickscom.com
+abz.com
+ac.rnm.ca
+accounts.pkr.com.invalid
+acsseo.com
+actionsplash.com
+actualdeals.com
+acuityads.com
+ad-balancer.at
+ad-balancer.net
+ad-center.com
+ad-pay.de
+ad-rotator.com
+ad-server.gulasidorna.se
+ad-serverparc.nl
+ad-souk.com
+ad-space.net
+ad-tech.com
+ad-up.com
+ad.100.tbn.ru
+ad.71i.de
+ad.a8.net
+ad.abcnews.com
+ad.abctv.com
+ad.aboutwebservices.com
+ad.abum.com
+ad.afy11.net
+ad.allstar.cz
+ad.altervista.org
+ad.amgdgt.com
+ad.anuntis.com
+ad.auditude.com
+ad.bizo.com
+ad.bnmla.com
+ad.bondage.com
+ad.caradisiac.com
+ad.centrum.cz
+ad.cgi.cz
+ad.choiceradio.com
+ad.clix.pt
+ad.cooks.com
+ad.crwdcntrl.net
+ad.digitallook.com
+ad.directrev.com
+ad.doctissimo.fr
+ad.domainfactory.de
+ad.e-kolay.net
+ad.eurosport.com
+ad.f1cd.ru
+ad.flurry.com
+ad.foxnetworks.com
+ad.freecity.de
+ad.gate24.ch
+ad.globe7.com
+ad.grafika.cz
+ad.hbv.de
+ad.hodomobile.com
+ad.httpool.com
+ad.hyena.cz
+ad.iinfo.cz
+ad.ilove.ch
+ad.infoseek.com
+ad.jamba.net
+ad.jamster.co.uk
+ad.jetsoftware.com
+ad.keenspace.com
+ad.leadbolt.net
+ad.liveinternet.ru
+ad.lupa.cz
+ad.media-servers.net
+ad.mediastorm.hu
+ad.mgd.de
+ad.musicmatch.com
+ad.nachtagenten.de
+ad.nozonedata.com
+ad.nttnavi.co.jp
+ad.nwt.cz
+ad.onad.eu
+ad.pandora.tv
+ad.preferances.com
+ad.profiwin.de
+ad.prv.pl
+ad.rambler.ru
+ad.reunion.com
+ad.scanmedios.com
+ad.sensismediasmart.com.au
+ad.seznam.cz
+ad.simgames.net
+ad.slutload.com
+ad.smartclip.net
+ad.tbn.ru
+ad.technoratimedia.com
+ad.thewheelof.com
+ad.turn.com
+ad.tv2.no
+ad.twitchguru.com
+ad.usatoday.com
+ad.virtual-nights.com
+ad.wavu.hu
+ad.way.cz
+ad.weatherbug.com
+ad.wsod.com
+ad.wz.cz
+ad.yadro.ru
+ad.yourmedia.com
+ad.zanox.com
+ad0.bigmir.net
+ad01.mediacorpsingapore.com
+ad1.emediate.dk
+ad1.emule-project.org
+ad1.kde.cz
+ad1.pamedia.com.au
+ad2.iinfo.cz
+ad2.linxcz.cz
+ad2.lupa.cz
+ad2flash.com
+ad2games.com
+ad3.iinfo.cz
+ad3.pamedia.com.au
+ad4game.com
+adaction.de
+adadvisor.net
+adap.tv
+adapt.tv
+adbanner.ro
+adbard.net
+adbers.com
+adblade.com
+adblockanalytics.com
+adboost.de.vu
+adboost.net
+adbooth.net
+adbot.com
+adbrite.com
+adbroker.de
+adbunker.com
+adbutler.com
+adbutler.de
+adbuyer.com
+adbuyer3.lycos.com
+adcash.com
+adcast.deviantart.com
+adcell.de
+adcenter.mdf.se
+adcenter.net
+adcentriconline.com
+adcept.net
+adclick.com
+adclient.uimserv.net
+adclient1.tucows.com
+adcomplete.com
+adconion.com
+adcontent.gamespy.com
+adcycle.com
+add.newmedia.cz
+addealing.com
+addfreestats.com
+addme.com
+adecn.com
+ademails.com
+adengage.com
+adexpose.com
+adext.inkclub.com
+adf.ly
+adfactor.nl
+adfarm.mediaplex.com
+adflight.com
+adforce.com
+adform.com
+adgardener.com
+adgoto.com
+adgridwork.com
+adhese.be
+adhese.com
+adimage.asiaone.com.sg
+adimage.guardian.co.uk
+adimages.been.com
+adimages.carsoup.com
+adimages.go.com
+adimages.homestore.com
+adimages.omroepzeeland.nl
+adimages.sanomawsoy.fi
+adimg.cnet.com
+adimg.com.com
+adimg.uimserv.net
+adimg1.chosun.com
+adimgs.sapo.pt
+adimpact.com
+adinjector.net
+adinterax.com
+adisfy.com
+adition.com
+adition.de
+adition.net
+adizio.com
+adjix.com
+adjug.com
+adjuggler.com
+adjuggler.yourdictionary.com
+adjustnetwork.com
+adk2.com
+adk2ads.tictacti.com
+adland.ru
+adlantic.nl
+adledge.com
+adlegend.com
+adlog.com.com
+adloox.com
+adlooxtracking.com
+adlure.net
+admagnet.net
+admailtiser.com
+adman.gr
+adman.in.gr
+adman.otenet.gr
+admanagement.ch
+admanager.btopenworld.com
+admanager.carsoup.com
+admarketplace.net
+admarvel.com
+admax.nexage.com
+admedia.com
+admedia.ro
+admeld.com
+admerize.be
+admeta.com
+admex.com
+adminder.com
+adminshop.com
+admized.com
+admob.com
+admonitor.com
+admotion.com.ar
+adnet-media.net
+adnet.asahi.com
+adnet.biz
+adnet.de
+adnet.ru
+adnet.worldreviewer.com
+adnetinteractive.com
+adnetwork.net
+adnetworkperformance.com
+adnews.maddog2000.de
+adnotch.com
+adnxs.com
+adocean.pl
+adonspot.com
+adoperator.com
+adorigin.com
+adpepper.dk
+adpepper.nl
+adperium.com
+adpia.vn
+adplus.co.id
+adplxmd.com
+adprofile.net
+adprojekt.pl
+adq.nextag.com
+adrazzi.com
+adreactor.com
+adrecreate.com
+adremedy.com
+adreporting.com
+adres.internet.com
+adrevolver.com
+adriver.ru
+adrolays.de
+adrotate.de
+adrotator.se
+adrta.com
+ads-click.com
+ads.4tube.com
+ads.5ci.lt
+ads.abovetopsecret.com
+ads.aceweb.net
+ads.activestate.com
+ads.adfox.ru
+ads.administrator.de
+ads.adshareware.net
+ads.adultfriendfinder.com
+ads.adultswim.com
+ads.advance.net
+ads.adverline.com
+ads.affiliates.match.com
+ads.ak.facebook.com.edgesuite.net
+ads.allvatar.com
+ads.alt.com
+ads.amdmb.com
+ads.amigos.com
+ads.aol.co.uk
+ads.aol.com
+ads.apn.co.nz
+ads.appsgeyser.com
+ads.as4x.tmcs.net
+ads.as4x.tmcs.ticketmaster.com
+ads.asia1.com.sg
+ads.asiafriendfinder.com
+ads.ask.com
+ads.aspalliance.com
+ads.avazu.net
+ads.batpmturner.com
+ads.beenetworks.net
+ads.belointeractive.com
+ads.berlinonline.de
+ads.betanews.com
+ads.betfair.com
+ads.betfair.com.au
+ads.bigchurch.com
+ads.bigfoot.com
+ads.bing.com
+ads.bittorrent.com
+ads.blog.com
+ads.bloomberg.com
+ads.bluelithium.com
+ads.bluemountain.com
+ads.bluesq.com
+ads.bonniercorp.com
+ads.boylesports.com
+ads.brabys.com
+ads.brazzers.com
+ads.bumq.com
+ads.businessweek.com
+ads.canalblog.com
+ads.canoe.ca
+ads.casinocity.com
+ads.cbc.ca
+ads.cc
+ads.cc-dt.com
+ads.centraliprom.com
+ads.cgnetworks.com
+ads.channel4.com
+ads.clearchannel.com
+ads.co.com
+ads.com.com
+ads.contactmusic.com
+ads.contentabc.com
+ads.contextweb.com
+ads.crakmedia.com
+ads.creative-serving.com
+ads.creativematch.com
+ads.cricbuzz.com
+ads.cybersales.cz
+ads.dada.it
+ads.datinggold.com
+ads.datingyes.com
+ads.dazoot.ro
+ads.deltha.hu
+ads.dennisnet.co.uk
+ads.desmoinesregister.com
+ads.detelefoongids.nl
+ads.deviantart.com
+ads.digital-digest.com
+ads.digitalmedianet.com
+ads.digitalpoint.com
+ads.directionsmag.com
+ads.domeus.com
+ads.eagletribune.com
+ads.easy-forex.com
+ads.eatinparis.com
+ads.economist.com
+ads.edbindex.dk
+ads.egrana.com.br
+ads.einmedia.com
+ads.electrocelt.com
+ads.elitetrader.com
+ads.emirates.net.ae
+ads.epltalk.com
+ads.eu.msn.com
+ads.exactdrive.com
+ads.expat-blog.biz
+ads.expedia.com
+ads.ezboard.com
+ads.factorymedia.com
+ads.fairfax.com.au
+ads.faxo.com
+ads.ferianc.com
+ads.filmup.com
+ads.financialcontent.com
+ads.flooble.com
+ads.fool.com
+ads.footymad.net
+ads.forbes.com
+ads.forbes.net
+ads.forium.de
+ads.fortunecity.com
+ads.fotosidan.se
+ads.foxkidseurope.net
+ads.foxnetworks.com
+ads.foxnews.com
+ads.freecity.de
+ads.friendfinder.com
+ads.ft.com
+ads.futurenet.com
+ads.gamecity.net
+ads.gamershell.com
+ads.gamespyid.com
+ads.gamigo.de
+ads.gaming-universe.de
+ads.gawker.com
+ads.geekswithblogs.net
+ads.glispa.com
+ads.gmodules.com
+ads.godlikeproductions.com
+ads.goyk.com
+ads.gplusmedia.com
+ads.gradfinder.com
+ads.grindinggears.com
+ads.groundspeak.com
+ads.gsm-exchange.com
+ads.gsmexchange.com
+ads.guardian.co.uk
+ads.guardianunlimited.co.uk
+ads.guru3d.com
+ads.hardwaresecrets.com
+ads.harpers.org
+ads.hbv.de
+ads.hearstmags.com
+ads.heartlight.org
+ads.heias.com
+ads.hideyourarms.com
+ads.hollywood.com
+ads.horsehero.com
+ads.horyzon-media.com
+ads.iafrica.com
+ads.ibest.com.br
+ads.ibryte.com
+ads.icq.com
+ads.ign.com
+ads.img.co.za
+ads.imgur.com
+ads.indiatimes.com
+ads.infi.net
+ads.internic.co.il
+ads.ipowerweb.com
+ads.isoftmarketing.com
+ads.itv.com
+ads.iwon.com
+ads.jewishfriendfinder.com
+ads.jiwire.com
+ads.jobsite.co.uk
+ads.jpost.com
+ads.jubii.dk
+ads.justhungry.com
+ads.kaktuz.net
+ads.kelbymediagroup.com
+ads.kinobox.cz
+ads.kinxxx.com
+ads.kompass.com
+ads.krawall.de
+ads.lesbianpersonals.com
+ads.linuxfoundation.org
+ads.linuxjournal.com
+ads.linuxsecurity.com
+ads.livenation.com
+ads.mariuana.it
+ads.massinfra.nl
+ads.mcafee.com
+ads.mediaodyssey.com
+ads.mediaturf.net
+ads.medienhaus.de
+ads.mgnetwork.com
+ads.mmania.com
+ads.moceanads.com
+ads.motor-forum.nl
+ads.motormedia.nl
+ads.msn.com
+ads.multimania.lycos.fr
+ads.nationalgeographic.com
+ads.ncm.com
+ads.netclusive.de
+ads.netmechanic.com
+ads.networksolutions.com
+ads.newdream.net
+ads.newgrounds.com
+ads.newmedia.cz
+ads.newsint.co.uk
+ads.newsquest.co.uk
+ads.ninemsn.com.au
+ads.nj.com
+ads.nola.com
+ads.nordichardware.com
+ads.nordichardware.se
+ads.nwsource.com
+ads.nyi.net
+ads.nytimes.com
+ads.nyx.cz
+ads.nzcity.co.nz
+ads.o2.pl
+ads.oddschecker.com
+ads.okcimg.com
+ads.ole.com
+ads.olivebrandresponse.com
+ads.oneplace.com
+ads.ookla.com
+ads.optusnet.com.au
+ads.outpersonals.com
+ads.passion.com
+ads.pennet.com
+ads.penny-arcade.com
+ads.pheedo.com
+ads.phpclasses.org
+ads.pickmeup-ltd.com
+ads.planet.nl
+ads.pni.com
+ads.pof.com
+ads.powweb.com
+ads.primissima.it
+ads.printscr.com
+ads.prisacom.com
+ads.program3.com
+ads.psd2html.com
+ads.pushplay.com
+ads.quoka.de
+ads.rcs.it
+ads.recoletos.es
+ads.rediff.com
+ads.redlightcenter.com
+ads.redtube.com
+ads.resoom.de
+ads.returnpath.net
+ads.s3.sitepoint.com
+ads.satyamonline.com
+ads.savannahnow.com
+ads.saymedia.com
+ads.scifi.com
+ads.seniorfriendfinder.com
+ads.servebom.com
+ads.sexinyourcity.com
+ads.shizmoo.com
+ads.shopstyle.com
+ads.sift.co.uk
+ads.silverdisc.co.uk
+ads.slim.com
+ads.smartclick.com
+ads.soft32.com
+ads.space.com
+ads.sptimes.com
+ads.stackoverflow.com
+ads.sun.com
+ads.supplyframe.com
+ads.t-online.de
+ads.tahono.com
+ads.techtv.com
+ads.telegraph.co.uk
+ads.themovienation.com
+ads.thestar.com
+ads.tmcs.net
+ads.totallyfreestuff.com
+ads.townhall.com
+ads.trinitymirror.co.uk
+ads.tripod.com
+ads.tripod.lycos.co.uk
+ads.tripod.lycos.de
+ads.tripod.lycos.es
+ads.tripod.lycos.it
+ads.tripod.lycos.nl
+ads.tripod.spray.se
+ads.tso.dennisnet.co.uk
+ads.uknetguide.co.uk
+ads.ultimate-guitar.com
+ads.uncrate.com
+ads.undertone.com
+ads.usatoday.com
+ads.v3.com
+ads.verticalresponse.com
+ads.vgchartz.com
+ads.videosz.com
+ads.virtual-nights.com
+ads.virtualcountries.com
+ads.vnumedia.com
+ads.waps.cn
+ads.wapx.cn
+ads.weather.ca
+ads.web.aol.com
+ads.web.cs.com
+ads.web.de
+ads.webmasterpoint.org
+ads.websiteservices.com
+ads.whi.co.nz
+ads.whoishostingthis.com
+ads.wiezoekje.nl
+ads.wikia.nocookie.net
+ads.wineenthusiast.com
+ads.wwe.biz
+ads.xhamster.com
+ads.xtra.co.nz
+ads.y-0.net
+ads.yahoo.com
+ads.yimg.com
+ads.yldmgrimg.net
+ads.yourfreedvds.com
+ads.youtube.com
+ads.zdnet.com
+ads.ztod.com
+ads03.redtube.com
+ads1.canoe.ca
+ads1.mediacapital.pt
+ads1.msn.com
+ads1.rne.com
+ads1.virtual-nights.com
+ads10.speedbit.com
+ads180.com
+ads2.brazzers.com
+ads2.clearchannel.com
+ads2.contentabc.com
+ads2.gamecity.net
+ads2.jubii.dk
+ads2.net-communities.co.uk
+ads2.oneplace.com
+ads2.rne.com
+ads2.virtual-nights.com
+ads2.xnet.cz
+ads2004.treiberupdate.de
+ads3.contentabc.com
+ads3.gamecity.net
+ads3.virtual-nights.com
+ads4.clearchannel.com
+ads4.gamecity.net
+ads4.virtual-nights.com
+ads4homes.com
+ads5.canoe.ca
+ads5.virtual-nights.com
+ads6.gamecity.net
+ads7.gamecity.net
+ads8.com
+adsatt.abc.starwave.com
+Adsatt.ABCNews.starwave.com
+adsatt.espn.go.com
+adsatt.espn.starwave.com
+Adsatt.go.starwave.com
+adsby.bidtheatre.com
+adscale.de
+adscholar.com
+adscience.nl
+adscpm.com
+adsdaq.com
+adsdk.com
+adsend.de
+adserv.evo-x.de
+adserv.gamezone.de
+adserv.iafrica.com
+adserv.qconline.com
+adserve.ams.rhythmxchange.com
+adserver-live.yoc.mobi
+adserver.43plc.com
+adserver.71i.de
+adserver.adultfriendfinder.com
+adserver.aidameter.com
+adserver.aol.fr
+adserver.beggarspromo.com
+adserver.betandwin.de
+adserver.bing.com
+adserver.bizhat.com
+adserver.break-even.it
+adserver.cams.com
+adserver.com
+adserver.digitoday.com
+adserver.dotcommedia.de
+adserver.finditquick.com
+adserver.flossiemediagroup.com
+adserver.freecity.de
+adserver.freenet.de
+adserver.friendfinder.com
+adserver.hardsextube.com
+adserver.hardwareanalysis.com
+adserver.html.it
+adserver.irishwebmasterforum.com
+adserver.janes.com
+adserver.libero.it
+adserver.news.com.au
+adserver.ngz-network.de
+adserver.nydailynews.com
+adserver.o2.pl
+adserver.oddschecker.com
+adserver.omroepzeeland.nl
+adserver.pl
+adserver.portalofevil.com
+adserver.portugalmail.net
+adserver.portugalmail.pt
+adserver.sanomawsoy.fi
+adserver.sciflicks.com
+adserver.sharewareonline.com
+adserver.spankaway.com
+adserver.startnow.com
+adserver.theonering.net
+adserver.twitpic.com
+adserver.viagogo.com
+adserver.virginmedia.com
+adserver.yahoo.com
+adserver01.de
+adserver1-images.backbeatmedia.com
+adserver1.backbeatmedia.com
+adserver1.mindshare.de
+adserver1.ogilvy-interactive.de
+adserver2.mindshare.de
+adserverplus.com
+adserversolutions.com
+adservinginternational.com
+adsfac.eu
+adsfac.net
+adsfac.us
+adshost1.com
+adside.com
+adsk2.co
+adskape.ru
+adsklick.de
+adsmarket.com
+adsmart.co.uk
+adsmart.com
+adsmart.net
+adsmogo.com
+adsnative.com
+adsoftware.com
+adsoldier.com
+adsonar.com
+adspace.ro
+adspeed.net
+adspirit.de
+adsponse.de
+adsremote.scrippsnetworks.com
+adsrevenue.net
+adsrv.deviantart.com
+adsrv.eacdn.com
+adsrv.iol.co.za
+adsrvr.org
+adsstat.com
+adstat.4u.pl
+adstest.weather.com
+adsupply.com
+adsymptotic.com
+adsynergy.com
+adsys.townnews.com
+adsystem.simplemachines.org
+adtech.de
+adtechus.com
+adtegrity.net
+adthis.com
+adtiger.de
+adtoll.com
+adtology.com
+adtoma.com
+adtrace.org
+adtrade.net
+adtrading.de
+adtrak.net
+adtriplex.com
+adultadvertising.com
+adv-adserver.com
+adv-banner.libero.it
+adv.cooperhosting.net
+adv.freeonline.it
+adv.hwupgrade.it
+adv.livedoor.com
+adv.webmd.com
+adv.wp.pl
+adv.yo.cz
+advariant.com
+adventory.com
+advert.bayarea.com
+advert.dyna.ultraweb.hu
+adverticum.com
+adverticum.net
+adverticus.de
+advertise.com
+advertiseireland.com
+advertisespace.com
+advertising.com
+advertising.guildlaunch.net
+advertisingbanners.com
+advertisingbox.com
+advertmarket.com
+advertmedia.de
+advertpro.sitepoint.com
+advertpro.ya.com
+adverts.carltononline.com
+advertserve.com
+advertstream.com
+advertwizard.com
+advideo.uimserv.net
+adview.ppro.de
+advisormedia.cz
+adviva.net
+advnt.com
+adwareremovergold.com
+adwhirl.com
+adwitserver.com
+adworldnetwork.com
+adworx.at
+adworx.be
+adworx.nl
+adx.allstar.cz
+adx.atnext.com
+adxpansion.com
+adxpose.com
+adxvalue.com
+adyea.com
+adzerk.net
+adzerk.s3.amazonaws.com
+adzones.com
+af-ad.co.uk
+affbuzzads.com
+affili.net
+affiliate.1800flowers.com
+affiliate.doubleyourdating.com
+affiliate.dtiserv.com
+affiliate.gamestop.com
+affiliate.mercola.com
+affiliate.mogs.com
+affiliate.offgamers.com
+affiliate.travelnow.com
+affiliate.viator.com
+affiliatefuel.com
+affiliatefuture.com
+affiliates.allposters.com
+affiliates.babylon.com
+affiliates.digitalriver.com
+affiliates.globat.com
+affiliates.internationaljock.com
+affiliates.streamray.com
+affiliates.thinkhost.net
+affiliates.thrixxx.com
+affiliates.ultrahosting.com
+affiliatetracking.com
+affiliatetracking.net
+affiliatewindow.com
+affiliation-france.com
+afftracking.justanswer.com
+ah-ha.com
+ahalogy.com
+aidu-ads.de
+aim4media.com
+aistat.net
+aktrack.pubmatic.com
+alclick.com
+alenty.com
+alexa-sitestats.s3.amazonaws.com
+all4spy.com
+alladvantage.com
+allosponsor.com
+amazingcounters.com
+amazon-adsystem.com
+americash.com
+amung.us
+an.tacoda.net
+anahtars.com
+analytics.adpost.org
+analytics.google.com
+analytics.live.com
+analytics.yahoo.com
+anm.intelli-direct.com
+annonser.dagbladet.no
+apex-ad.com
+api.intensifier.de
+apture.com
+arc1.msn.com
+arcadebanners.com
+ard.xxxblackbook.com
+are-ter.com
+as.webmd.com
+as1.advfn.com
+assets1.exgfnetwork.com
+assoc-amazon.com
+at-adserver.alltop.com
+atdmt.com
+athena-ads.wikia.com
+atwola.com
+auctionads.com
+auctionads.net
+audience2media.com
+audit.median.hu
+audit.webinform.hu
+auto-bannertausch.de
+autohits.dk
+avenuea.com
+avpa.javalobby.org
+avres.net
+avsads.com
+awempire.com
+awin1.com
+azfront.com
+b-1st.com
+b.aol.com
+b.engadget.com
+ba.afl.rakuten.co.jp
+babs.tv2.dk
+backbeatmedia.com
+banik.redigy.cz
+banner-exchange-24.de
+banner.ad.nu
+banner.ambercoastcasino.com
+banner.blogranking.net
+banner.buempliz-online.ch
+banner.casino.net
+banner.casinodelrio.com
+banner.cotedazurpalace.com
+banner.coza.com
+banner.cz
+banner.easyspace.com
+banner.elisa.net
+banner.eurogrand.com
+banner.featuredusers.com
+banner.getgo.de
+banner.goldenpalace.com
+banner.img.co.za
+banner.inyourpocket.com
+banner.kiev.ua
+banner.linux.se
+banner.media-system.de
+banner.mindshare.de
+banner.nixnet.cz
+banner.noblepoker.com
+banner.northsky.com
+banner.orb.net
+banner.penguin.cz
+banner.rbc.ru
+banner.relcom.ru
+banner.tanto.de
+banner.titan-dsl.de
+banner.vadian.net
+banner.webmersion.com
+banner.wirenode.com
+bannerads.de
+bannerboxes.com
+bannercommunity.de
+bannerconnect.com
+bannerconnect.net
+bannerexchange.cjb.net
+bannerflow.com
+bannergrabber.internet.gr
+bannerhost.com
+bannerimage.com
+bannerlandia.com.ar
+bannermall.com
+bannermarkt.nl
+bannerpower.com
+banners.adultfriendfinder.com
+banners.amigos.com
+banners.asiafriendfinder.com
+banners.audioholics.com
+banners.babylon-x.com
+banners.bol.com.br
+banners.cams.com
+banners.clubseventeen.com
+banners.czi.cz
+banners.dine.com
+banners.direction-x.com
+banners.directnic.com
+banners.easydns.com
+banners.freett.com
+banners.friendfinder.com
+banners.getiton.com
+banners.iq.pl
+banners.isoftmarketing.com
+banners.lifeserv.com
+banners.linkbuddies.com
+banners.passion.com
+banners.resultonline.com
+banners.sexsearch.com
+banners.sys-con.com
+banners.thomsonlocal.com
+banners.videosz.com
+banners.virtuagirlhd.com
+banners.wunderground.com
+bannerserver.com
+bannersgomlm.com
+bannershotlink.perfectgonzo.com
+bannersng.yell.com
+bannerspace.com
+bannerswap.com
+bannertesting.com
+bannery.cz
+bannieres.acces-contenu.com
+bans.adserver.co.il
+bans.bride.ru
+barnesandnoble.bfast.com
+basebanner.com
+baypops.com
+bbelements.com
+bbn.img.com.ua
+begun.ru
+belstat.com
+belstat.nl
+berp.com
+best-pr.info
+best-top.ro
+bestsearch.net
+bhclicks.com
+bidclix.com
+bidclix.net
+bidswitch.net
+bidtrk.com
+bidvertiser.com
+bigbangmedia.com
+bigclicks.com
+billboard.cz
+bitads.net
+bitmedianetwork.com
+bizad.nikkeibp.co.jp
+bizrate.com
+blast4traffic.com
+blingbucks.com
+blogads.com
+blogcounter.de
+blogherads.com
+blogrush.com
+blogtoplist.se
+blogtopsites.com
+blueadvertise.com
+bluekai.com
+bluelithium.com
+bluewhaleweb.com
+bm.annonce.cz
+bn.bfast.com
+boersego-ads.de
+boldchat.com
+boom.ro
+boomads.com
+boost-my-pr.de
+box.anchorfree.net
+bpath.com
+braincash.com
+brandreachsys.com
+bravenet.com.invalid
+bridgetrack.com
+brightinfo.com
+british-banners.com
+bs.yandex.ru
+bttrack.com
+budsinc.com
+bullseye.backbeatmedia.com
+buyhitscheap.com
+buysellads.com
+buzzonclick.com
+bvalphaserver.com
+bwp.download.com
+c.bigmir.net
+c1.nowlinux.com
+c1exchange.com
+campaign.bharatmatrimony.com
+caniamedia.com
+carbonads.com
+carbonads.net
+casalemedia.com
+casalmedia.com
+cash4members.com
+cash4popup.de
+cashcrate.com
+cashengines.com
+cashfiesta.com
+cashlayer.com
+cashpartner.com
+casinogames.com
+casinopays.com
+casinorewards.com
+casinotraffic.com
+casinotreasure.com
+cbanners.virtuagirlhd.com
+cbmall.com
+cdn.freefacti.com
+cdn.freefarcy.com
+cecash.com
+centerpointmedia.com
+ceskydomov.alias.ngs.modry.cz
+cetrk.com
+cgicounter.puretec.de
+ch.questionmarket.com
+chameleon.ad
+channelintelligence.com
+chart.dk
+chartbeat.com
+chartbeat.net
+checkm8.com
+checkstat.nl
+chestionar.ro
+chitika.net
+cibleclick.com
+cityads.telus.net
+cj.com
+cjbmanagement.com
+cjlog.com
+claria.com
+class-act-clicks.com
+click.absoluteagency.com
+click.fool.com
+click.kmindex.ru
+click2freemoney.com
+click2paid.com
+clickability.com
+clickadz.com
+clickagents.com
+clickbank.com
+clickbank.net
+clickbooth.com
+clickboothlnk.com
+clickbrokers.com
+clickcompare.co.uk
+clickdensity.com
+clickedyclick.com
+clickhereforcellphones.com
+clickhouse.com
+clickhype.com
+clicklink.jp
+clickmedia.ro
+clickonometrics.pl
+clicks.equantum.com
+clicks.mods.de
+clickserve.cc-dt.com
+clicksor.com
+clicktag.de
+clickthrucash.com
+clickthruserver.com
+clickthrutraffic.com
+clicktrace.info
+clicktrack.ziyu.net
+clicktracks.com
+clicktrade.com
+clickxchange.com
+clickz.com
+clickzxc.com
+clicmanager.fr
+clientmetrics-pa.googleapis.com
+clients.tbo.com
+clixgalore.com
+clk.konflab.com
+clkads.com
+clkrev.com
+cluster.adultworld.com
+clustrmaps.com
+cmpstar.com
+cnomy.com
+cnt.spbland.ru
+cnt1.pocitadlo.cz
+code-server.biz
+colonize.com
+comclick.com
+commindo-media-ressourcen.de
+commissionmonster.com
+compactbanner.com
+comprabanner.it
+confirmed-profits.com
+connextra.com
+contaxe.de
+content.acc-hd.de
+content.ad
+contextweb.com
+conversantmedia.com
+conversionruler.com
+cookies.cmpnet.com
+coremetrics.com
+count.rbc.ru
+count.rin.ru
+count.west263.com
+counted.com
+counter.bloke.com
+counter.cnw.cz
+counter.cz
+counter.dreamhost.com
+counter.fateback.com
+counter.mirohost.net
+counter.mojgorod.ru
+counter.nowlinux.com
+counter.rambler.ru
+counter.search.bg
+counter.sparklit.com
+counter.yadro.ru
+counters.honesty.com
+counting.kmindex.ru
+counts.tucows.com
+coupling-media.de
+cpalead.com
+cpays.com
+cpmaffiliation.com
+cpmstar.com
+cpxinteractive.com
+cqcounter.com
+crakmedia.com
+craktraffic.com
+crawlability.com
+crazypopups.com
+creafi-online-media.com
+creative.whi.co.nz
+creatives.as4x.tmcs.net
+creatives.livejasmin.com
+crispads.com
+criteo.com
+crowdgravity.com
+crtv.mate1.com
+crwdcntrl.net
+ctnetwork.hu
+cubics.com
+customad.cnn.com
+cyberbounty.com
+cybermonitor.com
+d.adroll.com
+dakic-ia-300.com
+danban.com
+dapper.net
+datashreddergold.com
+dbbsrv.com
+dc-storm.com
+de17a.com
+dealdotcom.com
+debtbusterloans.com
+decknetwork.net
+deloo.de
+demandbase.com
+demdex.net
+di1.shopping.com
+dialerporn.com
+didtheyreadit.com
+direct-xxx-access.com
+directaclick.com
+directleads.com
+directorym.com
+directtrack.com
+discountclick.com
+displayadsmedia.com
+dist.belnk.com
+dmtracker.com
+dmtracking.alibaba.com
+dmtracking2.alibaba.com
+dnads.directnic.com
+domaining.in
+domainsponsor.com
+domainsteam.de
+domdex.com
+doubleclick.com
+doubleclick.de
+doubleclick.net
+doublepimp.com
+drumcash.com
+dynamic.fmpub.net
+e-adimages.scrippsnetworks.com
+e-bannerx.com
+e-debtconsolidation.com
+e-m.fr
+e-n-t-e-r-n-e-x.com
+e-planning.net
+e.kde.cz
+eadexchange.com
+eas.almamedia.fi
+easyhits4u.com
+ebayadvertising.com
+ebocornac.com
+ebuzzing.com
+ecircle-ag.com
+eclick.vn
+ecoupons.com
+edgeio.com
+effectivemeasure.com
+effectivemeasure.net
+eiv.baidu.com
+elitedollars.com
+elitetoplist.com
+emarketer.com
+emediate.dk
+emediate.eu
+engine.espace.netavenir.com
+enginenetwork.com
+enoratraffic.com
+enquisite.com
+entercasino.com
+entrecard.s3.amazonaws.com
+eqads.com
+ero-advertising.com
+esellerate.net
+estat.com
+etahub.com
+etargetnet.com
+etracker.de
+eu-adcenter.net
+eu1.madsone.com
+eur.a1.yimg.com
+eurekster.com
+euro-linkindex.de
+euroclick.com
+euros4click.de
+eusta.de
+evergage.com
+evidencecleanergold.com
+ewebcounter.com
+exchange-it.com
+exchange.bg
+exchangead.com
+exchangeclicksonline.com
+exelator.com
+exit76.com
+exitexchange.com
+exitfuel.com
+exoclick.com
+exogripper.com
+experteerads.com
+exponential.com
+express-submit.de
+extractorandburner.com
+extreme-dm.com
+extremetracking.com
+eyeblaster.com
+eyereturn.com
+eyeviewads.com
+eyewonder.com
+ezula.com
+f5biz.com
+fast-adv.it
+fastclick.com
+fastclick.com.edgesuite.net
+fastclick.net
+fb-promotions.com
+fc.webmasterpro.de
+feedbackresearch.com
+feedjit.com
+ffxcam.fairfax.com.au
+fimc.net
+fimserve.com
+findcommerce.com
+findyourcasino.com
+fineclicks.com
+first.nova.cz
+firstlightera.com
+flashtalking.com
+fleshlightcash.com
+flexbanner.com
+flowgo.com
+flurry.com
+fonecta.leiki.com
+foo.cosmocode.de
+forex-affiliate.net
+fpctraffic.com
+fpctraffic2.com
+fragmentserv.iac-online.de
+free-banners.com
+freebanner.com
+freelogs.com
+freeonlineusers.com
+freepay.com
+freestats.com
+freestats.tv
+freewebcounter.com
+funklicks.com
+funpageexchange.com
+fusionads.net
+fusionquest.com
+fxclix.com
+fxstyle.net
+galaxien.com
+game-advertising-online.com
+gamehouse.com
+gamesites100.net
+gamesites200.com
+gamesitestop100.com
+gator.com
+gbanners.hornymatches.com
+gemius.pl
+geo.digitalpoint.com
+geobanner.adultfriendfinder.com
+geovisite.com
+getclicky.com
+globalismedia.com
+globaltakeoff.net
+globaltrack.com.invalid
+globe7.com
+globus-inter.com
+gmads.net
+go-clicks.de
+go-rank.de
+goingplatinum.com
+goldstats.com
+google-analytics.com
+googleadservices.com
+googlesyndication.com
+gostats.com
+gp.dejanews.com
+gpr.hu
+grafstat.ro
+grapeshot.co.uk
+greystripe.com
+gtop.ro
+gtop100.com
+gunggo.com
+harrenmedia.com
+harrenmedianetwork.com
+havamedia.net
+heias.com
+hentaicounter.com
+herbalaffiliateprogram.com
+hexusads.fluent.ltd.uk
+heyos.com
+hgads.com
+hidden.gogoceleb.com
+hightrafficads.com
+histats.com
+hit-parade.com
+hit.bg
+hit.ua
+hit.webcentre.lycos.co.uk
+hitbox.com
+hitcents.com
+hitfarm.com
+hitiz.com
+hitlist.ru
+hitlounge.com
+hitometer.com
+hits.europuls.eu
+hits.informer.com
+hits.puls.lv
+hits.theguardian.com
+hits4me.com
+hits4pay.com
+hitslink.com
+hittail.com
+hollandbusinessadvertising.nl
+homepageking.de
+hostedads.realitykings.com
+hotjar.com
+hotkeys.com
+hotlog.ru
+hotrank.com.tw
+hs-analytics.net
+htmlhubing.xyz
+httpool.com
+hurricanedigitalmedia.com
+hydramedia.com
+hyperbanner.net
+hypertracker.com
+i-clicks.net
+i.xx.openx.com
+i1img.com
+i1media.no
+ia.iinfo.cz
+iad.anm.co.uk
+iadnet.com
+iasds01.com
+iconadserver.com
+icptrack.com
+idcounter.com
+identads.com
+idot.cz
+idregie.com
+idtargeting.com
+ientrymail.com
+iesnare.com
+ifa.tube8live.com
+ilbanner.com
+ilead.itrack.it
+ilovecheating.com
+imageads.canoe.ca
+imagecash.net
+images-pw.secureserver.net
+images.v3.com
+imarketservices.com
+img.prohardver.hu
+imgpromo.easyrencontre.com
+imonitor.nethost.cz
+imprese.cz
+impressionmedia.cz
+impressionz.co.uk
+imrworldwide.com
+inboxdollars.com
+incentaclick.com
+indexstats.com
+indieclick.com
+industrybrains.com
+inetlog.ru
+infinite-ads.com
+infinityads.com
+infolinks.com
+information.com
+inringtone.com
+insightexpress.com
+insightexpressai.com
+inspectorclick.com
+instantmadness.com
+intelliads.com
+intellitxt.com
+interactive.forthnet.gr
+intergi.com
+internetfuel.com
+interreklame.de
+interstat.hu
+ip.ro
+ip193.cn
+iperceptions.com
+ipro.com
+ireklama.cz
+itfarm.com
+itop.cz
+its-that-easy.com
+itsptp.com
+jcount.com
+jinkads.de
+joetec.net
+js.users.51.la
+juicyads.com
+jumptap.com
+justrelevant.com
+justwebads.com
+k.iinfo.cz
+kanoodle.com
+keymedia.hu
+kindads.com
+kissmetrics.com
+kliks.nl
+kniverto.com
+komoona.com
+kompasads.com
+kontera.com
+kt-g.de
+ktu.sv2.biz
+lakequincy.com
+launchbit.com
+layer-ad.de
+layer-ads.de
+lbn.ru
+lct.salesforce.com
+lead-analytics.nl
+leadboltads.net
+leadclick.com
+leadingedgecash.com
+leadzupc.com
+levelrate.de
+lfstmedia.com
+liftdna.com
+ligatus.com
+ligatus.de
+lightningcast.net
+lightspeedcash.com
+link-booster.de
+link4ads.com
+linkadd.de
+linkbuddies.com
+linkexchange.com
+linkprice.com
+linkrain.com
+linkreferral.com
+links-ranking.de
+linkshighway.com
+linkstorms.com
+linkswaper.com
+linktarget.com
+liquidad.narrowcastmedia.com
+liveintent.com
+liverail.com
+loading321.com
+log.btopenworld.com
+logua.com
+lop.com
+lucidmedia.com
+lzjl.com
+m.webtrends.com
+m1.webstats4u.com
+m4n.nl
+mackeeperapp.mackeeper.com
+madclient.uimserv.net
+madisonavenue.com
+mads.cnet.com
+madvertise.de
+marchex.com
+market-buster.com
+marketing.888.com
+marketing.hearstmagazines.nl
+marketing.nyi.net
+marketing.osijek031.com
+marketingsolutions.yahoo.com
+maroonspider.com
+mas.sector.sk
+mastermind.com
+matchcraft.com
+mathtag.com
+max.i12.de
+maximumcash.com
+mbn.com.ua
+mbs.megaroticlive.com
+mbuyu.nl
+mdotm.com
+measuremap.com
+media-adrunner.mycomputer.com
+media-servers.net
+media.ftv-publicite.fr
+media.funpic.de
+media6degrees.com
+mediaarea.eu
+mediacharger.com
+mediadvertising.ro
+mediageneral.com
+mediamath.com
+mediamgr.ugo.com
+mediaplazza.com
+mediaplex.com
+mediascale.de
+mediatext.com
+mediax.angloinfo.com
+mediaz.angloinfo.com
+medleyads.com
+medyanetads.com
+megacash.de
+megago.com
+megastats.com
+megawerbung.de
+metaffiliation.com
+metanetwork.com
+methodcash.com
+metrics.windowsitpro.com
+mgid.com
+miarroba.com
+microstatic.pl
+microticker.com
+midnightclicking.com
+misstrends.com
+mixpanel.com
+mixtraffic.com
+mjxads.internet.com
+mlm.de
+mmismm.com
+mmtro.com
+moatads.com
+mobclix.com
+mocean.mobi
+moneyexpert.com
+monsterpops.com
+mopub.com
+mouseflow.com
+mpstat.us
+mr-rank.de
+mrskincash.com
+mtree.com
+musiccounter.ru
+muwmedia.com
+myaffiliateprogram.com
+mybloglog.com
+mycounter.ua
+mymoneymakingapp.com
+mypagerank.net
+mypagerank.ru
+mypowermall.com
+mystat-in.net
+mystat.pl
+mytop-in.net
+n69.com
+naiadsystems.com.invalid
+naj.sk
+namimedia.com
+nastydollars.com
+navigator.io
+navrcholu.cz
+nbjmp.com
+ndparking.com
+nedstat.com
+nedstat.nl
+nedstatbasic.net
+nedstatpro.net
+nend.net
+neocounter.neoworx-blog-tools.net
+neoffic.com
+net-filter.com
+netaffiliation.com
+netagent.cz
+netclickstats.com
+netcommunities.com
+netdirect.nl
+netincap.com
+netpool.netbookia.net
+netshelter.net
+network.business.com
+neudesicmediagroup.com
+newads.bangbros.com
+newbie.com
+newnet.qsrch.com
+newnudecash.com
+newopenx.detik.com
+newt1.adultadworld.com
+newt1.adultworld.com
+newtopsites.com
+ng3.ads.warnerbros.com
+ngs.impress.co.jp
+nitroclicks.com
+novem.pl
+nuggad.net
+numax.nu-1.com
+nuseek.com
+oas.benchmark.fr
+oas.foxnews.com
+oas.repubblica.it
+oas.roanoke.com
+oas.salon.com
+oas.toronto.com
+oas.uniontrib.com
+oas.villagevoice.com
+oascentral.businessweek.com
+oascentral.chicagobusiness.com
+oascentral.fortunecity.com
+oascentral.register.com
+oewa.at
+oewabox.at
+offerforge.com
+offermatica.com
+olivebrandresponse.com
+omniture.com
+onclasrv.com
+onclickads.net
+oneandonlynetwork.com
+onenetworkdirect.com
+onestat.com
+onestatfree.com
+online-metrix.net
+onlinecash.com
+onlinecashmethod.com
+onlinerewardcenter.com
+openad.tf1.fr
+openad.travelnow.com
+openads.friendfinder.com
+openads.org
+openx.angelsgroup.org.uk
+openx.blindferret.com
+opienetwork.com
+optimost.com
+optmd.com
+ordingly.com
+ota.cartrawler.com
+otto-images.developershed.com
+outbrain.com
+overture.com
+owebmoney.ru
+oxado.com
+oxcash.com
+oxen.hillcountrytexas.com
+p.adpdx.com
+pagead.l.google.com
+pagefair.com
+pagerank-ranking.de
+pagerank-submitter.de
+pagerank-suchmaschine.de
+pagerank-united.de
+pagerank4you.com
+pageranktop.com
+parse.ly.invalid
+parsely.com
+partage-facile.com
+partner-ads.com
+partner.pelikan.cz
+partner.topcities.com
+partnerad.l.google.com
+partnercash.de
+partners.priceline.com
+passion-4.net
+pay-ads.com
+paycounter.com
+paypopup.com
+payserve.com
+pbnet.ru
+pcash.imlive.com
+peep-auktion.de
+peer39.com
+pennyweb.com
+pepperjamnetwork.com
+percentmobile.com
+perfectaudience.com
+perfiliate.com
+performancerevenue.com
+performancerevenues.com
+performancing.com
+pgmediaserve.com
+pgpartner.com
+pheedo.com
+phoenix-adrunner.mycomputer.com
+phpadsnew.new.natuurpark.nl
+phpmyvisites.net
+picadmedia.com
+pillscash.com
+pimproll.com
+pixel.adsafeprotected.com
+pixel.jumptap.com
+pixel.redditmedia.com
+play4traffic.com
+playhaven.com
+plista.com
+plugrush.com
+pointroll.com
+pop-under.ru
+popads.net
+popub.com
+popunder.ru
+popup.msn.com
+popupmoney.com
+popupnation.com
+popups.infostart.com
+popuptraffic.com
+porngraph.com
+porntrack.com
+postrelease.com
+potenza.cz
+pr-star.de
+pr-ten.de
+praddpro.de
+prchecker.info
+precisioncounter.com
+predictad.com
+premium-offers.com
+primaryads.com
+primetime.net
+privatecash.com
+pro-advertising.com
+pro.i-doctor.co.kr
+proext.com
+profero.com
+projectwonderful.com
+promo.badoink.com
+promo.ulust.com
+promo1.webcams.nl
+promobenef.com
+promos.fling.com
+promote.pair.com
+promotion-campaigns.com
+pronetadvertising.com
+propellerads.com
+proranktracker.com
+proton-tm.com
+protraffic.com
+provexia.com
+prsitecheck.com
+psstt.com
+pub.chez.com
+pub.club-internet.fr
+pub.hardware.fr
+pub.realmedia.fr
+pubdirecte.com
+publicidad.elmundo.es
+pubmatic.com
+pubs.lemonde.fr
+pulse360.com
+q.azcentral.com
+qctop.com
+qnsr.com
+quantcast.com
+quantserve.com
+quarterserver.de
+questaffiliates.net
+quigo.com
+quinst.com
+quisma.com
+rad.msn.com
+radar.cedexis.com
+radarurl.com
+radiate.com
+rampidads.com
+rank-master.com
+rank-master.de
+rankchamp.de
+ranking-charts.de
+ranking-hits.de
+ranking-id.de
+ranking-links.de
+ranking-liste.de
+ranking-street.de
+rankingchart.de
+rankingscout.com
+rankyou.com
+rapidcounter.com
+rate.ru
+ratings.lycos.com
+rb1.design.ru
+re-directme.com
+reachjunction.com
+reactx.com
+readserver.net
+realcastmedia.com
+realclix.com
+realmedia-a800.d4p.net
+realtechnetwork.com
+realtracker.com
+reduxmedia.com
+reduxmediagroup.com
+reedbusiness.com.invalid
+referralware.com
+regnow.com
+reinvigorate.net
+reklam.rfsl.se
+reklama.mironet.cz
+reklama.reflektor.cz
+reklamcsere.hu
+reklame.unwired-i.net
+reklamer.com.ua
+relevanz10.de
+relmaxtop.com
+remotead.cnet.com
+republika.onet.pl
+retargeter.com
+revenue.net
+revenuedirect.com
+revsci.net
+revstats.com
+richmails.com
+richmedia.yimg.com
+richwebmaster.com
+rightstats.com
+rlcdn.com
+rle.ru
+rmads.msn.com
+rmedia.boston.com
+roar.com
+robotreplay.com
+roia.biz
+rok.com.com
+rose.ixbt.com
+rotabanner.com
+roxr.net
+rtbpop.com
+rtbpopd.com
+ru-traffic.com
+ru4.com
+rubiconproject.com
+s.adroll.com
+s2d6.com
+sageanalyst.net
+sail-horizon.com
+samsungacr.com
+samsungads.com
+sbx.pagesjaunes.fr
+scambiobanner.aruba.it
+scanscout.com
+scopelight.com
+scorecardresearch.com
+scratch2cash.com
+scripte-monster.de
+searchfeast.com
+searchmarketing.com
+searchramp.com
+secure.webconnect.net
+sedoparking.com
+sedotracker.com
+seeq.com.invalid
+sensismediasmart.com.au
+seo4india.com
+serv0.com
+servedby-buysellads.com
+servedbyadbutler.com
+servedbyopenx.com
+services.hearstmags.com
+serving-sys.com
+sexaddpro.de
+sexadvertentiesite.nl
+sexcounter.com
+sexinyourcity.com
+sexlist.com
+sextracker.com
+sexystat.com
+shareadspace.com
+shareasale.com
+sharepointads.com
+sher.index.hu
+shinystat.com
+shinystat.it
+shoppingads.com
+siccash.com
+sidebar.angelfire.com
+sinoa.com
+sitemeter.com
+sitestat.com
+sixsigmatraffic.com
+skimresources.com
+skylink.vn
+slickaffiliate.com
+slopeaota.com
+smart4ads.com
+smartadserver.com
+smowtion.com
+snapads.com
+snoobi.com
+socialspark.com
+softclick.com.br
+spacash.com
+sparkstudios.com
+specificmedia.co.uk
+specificpop.com
+spezialreporte.de
+spinbox.techtracker.com
+spinbox.versiontracker.com
+sponsorads.de
+sponsorpro.de
+sponsors.thoughtsmedia.com
+spot.fitness.com
+spotxchange.com
+spylog.com
+spywarelabs.com
+spywarenuker.com
+spywords.com
+srwww1.com
+starffa.com
+start.freeze.com
+stat.cliche.se
+stat.dealtime.com
+stat.dyna.ultraweb.hu
+stat.pl
+stat.su
+stat.tudou.com
+stat.webmedia.pl
+stat.zenon.net
+stat24.com
+stat24.meta.ua
+statcounter.com
+static.fmpub.net
+static.itrack.it
+staticads.btopenworld.com
+statistik-gallup.net
+statm.the-adult-company.com
+stats.blogger.com
+stats.cts-bv.nl
+stats.directnic.com
+stats.hyperinzerce.cz
+stats.mirrorfootball.co.uk
+stats.multiup.org
+stats.olark.com
+stats.suite101.com
+stats.surfaid.ihost.com
+stats.townnews.com
+stats.unwired-i.net
+stats.wordpress.com
+stats.x14.eu
+stats4all.com
+statsie.com
+statxpress.com
+steelhouse.com
+steelhousemedia.com
+stickyadstv.com
+suavalds.com
+subscribe.hearstmags.com
+sugoicounter.com
+sumo.com
+sumome.com
+superclix.de
+superstats.com
+supertop.ru
+supertop100.com
+suptullog.com
+surfmusik-adserver.de
+swan-swan-goose.com
+swissadsolutions.com
+swordfishdc.com
+sx.trhnt.com
+t.insigit.com
+t.pusk.ru
+taboola.com
+tacoda.net
+tagular.com
+tailsweep.co.uk
+tailsweep.com
+tailsweep.se
+takru.com
+tangerinenet.biz
+tapad.com
+targad.de
+targetingnow.com
+targetnet.com
+targetpoint.com
+tatsumi-sys.jp
+tcads.net
+teads.tv
+techclicks.net
+teenrevenue.com
+teliad.de
+text-link-ads.com
+textad.sexsearch.com
+textads.biz
+textads.opera.com
+textlinks.com
+tfag.de
+theadhost.com
+theads.me
+thebugs.ws
+thecounter.com
+therapistla.com
+therichkids.com
+thrnt.com
+thruport.com
+tinybar.com
+tizers.net
+tlvmedia.com
+tntclix.co.uk
+top-casting-termine.de
+top-site-list.com
+top.list.ru
+top.mail.ru
+top.proext.com
+top100-images.rambler.ru
+top100.mafia.ru
+top123.ro
+top20.com.invalid
+top20free.com
+top90.ro
+topbarh.box.sk
+topblogarea.se
+topbucks.com
+topforall.com
+topgamesites.net
+toplist.cz
+toplist.pornhost.com
+toplista.mw.hu
+toplistcity.com
+topmmorpgsites.com.invalid
+topping.com.ua
+toprebates.com
+topsafelist.net
+topsearcher.com
+topsir.com
+topsite.lv
+topsites.com.br
+topstats.com
+totemcash.com
+touchclarity.com
+touchclarity.natwest.com
+tour.brazzers.com
+tpnads.com
+track.adform.net
+track.anchorfree.com
+track.gawker.com
+trackalyzer.com
+tracker.icerocket.com
+tracker.marinsm.com
+tracking.crunchiemedia.com
+tracking.gajmp.com
+tracking.internetstores.de
+tracking.yourfilehost.com
+tracking101.com
+trackingsoft.com
+trackmysales.com
+tradeadexchange.com
+tradedoubler.com
+traffic-exchange.com
+traffic.liveuniversenetwork.com
+trafficadept.com
+trafficcdn.liveuniversenetwork.com
+trafficfactory.biz
+trafficholder.com
+traffichunt.com
+trafficjunky.net
+trafficleader.com
+trafficsecrets.com
+trafficspaces.net
+trafficstrategies.com
+trafficswarm.com
+traffictrader.net
+trafficz.com
+trafficz.net
+traffiq.com
+trafic.ro
+travis.bosscasinos.com
+trekblue.com
+trekdata.com
+trendcounter.com
+trendmd.com
+trhunt.com
+tribalfusion.com
+trix.net
+truehits.net
+truehits1.gits.net.th
+truehits2.gits.net.th
+tsms-ad.tsms.com
+tubemogul.com
+turn.com
+tvmtracker.com
+twittad.com
+tyroo.com
+uarating.com
+ukbanners.com
+ultramercial.com
+unanimis.co.uk
+untd.com
+updated.com
+urlcash.net
+us.a1.yimg.com
+usapromotravel.com
+usmsad.tom.com
+utarget.co.uk
+utils.mediageneral.net
+v1.cnzz.com
+validclick.com
+valuead.com
+valueclick.com
+valueclickmedia.com
+valuecommerce.com
+valuesponsor.com
+veille-referencement.com
+ventivmedia.com
+vericlick.com
+vertadnet.com
+veruta.com
+vervewireless.com
+vibrantmedia.com
+video-stats.video.google.com
+videoegg.com
+view4cash.de
+viewpoint.com
+visistat.com
+visit.webhosting.yahoo.com
+visitbox.de
+visual-pagerank.fr
+visualrevenue.com
+voicefive.com
+vpon.com
+vrs.cz
+vs.tucows.com
+vungle.com
+warlog.ru
+wdads.sx.atl.publicus.com
+web-stat.com
+web.informer.com
+web2.deja.com
+webads.co.nz
+webads.nl
+webangel.ru
+webcash.nl
+webcounter.cz
+webcounter.goweb.de
+webgains.com
+webmaster-partnerprogramme24.de
+webmasterplan.com
+webmasterplan.de
+weborama.fr
+webpower.com
+webreseau.com
+webseoanalytics.com
+websponsors.com
+webstat.channel4.com
+webstat.com
+webstat.net
+webstats4u.com
+webtrackerplus.com
+webtraffic.se
+webtraxx.de
+webtrendslive.com
+werbung.meteoxpress.com
+wetrack.it
+whaleads.com
+whenu.com
+whispa.com
+whoisonline.net
+wholesaletraffic.info
+widespace.com
+widgetbucks.com
+wikia-ads.wikia.com
+window.nixnet.cz
+wintricksbanner.googlepages.com
+witch-counter.de
+wlmarketing.com
+wmirk.ru
+wonderlandads.com
+wondoads.de
+woopra.com
+worldwide-cash.net
+wtlive.com
+www-banner.chat.ru
+www-google-analytics.l.google.com
+www.banner-link.com.br
+www.dnps.com
+www.kaplanindex.com
+www.money4exit.de
+www.photo-ads.co.uk
+www1.gto-media.com
+www8.glam.com
+wwwpromoter.com
+x-traceur.com
+x6.yakiuchi.com
+xchange.ro
+xclicks.net
+xertive.com
+xg4ken.com
+xiti.com
+xplusone.com
+xponsor.com
+xq1.net
+xrea.com
+xtendmedia.com
+xtremetop100.com
+xxxcounter.com
+xxxmyself.com
+y.ibsys.com
+yab-adimages.s3.amazonaws.com
+yabuka.com
+yadro.ru
+yesads.com
+yesadvertising.com
+yieldads.com
+yieldlab.net
+yieldmanager.com
+yieldmanager.net
+yieldmo.com
+yieldtraffic.com
+yoc.mobi
+yoggrt.com
+z5x.net
+zangocash.com
+zanox-affiliate.de
+zanox.com
+zantracker.com
+zedo.com
+zencudo.co.uk
+zenkreka.com
+zenzuu.com
+zeus.developershed.com
+zeusclicks.com
+zintext.com
+zmedia.com
+zv1.november-lax.com
+
diff --git a/app/src/main/res/raw/licenses_3rd_party.md b/app/src/main/res/raw/licenses_3rd_party.md
new file mode 100644
index 000000000..fce02fc68
--- /dev/null
+++ b/app/src/main/res/raw/licenses_3rd_party.md
@@ -0,0 +1,14 @@
+* NetCipher
+
+
+* ButterKnife
+
+
+* ShiftColorPicker
+
+
+* Android Support Library
+
+
+* Android Design Library
+
diff --git a/app/src/main/res/raw/maintainers.md b/app/src/main/res/raw/maintainers.md
new file mode 100644
index 000000000..999f10735
--- /dev/null
+++ b/app/src/main/res/raw/maintainers.md
@@ -0,0 +1,5 @@
+* Gregor Santner (gsantner)
+~° http://gsantner.net
+
+* Paul Schaub (vanitasvitae)
+~° https://github.com/vanitasvitae
diff --git a/app/src/main/res/raw/podlist.json b/app/src/main/res/raw/podlist.json
new file mode 100644
index 000000000..817418e54
--- /dev/null
+++ b/app/src/main/res/raw/podlist.json
@@ -0,0 +1,1531 @@
+{
+ "pods": [
+ {
+ "score": 20,
+ "podUrls": [
+ {"host": "joindiaspora.com"},
+ {
+ "protocol": "http",
+ "port": 80,
+ "host": "diasporaaqmjixh5.onion"
+ }
+ ],
+ "name": "JoinDiaspora*",
+ "mainLangs": ["en"],
+ "id": 38077
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.geraspora.de"}],
+ "name": "Geraspora",
+ "mainLangs": ["de"],
+ "id": 24783
+ },
+ {
+ "score": 20,
+ "podUrls": [
+ {"host": "diasp.org"},
+ {"host": "diasporgc3d32vv4.onion"}
+ ],
+ "name": "diasporg*",
+ "mainLangs": [],
+ "id": 12688
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "framasphere.org"}],
+ "name": "Framasphere",
+ "mainLangs": [],
+ "id": 38776
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "nerdpol.ch"}],
+ "name": "nerdpol.ch*",
+ "mainLangs": ["de"],
+ "id": 17343
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "despora.de"}],
+ "name": "Despora*",
+ "mainLangs": ["de"],
+ "id": 6695
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.zone"}],
+ "name": "zone",
+ "mainLangs": [],
+ "id": 41976
+ },
+ {
+ "score": 20,
+ "podUrls": [
+ {"host": "sechat.org"},
+ {
+ "protocol": "http",
+ "port": 80,
+ "host": "sechatqpscuj2npx.onion"
+ }
+ ],
+ "name": "Sechat*",
+ "mainLangs": [],
+ "id": 6524
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.orkz.net"}],
+ "name": "orkz.net",
+ "mainLangs": [],
+ "id": 41604
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "poddery.com"}],
+ "name": "poddery.com",
+ "mainLangs": [],
+ "id": 36667
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.disroot.org"}],
+ "name": "disroot.org",
+ "mainLangs": [],
+ "id": 39828
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diasporabr.com.br"}],
+ "name": "diasporabr.com.br",
+ "mainLangs": [],
+ "id": 18248
+ },
+ {
+ "score": -22,
+ "podUrls": [{"host": "diaspora-fr.org"}],
+ "name": "diaspora-fr.org",
+ "mainLangs": [],
+ "id": 13909
+ },
+ {
+ "score": -22,
+ "podUrls": [{"host": "diasp.de"}],
+ "name": "diasp.de",
+ "mainLangs": ["de"],
+ "id": 46864
+ },
+ {
+ "score": 99,
+ "podUrls": [{"host": "diaspora.com.ar"}],
+ "name": "com.ar",
+ "mainLangs": [],
+ "id": 22994
+ },
+ {
+ "score": -22,
+ "podUrls": [{"host": "diasp.eu"}],
+ "name": "diasp.eu",
+ "mainLangs": [],
+ "id": 46423
+ },
+ {
+ "podUrls": [{"host": "berdaguermontes.eu"}],
+ "name": "berdaguermontes.eu",
+ "mainLangs": [],
+ "id": 25432
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "berlinspora.de"}],
+ "name": "berlinspora.de",
+ "mainLangs": ["de"],
+ "id": 38327
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "community.kanalinseln.de"}],
+ "name": "community.kanalinseln.de",
+ "mainLangs": ["de"],
+ "id": 37468
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "d.consumium.org"}],
+ "name": "d.consumium.org",
+ "mainLangs": [],
+ "id": 2107
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "dia.manuelbichler.at"}],
+ "name": "dia.manuelbichler.at",
+ "mainLangs": ["de"],
+ "id": 14817
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "dia.myocastor.de"}],
+ "name": "dia.myocastor.de",
+ "mainLangs": ["de"],
+ "id": 7190
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diapod.net"}],
+ "name": "diapod.net",
+ "mainLangs": [],
+ "id": 43611
+ },
+ {
+ "podUrls": [{"host": "diapod.org"}],
+ "name": "diapod.org",
+ "mainLangs": [],
+ "id": 13926
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diasp.ca"}],
+ "name": "diasp.ca",
+ "mainLangs": [],
+ "id": 40056
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diasp.cz"}],
+ "name": "diasp.cz",
+ "mainLangs": [],
+ "id": 21452
+ },
+ {
+ "podUrls": [{"host": "diasp.eu.com"}],
+ "name": "diasp.eu.com",
+ "mainLangs": [],
+ "id": 4343
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diasp.nl"}],
+ "name": "diasp.nl",
+ "mainLangs": ["nl"],
+ "id": 33262
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspod.de"}],
+ "name": "diaspod.de",
+ "mainLangs": ["de"],
+ "id": 2553
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.alfter.us"}],
+ "name": "alfter.us",
+ "mainLangs": ["en"],
+ "id": 13402
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.bohramt.de"}],
+ "name": "bohramt.de",
+ "mainLangs": ["de"],
+ "id": 8209
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.deadhexagon.com"}],
+ "name": "deadhexagon.com",
+ "mainLangs": [],
+ "id": 16721
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.digi-merc.org"}],
+ "name": "digi-merc.org",
+ "mainLangs": [],
+ "id": 13165
+ },
+ {
+ "podUrls": [{"host": "dorf-post.de"}],
+ "name": "dorf-post.de",
+ "mainLangs": ["de"],
+ "id": 43137
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.espiritolivre.org"}],
+ "name": "espiritolivre.org",
+ "mainLangs": [],
+ "id": 8690
+ },
+ {
+ "podUrls": [{"host": "diaspora.horwood.biz"}],
+ "name": "horwood.biz",
+ "mainLangs": [],
+ "id": 12684
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.hzsogood.net"}],
+ "name": "hzsogood.net",
+ "mainLangs": [],
+ "id": 3651
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.kapper.net"}],
+ "name": "kapper.net",
+ "mainLangs": [],
+ "id": 40765
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.koehn.com"}],
+ "name": "koehn.com",
+ "mainLangs": [],
+ "id": 25812
+ },
+ {
+ "podUrls": [{"host": "diaspora.kosebamse.com"}],
+ "name": "kosebamse.com",
+ "mainLangs": [],
+ "id": 15474
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.lebarjack.com"}],
+ "name": "lebarjack.com",
+ "mainLangs": [],
+ "id": 36540
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.microdata.co.uk"}],
+ "name": "microdata.co.uk",
+ "mainLangs": ["en"],
+ "id": 8704
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.moosje.nl"}],
+ "name": "moosje.nl",
+ "mainLangs": ["nl"],
+ "id": 45461
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.net.gr"}],
+ "name": "net.gr",
+ "mainLangs": [],
+ "id": 9863
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.permutationsofchaos.com"}],
+ "name": "permutationsofchaos.com",
+ "mainLangs": [],
+ "id": 1513
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.pimpmypony.eu"}],
+ "name": "pimpmypony.eu",
+ "mainLangs": [],
+ "id": 38497
+ },
+ {
+ "podUrls": [{"host": "diaspora.pingupod.de"}],
+ "name": "pingupod.de",
+ "mainLangs": ["de"],
+ "id": 10135
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.podzimek.org"}],
+ "name": "podzimek.org",
+ "mainLangs": [],
+ "id": 12139
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.poleni.com"}],
+ "name": "poleni.com",
+ "mainLangs": [],
+ "id": 1154
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.psyco.fr"}],
+ "name": "psyco.fr",
+ "mainLangs": ["fr"],
+ "id": 31397
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.raven-ip.com"}],
+ "name": "raven-ip.com",
+ "mainLangs": [],
+ "id": 17410
+ },
+ {
+ "podUrls": [{"host": "diaspora.retrodigital.net"}],
+ "name": "retrodigital.net",
+ "mainLangs": [],
+ "id": 24640
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.soh.re"}],
+ "name": "soh.re",
+ "mainLangs": [],
+ "id": 39183
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.subsignal.org"}],
+ "name": "subsignal.org",
+ "mainLangs": [],
+ "id": 22108
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.trash-talk.de"}],
+ "name": "trash-talk.de",
+ "mainLangs": ["de"],
+ "id": 46672
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.u4u.org"}],
+ "name": "u4u.org",
+ "mainLangs": [],
+ "id": 9278
+ },
+ {
+ "podUrls": [{"host": "diaspora.unixcorn.org"}],
+ "name": "unixcorn.org",
+ "mainLangs": [],
+ "id": 43416
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diasporabrazil.org"}],
+ "name": "diasporabrazil.org",
+ "mainLangs": [],
+ "id": 42422
+ },
+ {
+ "score": 51,
+ "podUrls": [{"host": "diasporapr.tk"}],
+ "name": "diasporapr.tk",
+ "mainLangs": [],
+ "id": 12020
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diasporing.ch"}],
+ "name": "Diasporing.ch",
+ "mainLangs": ["de"],
+ "id": 8471
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "distributed.chat"}],
+ "name": "distributed.chat",
+ "mainLangs": [],
+ "id": 43459
+ },
+ {
+ "score": -17,
+ "podUrls": [{"host": "espora.com.es"}],
+ "name": "espora.com.es",
+ "mainLangs": [],
+ "id": 11247
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "espora.social"}],
+ "name": "espora.social",
+ "mainLangs": [],
+ "id": 5948
+ },
+ {
+ "podUrls": [{"host": "failure.net"}],
+ "name": "failure.net",
+ "mainLangs": [],
+ "id": 41690
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "flokk.no"}],
+ "name": "flokk.no",
+ "mainLangs": [],
+ "id": 45658
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "freehuman.fr"}],
+ "name": "freehuman.fr",
+ "mainLangs": ["fr"],
+ "id": 21531
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "iliketoast.net"}],
+ "name": "iliketoast.net",
+ "mainLangs": [],
+ "id": 23287
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "jons.gr"}],
+ "name": "jons.gr",
+ "mainLangs": [],
+ "id": 41539
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "kapok.se"}],
+ "name": "kapok.se",
+ "mainLangs": [],
+ "id": 32786
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "karmasphe.re"}],
+ "name": "karmasphe.re",
+ "mainLangs": [],
+ "id": 20368
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "laba.mba"}],
+ "name": "laba.mba",
+ "mainLangs": [],
+ "id": 32393
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "liberdade.digital"}],
+ "name": "liberdade.digital",
+ "mainLangs": [],
+ "id": 40958
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "librenet.co.za"}],
+ "name": "librenet.co.za",
+ "mainLangs": [],
+ "id": 14862
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "librenet.gr"}],
+ "name": "librenet.gr",
+ "mainLangs": [],
+ "id": 27582
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "misamigos.online"}],
+ "name": "misamigos.online",
+ "mainLangs": [],
+ "id": 16366
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "mondiaspora.net"}],
+ "name": "mondiaspora.net",
+ "mainLangs": [],
+ "id": 3365
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "networkwizard.de"}],
+ "name": "networkwizard.de",
+ "mainLangs": ["de"],
+ "id": 47458
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "nx-pod.de"}],
+ "name": "nx-pod.de",
+ "mainLangs": ["de"],
+ "id": 14678
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pe.spbstu.ru"}],
+ "name": "pe.spbstu.ru",
+ "mainLangs": ["ru"],
+ "id": 39808
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.4ray.co"}],
+ "name": "4ray.co",
+ "mainLangs": [],
+ "id": 2522
+ },
+ {
+ "podUrls": [{"host": "pod.8n1.org"}],
+ "name": "8n1.org",
+ "mainLangs": [],
+ "id": 12504
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.alterworld.info"}],
+ "name": "alterworld.info",
+ "mainLangs": [],
+ "id": 1176
+ },
+ {
+ "podUrls": [{"host": "pod.asap-soft.com"}],
+ "name": "asap-soft.com",
+ "mainLangs": [],
+ "id": 49271
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.cannyfoxx.me"}],
+ "name": "cannyfoxx.me",
+ "mainLangs": [],
+ "id": 3505
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.cyberdungeon.de"}],
+ "name": "cyberdungeon.de",
+ "mainLangs": ["de"],
+ "id": 887
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.dapor.net"}],
+ "name": "dapor.net",
+ "mainLangs": [],
+ "id": 45578
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.datenknoten.me"}],
+ "name": "datenknoten.me",
+ "mainLangs": [],
+ "id": 34173
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.diaspora.software"}],
+ "name": "software",
+ "mainLangs": [],
+ "id": 33657
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.dirkomatik.de"}],
+ "name": "dirkomatik.de",
+ "mainLangs": ["de"],
+ "id": 47570
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.gedankenausbruch.com"}],
+ "name": "gedankenausbruch.com",
+ "mainLangs": [],
+ "id": 6893
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.gleisnetze.de"}],
+ "name": "gleisnetze.de",
+ "mainLangs": ["de"],
+ "id": 31471
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.goodsharing.at"}],
+ "name": "goodsharing.at",
+ "mainLangs": ["de"],
+ "id": 36615
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.hashtagueule.fr"}],
+ "name": "hashtagueule.fr",
+ "mainLangs": ["fr"],
+ "id": 10296
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.hoizi.net"}],
+ "name": "hoizi.net",
+ "mainLangs": [],
+ "id": 27479
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.itabs.nl"}],
+ "name": "itabs.nl",
+ "mainLangs": ["nl"],
+ "id": 15016
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.jpope.org"}],
+ "name": "jpope.org",
+ "mainLangs": [],
+ "id": 11674
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.liebeleu.de"}],
+ "name": "liebeleu.de",
+ "mainLangs": ["de"],
+ "id": 29566
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.nomorestars.com"}],
+ "name": "nomorestars.com",
+ "mainLangs": [],
+ "id": 31958
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.ponk.pink"}],
+ "name": "ponk.pink",
+ "mainLangs": [],
+ "id": 14849
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.promedol.com"}],
+ "name": "promedol.com",
+ "mainLangs": [],
+ "id": 21338
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.psynet.su"}],
+ "name": "psynet.su",
+ "mainLangs": [],
+ "id": 16045
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.roocita.com"}],
+ "name": "roocita.com",
+ "mainLangs": [],
+ "id": 47269
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.sertelon.fr"}],
+ "name": "sertelon.fr",
+ "mainLangs": ["fr"],
+ "id": 15234
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.storel.li"}],
+ "name": "storel.li",
+ "mainLangs": [],
+ "id": 33181
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.tchncs.de"}],
+ "name": "tchncs.de",
+ "mainLangs": ["de"],
+ "id": 20115
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.thomasdalichow.de"}],
+ "name": "thomasdalichow.de",
+ "mainLangs": ["de"],
+ "id": 12001
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.volt.io"}],
+ "name": "volt.io",
+ "mainLangs": [],
+ "id": 19139
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "podbay.net"}],
+ "name": "podbay.net",
+ "mainLangs": [],
+ "id": 16270
+ },
+ {
+ "podUrls": [{"host": "podricing.pw"}],
+ "name": "podricing.pw",
+ "mainLangs": [],
+ "id": 6398
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pubpod.alqualonde.org"}],
+ "name": "pubpod.alqualonde.org",
+ "mainLangs": [],
+ "id": 26555
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "revreso.de"}],
+ "name": "revreso.de",
+ "mainLangs": ["de"],
+ "id": 41788
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "ruhrspora.de"}],
+ "name": "ruhrspora.de",
+ "mainLangs": ["de"],
+ "id": 46198
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "russiandiaspora.org"}],
+ "name": "russiandiaspora.org",
+ "mainLangs": [],
+ "id": 22166
+ },
+ {
+ "podUrls": [{"host": "shrekislove.us"}],
+ "name": "shrekislove.us",
+ "mainLangs": ["en"],
+ "id": 39003
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "social.acclaro.digital"}],
+ "name": "acclaro.digital",
+ "mainLangs": [],
+ "id": 33853
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "social.baldr.io"}],
+ "name": "baldr.io",
+ "mainLangs": [],
+ "id": 7781
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "social.daxbau.net"}],
+ "name": "daxbau.net",
+ "mainLangs": [],
+ "id": 37517
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "social.elaon.de"}],
+ "name": "elaon.de",
+ "mainLangs": ["de"],
+ "id": 30112
+ },
+ {
+ "podUrls": [{"host": "social.lanham.id.au"}],
+ "name": "lanham.id.au",
+ "mainLangs": [],
+ "id": 48421
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "social.mbuto.me"}],
+ "name": "mbuto.me",
+ "mainLangs": [],
+ "id": 18258
+ },
+ {
+ "score": 95,
+ "podUrls": [{"host": "socializer.cc"}],
+ "name": "socializer.cc",
+ "mainLangs": [],
+ "id": 30584
+ },
+ {
+ "podUrls": [{"host": "spora.zone"}],
+ "name": "spora.zone",
+ "mainLangs": [],
+ "id": 24735
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "subvillage.de"}],
+ "name": "subvillage.de",
+ "mainLangs": ["de"],
+ "id": 29359
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "sysad.org"}],
+ "name": "sysad.org",
+ "mainLangs": [],
+ "id": 45830
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "teki.be"}],
+ "name": "teki.be",
+ "mainLangs": [],
+ "id": 5276
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "therealtalk.org"}],
+ "name": "therealtalk.org",
+ "mainLangs": [],
+ "id": 26786
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "thinkopen.net"}],
+ "name": "thinkopen.net",
+ "mainLangs": [],
+ "id": 7366
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "tippentappen.de"}],
+ "name": "tippentappen.de",
+ "mainLangs": ["de"],
+ "id": 622
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "whatsnewz.com"}],
+ "name": "whatsnewz.com",
+ "mainLangs": [],
+ "id": 5842
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "wk3.org"}],
+ "name": "wk3.org",
+ "mainLangs": [],
+ "id": 39292
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "www.datataffel.dk"}],
+ "name": "datataffel.dk",
+ "mainLangs": [],
+ "id": 35984
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "www.diasporaix.de"}],
+ "name": "diasporaix.de",
+ "mainLangs": ["de"],
+ "id": 26219
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.hofud.com"}],
+ "name": "hofud.com",
+ "mainLangs": [],
+ "id": 10983
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspora.softwarelivre.org"}],
+ "name": "softwarelivre.org",
+ "mainLangs": [],
+ "id": 33510
+ },
+ {
+ "score": 39,
+ "podUrls": [{"host": "confetticake.club"}],
+ "name": "confetticake.club",
+ "mainLangs": [],
+ "id": 41623
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "diaspote.org"}],
+ "name": "diaspote.org",
+ "mainLangs": [],
+ "id": 33317
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "pod.userzap.de"}],
+ "name": "userzap.de",
+ "mainLangs": ["de"],
+ "id": 12816
+ },
+ {
+ "score": 20,
+ "podUrls": [{"host": "www.vodeoo.com"}],
+ "name": "vodeoo.com",
+ "mainLangs": [],
+ "id": 5856
+ },
+ {
+ "podUrls": [{"host": "diaspora.punkbeer.me"}],
+ "name": "punkbeer.me",
+ "mainLangs": [],
+ "id": 16609
+ },
+ {
+ "podUrls": [{"host": "ingtech.net"}],
+ "name": "ingtech.net",
+ "mainLangs": [],
+ "id": 13051
+ },
+ {
+ "podUrls": [{"host": "mmkr.co"}],
+ "name": "mmkr.co",
+ "mainLangs": [],
+ "id": 30787
+ },
+ {
+ "podUrls": [{"host": "pod.kneedrag.org"}],
+ "name": "kneedrag.org",
+ "mainLangs": [],
+ "id": 24936
+ },
+ {
+ "podUrls": [{"host": "spyurk.am"}],
+ "name": "Spyurk",
+ "mainLangs": [],
+ "id": 24921
+ },
+ {
+ "podUrls": [{"host": "pod.mew.cat"}],
+ "name": "mew.cat",
+ "mainLangs": [],
+ "id": 10609
+ },
+ {
+ "podUrls": [{"host": "dfhz.tk"}],
+ "name": "dfhz.tk",
+ "mainLangs": [],
+ "id": 17389
+ },
+ {
+ "podUrls": [{"host": "netiz.in"}],
+ "name": "netiz.in",
+ "mainLangs": ["in"],
+ "id": 26206
+ },
+ {
+ "score": 98,
+ "podUrls": [{"host": "pod1.orobouros.net"}],
+ "name": "pod1.orobouros.net",
+ "mainLangs": [],
+ "id": 41891
+ },
+ {
+ "podUrls": [{"host": "witchcraft.ml"}],
+ "name": "witchcraft.ml",
+ "mainLangs": [],
+ "id": 37791
+ },
+ {
+ "score": 40,
+ "podUrls": [{"host": "libellula.criptica.org"}],
+ "name": "libellula.criptica.org",
+ "mainLangs": [],
+ "id": 36850
+ },
+ {
+ "podUrls": [{"host": "pod.datamol.org"}],
+ "name": "datamol.org",
+ "mainLangs": [],
+ "id": 31476
+ },
+ {
+ "podUrls": [{"host": "social.berdaguermontes.eu"}],
+ "name": "berdaguermontes.eu",
+ "mainLangs": [],
+ "id": 47620
+ },
+ {
+ "podUrls": [{"host": "mh8.lat-clan.fr"}],
+ "name": "mh8.lat-clan.fr",
+ "mainLangs": ["fr"],
+ "id": 25711
+ },
+ {
+ "score": 55,
+ "podUrls": [{"host": "dp.lumy.me"}],
+ "name": "dp.lumy.me",
+ "mainLangs": [],
+ "id": 37629
+ },
+ {
+ "podUrls": [{"host": "social.souvenirfromlife.fr"}],
+ "name": "souvenirfromlife.fr",
+ "mainLangs": ["fr"],
+ "id": 22180
+ },
+ {
+ "podUrls": [{"host": "diaspora.mokrynskyi.com"}],
+ "name": "mokrynskyi.com",
+ "mainLangs": [],
+ "id": 24363
+ },
+ {
+ "score": 40,
+ "podUrls": [{"host": "hub.transition-regensburg.de"}],
+ "name": "hub.transition-regensburg.de",
+ "mainLangs": ["de"],
+ "id": 3300
+ },
+ {
+ "podUrls": [{"host": "co.zy.lc"}],
+ "name": "co.zy.lc",
+ "mainLangs": [],
+ "id": 31291
+ },
+ {
+ "podUrls": [{"host": "diaspora.treefish.org"}],
+ "name": "treefish.org",
+ "mainLangs": [],
+ "id": 37210
+ },
+ {
+ "podUrls": [{"host": "diaspora.bke.ro"}],
+ "name": "bke.ro",
+ "mainLangs": [],
+ "id": 43350
+ },
+ {
+ "podUrls": [{"host": "pod.sd.vc"}],
+ "name": "sd.vc",
+ "mainLangs": [],
+ "id": 3302
+ },
+ {
+ "podUrls": [{"host": "diaspora.cxx.rocks"}],
+ "name": "cxx.rocks",
+ "mainLangs": [],
+ "id": 20172
+ },
+ {
+ "podUrls": [{"host": "social.homenet.org"}],
+ "name": "homenet.org",
+ "mainLangs": [],
+ "id": 48291
+ },
+ {
+ "podUrls": [{"host": "social.cigliola.com"}],
+ "name": "cigliola.com",
+ "mainLangs": [],
+ "id": 31082
+ },
+ {
+ "podUrls": [{"host": "friaspora.tk"}],
+ "name": "friaspora.tk",
+ "mainLangs": [],
+ "id": 32711
+ },
+ {
+ "podUrls": [{"host": "pod.lasserh.dk"}],
+ "name": "lasserh.dk",
+ "mainLangs": [],
+ "id": 29141
+ },
+ {
+ "podUrls": [{"host": "hubz.tk"}],
+ "name": "hubz.tk",
+ "mainLangs": [],
+ "id": 31332
+ },
+ {
+ "podUrls": [{"host": "hubzilla.kosebamse.com"}],
+ "name": "hubzilla.kosebamse.com",
+ "mainLangs": [],
+ "id": 19953
+ },
+ {
+ "podUrls": [{"host": "driaans.nl"}],
+ "name": "driaans.nl",
+ "mainLangs": ["nl"],
+ "id": 6488
+ },
+ {
+ "podUrls": [{"host": "dispersio.us"}],
+ "name": "dispersio.us",
+ "mainLangs": ["en"],
+ "id": 9
+ },
+ {
+ "podUrls": [{"host": "go.lookbehind.eu"}],
+ "name": "go.lookbehind.eu",
+ "mainLangs": [],
+ "id": 39260
+ },
+ {
+ "podUrls": [{"host": "diasp.ot-si.com"}],
+ "name": "diasp.ot-si.com",
+ "mainLangs": [],
+ "id": 1100
+ },
+ {
+ "score": 99,
+ "podUrls": [{"host": "diaspora.crossfamilyweb.com"}],
+ "name": "crossfamilyweb.com",
+ "mainLangs": [],
+ "id": 10816
+ },
+ {
+ "podUrls": [{"host": "hub.aoeu.me"}],
+ "name": "hub.aoeu.me",
+ "mainLangs": [],
+ "id": 22535
+ },
+ {
+ "podUrls": [{"host": "dia.pwn.ninja"}],
+ "name": "dia.pwn.ninja",
+ "mainLangs": [],
+ "id": 41416
+ },
+ {
+ "podUrls": [{"host": "3ogsbs3vuvapgg37.onion.to"}],
+ "name": "3ogsbs3vuvapgg37.onion.to",
+ "mainLangs": [],
+ "id": 43975
+ },
+ {
+ "podUrls": [{"host": "hubz.secretlair.me"}],
+ "name": "hubz.secretlair.me",
+ "mainLangs": [],
+ "id": 31412
+ },
+ {
+ "podUrls": [{"host": "got.noip.me"}],
+ "name": "got.noip.me",
+ "mainLangs": [],
+ "id": 24638
+ },
+ {
+ "podUrls": [{"host": "pod.bitcast.info"}],
+ "name": "bitcast.info",
+ "mainLangs": [],
+ "id": 31132
+ },
+ {
+ "podUrls": [{"host": "f.tschlotfeldt.de"}],
+ "name": "f.tschlotfeldt.de",
+ "mainLangs": ["de"],
+ "id": 12221
+ },
+ {
+ "podUrls": [{"host": "hubloq.net"}],
+ "name": "hubloq.net",
+ "mainLangs": [],
+ "id": 41618
+ },
+ {
+ "podUrls": [{"host": "hubzilla.kneedrag.org"}],
+ "name": "hubzilla.kneedrag.org",
+ "mainLangs": [],
+ "id": 38463
+ },
+ {
+ "podUrls": [{"host": "gesichtsbu.ch"}],
+ "name": "gesichtsbu.ch",
+ "mainLangs": ["de"],
+ "id": 47552
+ },
+ {
+ "podUrls": [{"host": "hubzi.fraengii.de"}],
+ "name": "hubzi.fraengii.de",
+ "mainLangs": ["de"],
+ "id": 40845
+ },
+ {
+ "podUrls": [{"host": "friendica.narf.ssji.net"}],
+ "name": "friendica.narf.ssji.net",
+ "mainLangs": [],
+ "id": 37056
+ },
+ {
+ "podUrls": [{"host": "durohr.de"}],
+ "name": "durohr.de",
+ "mainLangs": ["de"],
+ "id": 48207
+ },
+ {
+ "podUrls": [{"host": "0kcg.com"}],
+ "name": "0kcg.com",
+ "mainLangs": [],
+ "id": 9891
+ },
+ {
+ "podUrls": [{"host": "aegibson.me"}],
+ "name": "aegibson.me",
+ "mainLangs": [],
+ "id": 45381
+ },
+ {
+ "podUrls": [{"host": "redmatrix.us"}],
+ "name": "redmatrix.us",
+ "mainLangs": ["en"],
+ "id": 28470
+ },
+ {
+ "podUrls": [{"host": "15o2.de"}],
+ "name": "15o2.de",
+ "mainLangs": ["de"],
+ "id": 10262
+ },
+ {
+ "podUrls": [{"host": "upt.social"}],
+ "name": "upt.social",
+ "mainLangs": [],
+ "id": 22385
+ },
+ {
+ "podUrls": [{"host": "friendica.bohrshouse.com"}],
+ "name": "friendica.bohrshouse.com",
+ "mainLangs": [],
+ "id": 9771
+ },
+ {
+ "podUrls": [{"host": "hubzilla.site"}],
+ "name": "hubzilla.site",
+ "mainLangs": [],
+ "id": 12050
+ },
+ {
+ "podUrls": [{"host": "hubzilla.zavb.de"}],
+ "name": "hubzilla.zavb.de",
+ "mainLangs": ["de"],
+ "id": 16156
+ },
+ {
+ "podUrls": [{"host": "diaspora.aeriesguard.com"}],
+ "name": "aeriesguard.com",
+ "mainLangs": [],
+ "id": 25987
+ },
+ {
+ "podUrls": [{"host": "pod.cornify.de"}],
+ "name": "cornify.de",
+ "mainLangs": ["de"],
+ "id": 46021
+ },
+ {
+ "podUrls": [{"host": "hochminuseins.net"}],
+ "name": "hochminuseins.net",
+ "mainLangs": [],
+ "id": 27013
+ },
+ {
+ "podUrls": [{"host": "thecrimsontint.com"}],
+ "name": "thecrimsontint.com",
+ "mainLangs": [],
+ "id": 44004
+ },
+ {
+ "podUrls": [{"host": "diaspora.clubelek.fr"}],
+ "name": "clubelek.fr",
+ "mainLangs": ["fr"],
+ "id": 48494
+ },
+ {
+ "podUrls": [{"host": "teamhubzilla.org"}],
+ "name": "teamhubzilla.org",
+ "mainLangs": [],
+ "id": 35346
+ },
+ {
+ "podUrls": [{"host": "diaspora.schlimme.net"}],
+ "name": "schlimme.net",
+ "mainLangs": [],
+ "id": 33506
+ },
+ {
+ "podUrls": [{"host": "www.wiwiec.com"}],
+ "name": "wiwiec.com",
+ "mainLangs": [],
+ "id": 14764
+ },
+ {
+ "podUrls": [{"host": "diasp.danletard.de"}],
+ "name": "diasp.danletard.de",
+ "mainLangs": ["de"],
+ "id": 38252
+ },
+ {
+ "podUrls": [{"host": "libertypod.com"}],
+ "name": "libertypod.com",
+ "mainLangs": [],
+ "id": 47484
+ },
+ {
+ "podUrls": [{"host": "hubzilla.pskosinski.pl"}],
+ "name": "hubzilla.pskosinski.pl",
+ "mainLangs": [],
+ "id": 49996
+ },
+ {
+ "podUrls": [{"host": "pod.juvenesco.eu"}],
+ "name": "juvenesco.eu",
+ "mainLangs": [],
+ "id": 35432
+ },
+ {
+ "score": 99,
+ "podUrls": [{"host": "pirati.ca"}],
+ "name": "pirati.ca",
+ "mainLangs": [],
+ "id": 7832
+ },
+ {
+ "podUrls": [{"host": "diasp.sk"}],
+ "name": "diasp.sk",
+ "mainLangs": [],
+ "id": 42123
+ },
+ {
+ "podUrls": [{"host": "f.libreden.net"}],
+ "name": "f.libreden.net",
+ "mainLangs": [],
+ "id": 28236
+ },
+ {
+ "podUrls": [{"host": "pod.mausdompteur.de"}],
+ "name": "mausdompteur.de",
+ "mainLangs": ["de"],
+ "id": 22517
+ },
+ {
+ "podUrls": [{"host": "diaspora.raitisoja.com"}],
+ "name": "raitisoja.com",
+ "mainLangs": [],
+ "id": 13163
+ },
+ {
+ "score": 99,
+ "podUrls": [{"host": "squeet.me"}],
+ "name": "squeet.me",
+ "mainLangs": [],
+ "id": 33979
+ },
+ {
+ "podUrls": [{"host": "pod.kakise.xyz"}],
+ "name": "kakise.xyz",
+ "mainLangs": [],
+ "id": 15607
+ },
+ {
+ "podUrls": [{"host": "pod.diasporacaribe.org"}],
+ "name": "diasporacaribe.org",
+ "mainLangs": [],
+ "id": 5615
+ },
+ {
+ "podUrls": [{"host": "hub.feder8.ru"}],
+ "name": "hub.feder8.ru",
+ "mainLangs": ["ru"],
+ "id": 451
+ },
+ {
+ "podUrls": [{"host": "hubzilla.a-zwenkau.de"}],
+ "name": "hubzilla.a-zwenkau.de",
+ "mainLangs": ["de"],
+ "id": 973
+ },
+ {
+ "podUrls": [{"host": "social.punkbeer.me"}],
+ "name": "punkbeer.me",
+ "mainLangs": [],
+ "id": 9210
+ },
+ {
+ "podUrls": [{"host": "pod.sapient.fun"}],
+ "name": "sapient.fun",
+ "mainLangs": [],
+ "id": 12715
+ },
+ {
+ "podUrls": [{"host": "pod.phantasie.cc"}],
+ "name": "phantasie.cc",
+ "mainLangs": [],
+ "id": 17966
+ },
+ {
+ "podUrls": [{"host": "diaspora.masharih.me"}],
+ "name": "masharih.me",
+ "mainLangs": [],
+ "id": 33148
+ },
+ {
+ "podUrls": [{"host": "hub.mariovavti.com"}],
+ "name": "hub.mariovavti.com",
+ "mainLangs": [],
+ "id": 43393
+ },
+ {
+ "podUrls": [{"host": "social.patur.in"}],
+ "name": "patur.in",
+ "mainLangs": ["in"],
+ "id": 35985
+ },
+ {
+ "podUrls": [{"host": "jeroenpraat.nl"}],
+ "name": "jeroenpraat.nl",
+ "mainLangs": ["nl"],
+ "id": 12243
+ },
+ {
+ "podUrls": [{"host": "grindcore.ch"}],
+ "name": "grindcore.ch",
+ "mainLangs": ["de"],
+ "id": 8505
+ },
+ {
+ "podUrls": [{"host": "pod.vixiv.net"}],
+ "name": "vixiv.net",
+ "mainLangs": [],
+ "id": 22599
+ },
+ {
+ "podUrls": [{"host": "social.deuxfleurs.fr"}],
+ "name": "deuxfleurs.fr",
+ "mainLangs": ["fr"],
+ "id": 19227
+ },
+ {
+ "podUrls": [{"host": "parlementum.net"}],
+ "name": "parlementum.net",
+ "mainLangs": [],
+ "id": 8591
+ },
+ {
+ "podUrls": [{"host": "villianbook.com"}],
+ "name": "villianbook.com",
+ "mainLangs": [],
+ "id": 40782
+ },
+ {
+ "podUrls": [{"host": "frolix8.asia"}],
+ "name": "frolix8.asia",
+ "mainLangs": [],
+ "id": 14385
+ },
+ {
+ "podUrls": [{"host": "hub.vilarejo.pro.br"}],
+ "name": "hub.vilarejo.pro.br",
+ "mainLangs": [],
+ "id": 45914
+ },
+ {
+ "podUrls": [{"host": "social.vixiv.net"}],
+ "name": "vixiv.net",
+ "mainLangs": [],
+ "id": 13922
+ }
+ ],
+ "timestamp": 1502656297127
+}
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
deleted file mode 100644
index eda8f3d66..000000000
--- a/app/src/main/res/values-de/strings.xml
+++ /dev/null
@@ -1,144 +0,0 @@
-
-
- Neu laden
- Pod auswählen
- Pod-Domain eingeben
- App beenden
- Pod-Adresse bestätigen
- Anmerkung: Auf dieser Liste finden sich nur Einträge von podupti.me
- Bitte einen gültigen Domain-Namen (URL) eingeben
- Fehler: Konnte die Podliste nicht abfragen!
- Podliste wird geladen...
- Entschuldigung, du musst mit dem Internet verbunden sein um fortzufahren.
- Bestätigung
- Willst du folgenden Pod benutzen:
- Möchtest du die App verlassen?
- JA
- NEIN
- Das wird alle Cookies und Session-Daten löschen. Willst du wirklich den Pod wechseln?
-
- Du musst der App Zugriff auf den Gerätespeicher gewähren, damit das Bildschirmfoto
- gespeichert werden kann. Danach solltest du die Anwendung komplett schließen oder das Telefon neu starten.
- Wenn du den Zugriff verweigerst und die Funktion später doch nutzen willst, kannst du die Berechtigung
- nachträglich erteilen. Öffne dafür: Systemeinstellungen - Apps - Wetter. Im Bereich Berechtigungen kannst
- dann die entsprechende Einstellung vornehmen.
-
- // Drawer and App
-
- Einstellungen
- Stream
- Benachrichtigungen
- Unterhaltungen
- Gelikete Beiträge
- Kommentierte Beiträge
- Kontakte
- Erwähnungen
- Meine Aktivitäten
- Verfolgte Tags
- Tags verwalten
- Persönliche Einstellungen
- Meine Aspekte
- Pod wechseln
- Öffentliche Aktivitäten
-
- // Floating Action Buttons - Titles
-
- Nach oben scrollen
- Suche nach Tags oder Personen ...
- Neuer Beitrag
-
- Suche
- nach Tags
- nach Personen
- Füge einen Namen ein.
- Versuche einen Tag wie: #neuhier oder #kunst
-
- //Dialog
-
-
- //Dialog
- Unterhaltungen
- Neuer Beitrag
- Benachrichtigungen
- Schriftgröße ändern
- Bilder laden umschalten
- Ansicht wechseln
- Bitte den Stream neu laden.
- normal
- groß
- riesig
- Konnte Bild nicht laden ...
- Inhalt Teilen
- Link als Text teilen
- Bildschirmfoto teilen
- Bildschirmfoto machen
- Bildschirmfoto wird gespeichert...
- Diaspora Einstellungen
- Ansichtseinstellungen
- OK
- Markdown Formatierung
- Lizenz
- Lizenz | Hilfe | Spende
- "Auf Diaspora* ist es möglich, dass du deinen Text in Nachrichten, Kommentaren und Unterhaltungen formatierst, indem du ein vereinfachtes Mark-Up-System namens Markdown verwendest. Diese Seite stellt eine kurze Einleitung dar, um grundlegende Formatierungen anwenden zu können.\n\n
-
- Überschriften\n
- # das ist eine sehr große Überschrift\n
- ## halb so groß wie die oben drüber\n
- ### doppelt so groß wie normaler Text\n\n
-
- Kursiv und fett\n
- Kursiv: *Wort* oder _Wort_\n
- Fett: **Wort** oder __Wort__\n
- Fett kursiv: ***Wort*** oder ___Wort___\n\n
-
- Aufzählung\n
- *, + oder - vor jeder Linie die du als Teil der Aufzählung haben willst oder 1., 2., usw. vor jedem Aufzählungspunkt.\n\n
-
- Zitat\n
- Wenn du einen Teil eines Artikels oder eines anderen Kommentares zitieren willst, kannst du deinen Text bequem formatieren, indem du die Zeile oder den Abschnitt mit einem > Zeichen beginnst.\n\n
-
- Horizontale Linie\n
- Um eine horizontale Linie zu erstellen benutzt du mindestens drei - - -, _ _ _ oder * * * in einer separaten Linie. Jede größere Anzahl bewirkt dasselbe und Leerzeichen zwischen den Zeichen stören nicht.\n\n
-
- Inline Link\n
- [displayed text here](http://link.address.here \"alt text\") Der 'alt text' ist optional, und zeigt einen Tool-Tip, wenn der Curser sich über den Link bewegt.\n\n
-
- Externe Bilder\n
- ![Alt text](http://website.com/image.jpg \"optional title\") Der 'alt text' in der eckigen Klammer wird angezeigt, wenn das Bild nicht geladen werden kann. Der optionale Titel zeigt einen Tool-Tip, wenn der Curser sich über den Link bewegt. Beides ist nützlich aber nicht nötig.\n\n
-
- Escape\n
- Wenn du in deiner Nachricht ein Zeichen benutzen willst, das ebenfalls zur Markdownformatierung verwendet wird, kannst du verhindern, dass es von Markdown als Formatcode gelesen wird, indem du es 'escapest'. Um dies zu tun, musst du einen Backslash vor das Zeichen setzen. Du kannst trotzdem 'diaspora*' oder 'D*' schreiben, ohne dass das Sternchen als formatierender Code gelesen wird!\n\n
-
- Sonderzeichen\n
- Du kannst Symbole und Sonderzeichen nutzen, indem du bestimmte Zeichenfolgen verwendest. Z.B.:\n
- (c) = © ; (r) = ® ; x^2 = x²"
- Besuche die Projektseite auf GitHub
- Zuerst einmal: ich bin kein Entwickler. Ich verstehe ein paar Grundlagen, aber das Meiste meiner
- Arbeit ist mit \"Copy and Paste\" passiert. Diese App ist ein Fork der original \"DiasporaNativeWebApp\"
- von \"martinchodev\" (der die allermeiste Arbeit erledigt hat -> mein großer Dank gilt ihm), welche unter der GPL-Lizenz
- veröffentlicht wurde.\n\n
-
-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 http://www.gnu.org/licenses/.\n\n
-
- Die Bilder des Startbildschirms können auf Flickr gefunden werden:
- https://www.flickr.com/photos/129581906@N06/sets/72157651933980136/with/16594947123/.
- Sie wurden von \"Lydia\" veröffentlicht und stehen unter der cc by-nc-sa Lizenz.
- Suche nach Personen ...
- Suche nach Tags ...
- per Bitcoin
- Wenn dir die App gefällt, kannst du dem Entwickler der original \"DiasporaNativeWebApp\"
- eine Kleinigkeit spenden:
- Spenden
- Startbildschirm
-
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
index 251fb9fb7..5b55f9784 100644
--- a/app/src/main/res/values-v21/styles.xml
+++ b/app/src/main/res/values-v21/styles.xml
@@ -1,9 +1,9 @@
>
-
diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml
index 63fc81644..c6dc80ce1 100644
--- a/app/src/main/res/values-w820dp/dimens.xml
+++ b/app/src/main/res/values-w820dp/dimens.xml
@@ -3,4 +3,5 @@
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
64dp
+ 32dp
diff --git a/app/src/main/res/values-w820dp/strings.xml b/app/src/main/res/values-w820dp/strings.xml
deleted file mode 100644
index 03fdcf711..000000000
--- a/app/src/main/res/values-w820dp/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
- Diaspora Native WebApp
- Reload
- Select Pod
- Enter pod domain
- Exit app
- Confirm pod url
- Diaspora
- Note: This list is populated with only the secure pods from podupti.me
- Please enter a valid domain name
- Error: Could not retrieve list of pods!
- Loading pod list ...
- Sorry, you must be connected to the Internet to proceed
- Confirmation
- Do you want to use the pod:
- Are you sure you want to exit?
- YES
- NO
- This will erase all cookies and session data. Do you really want to change pods?
- New post
-
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000..50bffbc7e
--- /dev/null
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -0,0 +1,201 @@
+
+
+
+ 重新下載
+
+ 設定
+ 通知
+ 對話
+ 流水帳
+ 個人檔案
+ 社交面
+ 活動
+ 按過讚
+ 留言
+ 指指點點
+ 公開
+ 搜尋
+ 聯絡人
+ 更新紀錄
+ 統計資料
+
+ 全部的通知
+ 其他留言
+ 貼文被留言
+ 被按讚
+ 被指指點點
+ 被分享
+ 開始分享
+
+ 錯誤: 無法取得豆莢列表!
+ 抱歉,要有網路連線才能繼續
+ 確認
+ 確定要退出嗎?
+
+ 再來
+ 說明 | 幫助
+ 追蹤中的標籤
+ 公開活動
+ 回報
+ 分享連結文字
+ 分享網頁截圖
+ 產生網路截圖
+ 圖片儲存為
+ 截圖儲存為:
+ 已經複製了連結網址…
+ 新增貼文
+ 回到最上面
+ 找人或標籤
+ 退出應用程式
+ 切換行動/桌上型版面
+ 分享…
+ 找標籤
+ 找人
+ 請輸入名稱
+ 分享連結網址
+ 儲存圖片
+ 分享圖片
+ 用外部瀏覽器開啟…
+ 將連結網址複製到剪貼簿
+ 將圖片網址複製到剪貼簿
+
+ 無法載入圖片
+
+ 必須要給予應用程式「儲存空間」的權限,才能儲存畫面截圖。給予後應該要將應用程式完全結束,或是將裝置重新啟動。如果你現在沒給權限,之後才想要使用截圖功能,還是可以重給權限。作法是:先打開「設定」—應用程式—dandelion*,在「權限」的地方切換是否給予「儲存空間」權限。
+ 必須要給予應用程式「儲存空間」的權限,才能儲存或上傳圖片。給予後應該要將應用程式完全結束,或是將裝置重新啟動。如果你現在沒給權限,之後才想要儲存圖片,還是可以重給權限。作法是:先打開「設定」—應用程式—dandelion*,在「權限」的地方切換是否給予「儲存空間」權限。
+ 權限被拒絕了。
+ 取得權限了。請再重試一次。
+ 自選豆莢
+ 豆莢名稱
+ 網路協定
+ 豆莢網址
+ 沒有填值
+ 回到最近瀏覽的流水帳頁面嗎?
+ 隱藏主畫面中的狀態列
+ 隱藏狀態列
+ 在主畫面中顯示標題列
+ 顯示標題列
+
+ 說明
+ 授權條款
+ 除錯
+ 應用程式
+ 裝置
+ diaspora* 豆莢
+ 除錯紀錄
+ 除錯紀錄(詳細版)
+ 應用程式版本: %1$s
+ Android 版本: %1$s
+ 裝置名稱: %1$s
+ 代號: %1$s
+ 豆莢設定名稱: %1$s
+ 豆莢網址: %1$s
+ 已經複製除錯紀錄到剪貼簿了
+ dandelion* (蒲公英*) 是你瀏覽 diaspora* 社交網站的好朋友。它讓你的體驗增加了好用的工具列,並且支援像是 Tor 之類的代理伺服器服務。
+ 貢獻程式碼!
+ dandelion* 是自由軟體開發專案,並且追隨 diaspora* 專案的信念。想要貢獻你的心力嗎?那就來吧!目前我們還只是個很小的團隊,任何形式的幫忙都會讓我們足感心!
+ 取得源碼
+ 翻譯應用程式!
+ 應用程式沒有你使用語言的版本嗎?你可以改變現狀!何不來幫忙我們翻譯呢?我們使用 crowdin 平台,好讓每個人都能夠參與應用程式的翻譯工作。
+ 我來翻譯
+ 意見回饋!
+ dandelion* 還在開發階段,所以如果你有任何建議,或是有其他的意見要回饋,請使用我們的臭蟲追蹤網站來讓我們知道!
+ 回報臭蟲
+ 報給人知!
+ 告訴你的朋友和家人有 #dandelion 這個好東西!何不部落格一下你的使用經驗呢?我們期待聽聽你的故事!
+ 分享應用程式
+ 哇!看看 #dandelion 這套應用程式! %1$s
+
+ 維護人員
+ 目前這套應用程式是由以下人員開發與維護: <br><br>%1$s
+ 貢獻人
+ %1$s<br><br>感謝你們!
+ GNU GPLv3+ 授權條款
+ 第三方程式庫
+ 我們使用了下列程式庫:
+ 我們從 LeafPic 應用程式得到一些啟發以及程式碼。去看看吧,它也是自由軟體喔!
+ 再多說一些
+
+
+
+ 上方工具列可載入流水帳
+ 可以點上方工具列的空白區域來進入流水帳
+
+ 外觀
+ 網路
+ 豆莢設定
+ 操作
+
+
+ 側邊導覽選單
+ 控制側邊導覽選單中顯示的項目
+ 使用者
+ 平常
+ 管理員
+
+ 佈景主題和色彩
+ 控制應用程式使用的顏色
+ 主要顏色
+ 工具列使用的顏色
+ 強調顏色
+ 進度條使用的顏色
+ AMOLED 顯示模式
+ 在應用程式中使用適合 AMOLED 螢幕的黑色來顯示。改變這項設定後需要重開應用程式。另外你還需要使用色彩主題 Dark 才會有深色的版面,請到你的 diaspora* 帳號設定頁面修改。
+
+ 延伸型通知
+ 將通知鈴伸展成為顯示通知類別的下拉式選單
+ 更改應用程式使用的語言。要重新開啟應用程式才會生效
+ 語言
+ 系統設定語言
+
+ 控制網頁視圖中的文字大小
+ 字型大小
+ 一般
+ 較大
+ 超大
+
+ 載入圖片
+ 切換是否要載入圖片,以節省行動資料流量等等
+
+ 畫面旋轉
+ 控制畫面自動旋轉功能
+ 系統預設
+ 感應\n(忽略系統設定)
+ 直向
+ 横向
+
+ 載入 Tor 的預設值
+ 載入使用 Tor (Orbot) HTTP 代理伺服器的設定
+ 代理伺服器
+ 啓用代理伺服器
+ 讓 dandelion* 透用代理伺服器連網來避開防火牆。\n設定後可能需要重新啟動。某些手機上可能會沒有作用。
+ 主機
+ 通訊埠
+ 應用程式要重新啟動以停止使用代理伺服器
+ 已經載入 Orbot 的代理伺服器設定了
+
+ 使用 Chrome Custom Tabs 開啟外部連結。必須要安裝 Chromium 或是 Google Chrome 才能使用這個功能。
+\n請留意:Chrome Custom Tabs 不會使用應用程式設定的代理伺服器!
+
+ 個人設定
+ 打開你的 diaspora* 帳號設定頁面
+ 管理聯絡人清單
+ 管理雜湊標籤
+ 停止追蹤目前正在追蹤中的雜湊標籤
+ 更換帳號
+ 刪除本機連線 session 資料,並改用另外一個 diaspora* 豆莢或帳號
+ 將要清除所有的 cookie 和 session 資料。確定真的要切換帳號嗎?
+ 清除快取資料
+ 清除網頁視圖的快取資料
+ 當捲動畫面時,自動隱藏上下兩邊的工具列
+ 自動隱藏工具列
+ 分享時附加應用程式標記
+ 在分享的文字之後附加以下應用程式標記: [via #dandelion]
+
+ 其他
+ 完全重置
+ 將本機上此應用程式的設定全部刪掉,並將所有帳號都登出
+ 將要重設此應用程式的所有設定為預設值,並登出你在所有豆莢的帳號。不過不會變動已經下載了的圖片。確定要繼續嗎?
+ 開啟基本的廣告封鎖器。廣告可能會出現在嵌入式視圖之類的地方。
+ 廣告封鎖
+
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
new file mode 100644
index 000000000..4decbb459
--- /dev/null
+++ b/app/src/main/res/values/arrays.xml
@@ -0,0 +1,28 @@
+
+
+
+ - @string/font_size_normal
+ - @string/font_size_large
+ - @string/font_size_huge
+
+
+
+ - normal
+ - large
+ - huge
+
+
+
+
+ - @string/rotation_val_system
+ - @string/rotation_val_sensor
+ - @string/rotation_val_portrait
+ - @string/rotation_val_landscape
+
+
+ - @string/rotation_system
+ - @string/rotation_sensor
+ - @string/rotation_portrait
+ - @string/rotation_landscape
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/color.xml b/app/src/main/res/values/color.xml
index b4ade8405..03dbddbf9 100644
--- a/app/src/main/res/values/color.xml
+++ b/app/src/main/res/values/color.xml
@@ -1,17 +1,356 @@
-
- #607d8b
- #607d8b
- #ff9800
+
- #ff9800
- #ffb74d
+ @color/primary
+ @color/primary_dark
+ @color/accent
- #607d8b
- #90a4ae
-
- #99212121
- #ffffff
+
+ @color/md_blue_750
+ @color/md_blue_900
+ @color/accent_green
+ #BBDEFB
+ #212121
+ #727272
+ #FFFFFF
+ #B6B6B6
+
#ffffff
+ #000000
+ #eeeeee
+ #c4000000
+ @color/md_grey_200
+
+
+ #f44336
+ #e91e63
+ #9c27b0
+ #673ab7
+ #3f51b5
+ #2196f3
+ #00bcd4
+ #009688
+ #4caf50
+ #ffeb3b
+ #ffc107
+ #ff9800
+ #795548
+ #FFFFFF
+ #9e9e9e
+ #000000
+
+
+ #000000
+
+ #8A000000
+ #DE000000
+ #8A000000
+ @color/md_grey_300
+ @color/md_grey_100
+ @color/md_grey_200
+ @color/md_white_1000
+ @color/md_white_1000
+ #61000000
+ #1F000000
+
+ @color/md_grey_900
+ @color/md_grey_850
+ @color/md_grey_800
+ @color/md_grey_800
+ #4DFFFFFF
+ #1FFFFFFF
+ #B3FFFFFF
+ #FFFFFFFF
+ #B3FFFFFF
+ @color/md_black_1000
+
+
+ #FFECB3
+ #FFE082
+ #FFD54F
+ #FFCA28
+ #FFF8E1
+ #FFC107
+ #FFB300
+ #FFA000
+ #FF8F00
+ #FF6F00
+ #FFE57F
+ #FFD740
+ #FFC400
+ #FFAB00
+
+ #BBDEFB
+ #90CAF9
+ #64B5F6
+ #42A5F5
+ #E3F2FD
+ #2196F3
+ #1E88E5
+ #207be6
+ #1976D2
+ #195ed2
+ #1565C0
+ #0D47A1
+ #82B1FF
+ #448AFF
+ #2979FF
+ #2962FF
+
+ #CFD8DC
+ #B0BEC5
+ #90A4AE
+ #78909C
+ #ECEFF1
+ #607D8B
+ #546E7A
+ #455A64
+ #37474F
+ #263238
+
+ #D7CCC8
+ #BCAAA4
+ #A1887F
+ #8D6E63
+ #EFEBE9
+ #795548
+ #6D4C41
+ #5D4037
+ #4E342E
+ #3E2723
+
+ #B2EBF2
+ #80DEEA
+ #4DD0E1
+ #26C6DA
+ #E0F7FA
+ #00BCD4
+ #00ACC1
+ #0097A7
+ #00838F
+ #006064
+ #84FFFF
+ #18FFFF
+ #00E5FF
+ #00B8D4
+
+
+ #FFCCBC
+ #FFAB91
+ #FF8A65
+ #FF7043
+ #FBE9E7
+ #FF5722
+ #F4511E
+ #FF5300
+ #E64A19
+ #D84315
+ #BF360C
+ #FF9E80
+ #FF6E40
+ #FF3D00
+ #DD2C00
+
+ #D1C4E9
+ #B39DDB
+ #9575CD
+ #7E57C2
+ #EDE7F6
+ #673AB7
+ #5E35B1
+ #512DA8
+ #4527A0
+ #311B92
+ #B388FF
+ #7C4DFF
+ #651FFF
+ #6200EA
+
+ #C8E6C9
+ #A5D6A7
+ #81C784
+ #66BB6A
+ #E8F5E9
+ #4CAF50
+ #43A047
+ #388E3C
+ #2E7D32
+ #1B5E20
+ #B9F6CA
+ #69F0AE
+ #00E676
+ #00C853
+
+ #F5F5F5
+ #EEEEEE
+ #E0E0E0
+ #BDBDBD
+ #FAFAFA
+ #9E9E9E
+ #757575
+ #616161
+ #424242
+ #303030
+ #212121
+
+ #C5CAE9
+ #9FA8DA
+ #7986CB
+ #5C6BC0
+ #E8EAF6
+ #3F51B5
+ #3949AB
+ #303F9F
+ #283593
+ #1A237E
+ #8C9EFF
+ #536DFE
+ #3D5AFE
+ #304FFE
+
+ #B3E5FC
+ #81D4FA
+ #4FC3F7
+ #29B6F6
+ #E1F5FE
+ #03A9F4
+ #039BE5
+ #0288D1
+ #0277BD
+ #01579B
+ #80D8FF
+ #40C4FF
+ #00B0FF
+ #0091EA
+
+ #DCEDC8
+ #C5E1A5
+ #AED581
+ #9CCC65
+ #F1F8E9
+ #8BC34A
+ #7CB342
+ #689F38
+ #558B2F
+ #33691E
+ #CCFF90
+ #B2FF59
+ #76FF03
+ #64DD17
+
+ #F0F4C3
+ #E6EE9C
+ #DCE775
+ #D4E157
+ #F9FBE7
+ #CDDC39
+ #C0CA33
+ #AFB42B
+ #9E9D24
+ #827717
+ #F4FF81
+ #EEFF41
+ #C6FF00
+ #AEEA00
+
+ #FFE0B2
+ #FFCC80
+ #FFB74D
+ #FFA726
+ #FFF3E0
+ #FF9800
+ #FB8C00
+ #F57C00
+ #EF6C00
+ #E65100
+ #FFD180
+ #FFAB40
+ #FF9100
+ #FF6D00
+
+ #F8BBD0
+ #F48FB1
+ #F06292
+ #EC407A
+ #FCE4EC
+ #E91E63
+ #D81B60
+ #C2185B
+ #AD1457
+ #880E4F
+ #FF80AB
+ #FF4081
+ #F50057
+ #C51162
+
+ #E1BEE7
+ #CE93D8
+ #BA68C8
+ #AB47BC
+ #F3E5F5
+ #9C27B0
+ #8E24AA
+ #7B1FA2
+ #6A1B9A
+ #4A148C
+ #EA80FC
+ #E040FB
+ #D500F9
+ #AA00FF
+
+ #FFCDD2
+ #EF9A9A
+ #E57373
+ #EF5350
+ #FFEBEE
+ #F44336
+ #E53935
+ #D32F2F
+ #C62828
+ #B71C1C
+ #FF8A80
+ #FF5252
+ #FF1744
+ #D50000
+
+ #B2DFDB
+ #80CBC4
+ #4DB6AC
+ #26A69A
+ #E0F2F1
+ #009688
+ #00897B
+ #00796B
+ #00695C
+ #004D40
+ #A7FFEB
+ #64FFDA
+ #1DE9B6
+ #00BFA5
+
+ #FFFFFF
+ #FFF9C4
+ #FFF59D
+ #FFF176
+ #FFEE58
+ #FFFDE7
+ #FFEB3B
+ #FDD835
+ #FBC02D
+ #F9A825
+ #F57F17
+ #FFFF8D
+ #FFFF00
+ #FFEA00
+ #FFD600
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 05cdb9535..a3a0e44d8 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -1,8 +1,16 @@
16dp
- 160dp
16dp
+ 8dp
16dp
+ 45dp
+
+
+
+
+ 13sp
+ 30dp
diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml
new file mode 100644
index 000000000..89297341a
--- /dev/null
+++ b/app/src/main/res/values/integers.xml
@@ -0,0 +1,4 @@
+
+
+ 1300
+
diff --git a/app/src/main/res/values/strings-not_translatable.xml b/app/src/main/res/values/strings-not_translatable.xml
new file mode 100644
index 000000000..3f399d323
--- /dev/null
+++ b/app/src/main/res/values/strings-not_translatable.xml
@@ -0,0 +1,149 @@
+
+
+
+ dandelion*
+ Your companion app for browsing diaspora*
+
+ \n\n\n_________________________\n
+ **Tags:** #dandelíon \n\n
+ *via [dandelion*](/people?q=dandelion00%40diasp.org) client [(Source)](https://github.com/Diaspora-for-Android/dandelion)*
+
+
+ Tor
+ HTTP
+
+ @string/new_post
+ @string/search
+ http
+ https
+
+ @string/about_activity__title_about_license
+
+ Copyright © 2015-2018
+ \nThis 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.
+ \n
+ \nThis 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.
+
+ https://www.gnu.org/licenses/gpl-3.0.html
+ @string/pref_title__sub_miscelaneous
+ https://github.com/HoraApps/LeafPic
+ https://f-droid.org/repository/browse/?fdid=com.github.dfa.diaspora_android
+ @string/app_name
+ https://github.com/Diaspora-for-Android/dandelion
+ https://crowdin.com/project/diaspora-for-android/invite
+ https://github.com/Diaspora-for-Android/dandelion/issues
+
+
+
+ not_implemented
+ @string/not_implemented
+ @string/not_implemented
+ @string/not_implemented
+ @string/not_implemented
+
+
+ auto
+ sensor
+ portrait
+ landscape
+
+
+ @string/stream
+ @string/liked
+ @string/commented
+ @string/mentions
+ @string/activities
+ @string/aspects
+ @string/profile
+ @string/statistics
+ @string/contacts
+
+
+ Chrome Custom Tabs
+ wasProxyEnabled
+ @string/contacts
+ @string/about_activity__title_debug_info
+ @string/fragment_debug__section_log
+ @string/fragment_debug__section_log
+ @string/fragment_debug__section_log_spam
+ @string/pref_desc__http_proxy_enabled
+ @string/nav_menu_more
+
+
+ pref_key__current_pod_0
+ pref_key_font_size
+ pref_key__language
+ pref_key_intellihide_toolbars
+ pref_key_personal_settings
+ pref_key_manage_tags
+ pref_key_manage_contacts
+ pref_key_change_account
+ pref_key_load_images
+ pref_key_clear_cache
+ pref_key__chrome_custom_tabs_enabled
+ pref_key__http_proxy_load_tor_preset
+ pref_key__extended_notifications
+ pref_key__topbar_stream_shortcut
+ pref_key__app_first_start
+ pref_key__app_first_start_current_version
+ pref_key__primary_color__amoled_mode
+ @string/pref_key__primary_color_shade
+ pref_key_primary_color_base
+ pref_key_primary_color_shade
+ @string/pref_key__accent_color_shade
+ pref_key_accent_color_base
+ pref_key_accent_color_shade
+ pref_key_append_shared_via_app
+ pref_key_proxy_enabled
+ pref_key_proxy_host
+ pref_key_proxy_port
+ pref_key__screen_rotation
+ pref_key__visibility_navslider__exit
+ pref_key__visibility_nav__help_license
+ pref_key__visibility_nav__public_activities
+ pref_key__visibility_nav__mentions
+ pref_key__visibility_nav__commented
+ pref_key__visibility_nav__liked
+ pref_key__visibility_nav__activities
+ pref_key__visibility_nav__aspects
+ pref_key__visibility_nav__followed_tags
+ pref_key__visibility_nav__profile
+ pref_key__visibility_nav__contacts
+ pref_key__visibility_nav__reports
+ pref_key__visibility_nav__statistics
+ pref_key__visibility_nav__toggle_mobile_desktop
+ pref_key__visibility_nav__dandelion_account
+ pref_key__podprofile_avatar_url
+ pref_key__podprofile_name
+ pref_key__podprofile_id
+ pref_key__podprofile_aspects
+ pref_key__podprofile_aspects_favs
+ pref_key__podprofile_followed_tags
+ pref_key__podprofile_followed_tags_favs
+ pref_key__podprofile_unread_message_count
+ pref_key__podprofile_notification_count
+ pref_key__podprofile_last_stream_position
+ pref_key__logging_spam_enabled
+ pref_key__logging_enabled
+ pref_key__wipe_settings
+ pref_key__title__appearance
+ pref_key__title__pod_settings
+ pref_key__title__network
+ pref_key__title__more
+ pref_key__title__debugging
+ pref_key__title__proxy
+ pref_key__title__themes
+ pref_key__cat_themes
+ pref_key__cat_nav_slider
+ pref_key__cat_proxy
+ pref_key__cat_debugging
+ pref_key__adblock_enable
+ pref_key__is_overview_statusbar_hidden
+ pref_key__recreate_main_activity
+ pref_key__show_title
+ PDF
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 32acf2ba7..2dbaeaf3e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,166 +1,7 @@
+
-
- //Application
-
- Open navigation drawer
- Close navigation drawer
- Diaspora
+
+ Open navigation drawer
+ Close navigation drawer
Reload
- You must grant \"Access Storage Permission\" to save screenshots. After that you should
- completely close the app or restart the phone. If you don\'t permit the storage access but want to use the
- screenshot function at a later time, you can grant the permission later. Please open then: systemsettings - apps -
- weather. In the permissions section you can grant the \"write storage permission\".
-
- //Pod Activity
-
- Select Pod
- Enter pod domain
- Confirm pod url
- Note: This list is populated with only the secure pods from podupti.me
- Please enter a valid domain name
- Error: Could not retrieve list of pods!
- Loading pod list ...
-
-
- Sorry, you must be connected to the Internet to proceed
- Confirmation
- Do you want to use the pod:
- Do you want to exit?
- YES
- NO
- This will erase all cookies and session data. Do you really want to change pods?
- New
- Notifications
- Conversations
-
- // Drawer and App
-
- The community-run distributed social network
- Settings
- License | Help | Donate
- Stream
- Notifications
- Conversations
- Liked
- Commented
- Contacts
- Mentions
- My activity
- Followed Hashtags
- Manage Hashtags
- Personal settings
- My aspects
- Change pod
- Public activities
- View settings
- Diaspora settings
- Share content
-
- Change font size
- Toggle image loading
- Change view
- Share link as text
- Share screenshot of webpage
- Take screenshot of webpage
-
- License
- Markdown formating
- Donate
-
- Taking screenshot ...
-
-
- // Floating Action Buttons - Titles
-
- Go to top
- Search by tags or persons
- Search for tag ...
- Search for person ...
- New message
-
-
- Search
- by tags
- by people
- Please add a name
- Try to add a tag like: #newhere or #art
-
- //Dialog //License
-
- "On diaspora* it's possible to format your text in status messages, comments and conversations using a simplified mark-up system called Markdown. This page gives an introduction to the codes used to create this formatting.\n\n
-
- Heading\n
- # This is an extremely large header\n
- ## Half as big as the one above\n
- ### Twice the size of normal text\n\n
-
- Italics and bold\n
- Italics: *word* or _word_\n
- Bold: **word** or __word__\n
- Bold italics: ***word*** or ___word___\n\n
-
- Bulleted list\n
- *, + or - in front of each line you want as part of the list or 1., 2., etc. in front of the list items\n\n
-
- Quotation\n
- When you want to quote an extract of an article or another comment, you can conveniently format your text by starting the line or the paragraph by the > character\n\n
-
- Horizontal line\n
- To create a horizontal line, use at least three - - -, _ _ _ or * * * on a separate line. Any number above three will do the same thing and spaces between the characters do not matter.\n\n
-
- Inline link\n
- [displayed text here](http://link.address.here \"alt text\") The 'alt text' is optional, and is displayed as a tool-tip when the cursor is moved over the link.\n\n
-
- External images\n
- ![Alt text](http://website.com/image.jpg \"optional title\") The alternative text in the square brackets is displayed if the image cannot be loaded, and the optional title is displayed as a tool-tip when the cursor is moved over the image; both are useful but not essential.\n\n
-
- Escape\n
- If you want your message to include a character which is also used in Markdown coding, you can prevent it from being read by Markdown as a formatting code by 'escaping' it. To do this, place a backslash in front of the character. You can, however, type 'diaspora*' or 'D*' without the asterisk becoming a formatting code!\n\n
-
- Special characters\n
- You can create the following symbols and other special characters using a combination of characters:\n
- (c) = © ; (r) = ® ; x^2 = x²"
-
- If you like the app fell free to donate to the devoloper of the original \"DiasporaNativeWebApp\":
- Via Bitcoin
-
- Visit projectsite on Github
- OK
- Copyright © 2016 by scoute-dich
- First of all: I am not a developer. I understand a few basics and most of my work is done
- by copy and paste. This app is a fork of the original \"DiasporaNativeWebApp\"
- from \"martinchodev\" (who did most of the work -> big thanks to him), which is released under the GPL-license.\n\n
-
- 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 http://www.gnu.org/licenses/.\n\n
-
- The splashscreen images can be found on flickr: https://www.flickr.com/photos/129581906@N06/sets/72157651933980136/with/16594947123/.
- They were published by \"Lydia\" and are licensed under cc by-nc-sa.
-
- Splash screen
- Please reload the stream
- Normal
- Large
- Huge
-
- Unable to get image
-
-
- - @drawable/splashscreen1
- - @drawable/splashscreen2
- - @drawable/splashscreen3
- - @drawable/splashscreen4
- - @drawable/splashscreen5
- - @drawable/splashscreen6
-
-
\ No newline at end of file
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index f51828050..e3e895fd0 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -1,14 +1,15 @@
-
-
@@ -17,9 +18,24 @@
-
+
+
+
+
+
diff --git a/app/src/main/res/xml-v25/shortcuts.xml b/app/src/main/res/xml-v25/shortcuts.xml
new file mode 100755
index 000000000..cb3852d18
--- /dev/null
+++ b/app/src/main/res/xml-v25/shortcuts.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/xml/file_provider_paths.xml b/app/src/main/res/xml/file_provider_paths.xml
new file mode 100644
index 000000000..c8826f4c5
--- /dev/null
+++ b/app/src/main/res/xml/file_provider_paths.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/preferences__master.xml b/app/src/main/res/xml/preferences__master.xml
new file mode 100644
index 000000000..db8ba451a
--- /dev/null
+++ b/app/src/main/res/xml/preferences__master.xml
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/preferences__sub_debugging.xml b/app/src/main/res/xml/preferences__sub_debugging.xml
new file mode 100644
index 000000000..8115f012d
--- /dev/null
+++ b/app/src/main/res/xml/preferences__sub_debugging.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/preferences__sub_navslider_vis.xml b/app/src/main/res/xml/preferences__sub_navslider_vis.xml
new file mode 100644
index 000000000..55150b9f6
--- /dev/null
+++ b/app/src/main/res/xml/preferences__sub_navslider_vis.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/preferences__sub_proxy.xml b/app/src/main/res/xml/preferences__sub_proxy.xml
new file mode 100644
index 000000000..ab7b01da0
--- /dev/null
+++ b/app/src/main/res/xml/preferences__sub_proxy.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/xml/preferences__sub_themes.xml b/app/src/main/res/xml/preferences__sub_themes.xml
new file mode 100644
index 000000000..318a68c85
--- /dev/null
+++ b/app/src/main/res/xml/preferences__sub_themes.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/test/java/de/baumann/diaspora/ExampleUnitTest.java b/app/src/test/java/de/baumann/diaspora/ExampleUnitTest.java
deleted file mode 100644
index 807b065ff..000000000
--- a/app/src/test/java/de/baumann/diaspora/ExampleUnitTest.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package de.baumann.diaspora;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * To work on unit tests, switch the Test Artifact in the Build Variants view.
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() throws Exception {
- assertEquals(4, 2 + 2);
- }
-}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index e0b366a78..5c5bfd787 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,11 +1,35 @@
+import java.text.SimpleDateFormat
+
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
+ ext.version_setup_compileSdk = 27
+ ext.version_setup_minSdk = 17
+ ext.version_setup_targetSdk = ext.version_setup_compileSdk
+ ext.version_setup_buildTools = "27.0.3" // Specifying optional
+
+ // https://developer.android.com/studio/releases/gradle-plugin.html
+ ext.version_gradle_tools = "3.1.0"
+ // https://developer.android.com/topic/libraries/support-library/revisions.html
+ ext.version_library_appcompat = "27.1.1"
+ // https://github.com/JakeWharton/butterknife/releases
+ ext.version_library_butterknife = "8.8.1"
+ // https://github.com/guardianproject/NetCipher/releases
+ ext.version_library_netcipher = "2.0.0-alpha1"
+ // https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-gradle-plugin#LookAtCentral
+ ext.version_plugin_kotlin = "1.2.21"
+ ext.enable_plugin_kotlin = false
+
repositories {
jcenter()
+ google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.5.0'
+ classpath "com.android.tools.build:gradle:$version_gradle_tools"
+ classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
+ if (project.enable_plugin_kotlin) {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$version_plugin_kotlin"
+ }
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -15,9 +39,61 @@ buildscript {
allprojects {
repositories {
jcenter()
+ google()
+ mavenCentral()
+ maven { url "https://jitpack.io" }
+ }
+
+ tasks.matching { task -> task.name.matches('.*generate.*Resources') }.all {
+ task -> task.dependsOn copyRepoFiles
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
+
+final String[] ROOT_TO_RAW_COPYFILES = ["README.md", "CHANGELOG.md", "CONTRIBUTORS.md", "LICENSE.txt", "LICENSE.md", "LICENSE"]
+task copyRepoFiles(type: Copy) {
+ from rootProject.files(ROOT_TO_RAW_COPYFILES)
+ into "app/src/main/res/raw"
+ rename { String fileName -> fileName.replace(fileName, fileName.toLowerCase()) }
+}
+
+
+@SuppressWarnings(["UnnecessaryQualifiedReference", "SpellCheckingInspection", "GroovyUnusedDeclaration"])
+// Returns used android languages as a buildConfig array: {'de', 'it', ..}"
+static String findUsedAndroidLocales() {
+ Set langs = new HashSet<>()
+ new File('.').eachFileRecurse(groovy.io.FileType.DIRECTORIES) {
+ final foldername = it.name
+ if (foldername.startsWith('values-') && !it.canonicalPath.contains("build" + File.separator + "intermediates")) {
+ new File(it.toString()).eachFileRecurse(groovy.io.FileType.FILES) {
+ if (it.name.toLowerCase().endsWith(".xml") && it.getCanonicalFile().getText('UTF-8').contains("
+ try {
+ def stdout = new ByteArrayOutputStream()
+ exec {
+ commandLine 'git', 'rev-parse', 'HEAD'
+ standardOutput = stdout
+ }
+ return stdout.toString().trim()
+ } catch (Exception ignored) {
+ return 'unknown'
+ }
+}
+@SuppressWarnings(["UnnecessaryQualifiedReference", "SpellCheckingInspection", "GroovyUnusedDeclaration"])
+// Returns the build date in a RFC3339 compatible format. TZ is always converted to UTC
+static String getBuildDate() {
+ final SimpleDateFormat RFC3339_LIKE = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'")
+ RFC3339_LIKE.setTimeZone(TimeZone.getTimeZone("UTC"))
+ return RFC3339_LIKE.format(new Date())
+}
diff --git a/circle.yml b/circle.yml
new file mode 100644
index 000000000..35978980e
--- /dev/null
+++ b/circle.yml
@@ -0,0 +1,38 @@
+###################
+general:
+ artifacts:
+ - /home/ubuntu/dandelion/app/build/outputs/apk/
+ branches:
+ ignore:
+ - gh-pages
+ - l10n_master
+ - crowdin
+
+###################
+machine:
+ java:
+ version: oraclejdk8
+ environment:
+ ANDROID_HOME: /usr/local/android-sdk-linux
+
+###################
+dependencies:
+ pre:
+ # Android SDK Platform
+ - if [ ! -d "/usr/local/android-sdk-linux/platforms/android-26" ]; then echo y | android update sdk --no-ui --all --filter "android-26"; fi
+ # Android SDK Build-tools
+ - if [ ! -d "/usr/local/android-sdk-linux/build-tools/26.0.1" ]; then echo y | android update sdk --no-ui --all --filter "build-tools-26.0.1"; fi
+ # Android Support Repository - deprecated
+ #- if [ ! -d "/usr/local/android-sdk-linux/extras/android/m2repository/com/android/support/design/26.2.0" ]; then echo y | android update sdk --no-ui --all --filter "extra-android-m2repository"; fi
+
+
+ cache_directories:
+ - /usr/local/android-sdk-linux/platforms/android-26
+ - /usr/local/android-sdk-linux/build-tools/26.0.1
+ #- /usr/local/android-sdk-linux/extras/android/m2repository
+
+###################
+test:
+ override:
+ - (./gradlew assembleFlavorDefault):
+ timeout: 360
diff --git a/gradle.properties b/gradle.properties
old mode 100644
new mode 100755
index 1d3591c8a..44129c912
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,18 +1,28 @@
-# Project-wide Gradle settings.
-
-# IDE (e.g. Android Studio) users:
-# Gradle settings configured through the IDE *will override*
-# any settings specified in this file.
-
-# For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
-
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
-# Default value: -Xmx10248m -XX:MaxPermSize=256m
-# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
-
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
\ No newline at end of file
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Settings specified in this file will override any Gradle settings
+# configured through the IDE.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# The Gradle daemon aims to improve the startup and execution time of Gradle.
+# When set to true the Gradle daemon is to run the build.
+# TODO: disable daemon on CI, since builds should be clean and reliable on servers
+org.gradle.daemon=true
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+org.gradle.parallel=true
+
+# Enables new incubating mode that makes Gradle selective when configuring projects.
+# Only relevant projects are configured which results in faster builds for large multi-projects.
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:configuration_on_demand
+org.gradle.configureondemand=true
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 05ef575b0..13372aef5 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f23df6e46..18a0cf951 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Oct 21 11:34:03 PDT 2015
+#Sun Apr 08 08:39:15 CEST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
diff --git a/gradlew.bat b/gradlew.bat
index 8a0b282aa..aec99730b 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,90 +1,90 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windowz variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/metadata/en b/metadata/en
new file mode 120000
index 000000000..f2b0341fe
--- /dev/null
+++ b/metadata/en
@@ -0,0 +1 @@
+en-US
\ No newline at end of file
diff --git a/metadata/en-US/featureGraphic.png b/metadata/en-US/featureGraphic.png
new file mode 100644
index 000000000..39038c2f2
Binary files /dev/null and b/metadata/en-US/featureGraphic.png differ
diff --git a/metadata/en-US/full_description.txt b/metadata/en-US/full_description.txt
new file mode 100644
index 000000000..651f76020
--- /dev/null
+++ b/metadata/en-US/full_description.txt
@@ -0,0 +1,18 @@
+dandelion* is a client for the community-run, distributed social network diaspora*.
+It adds useful features to your networking experience:
+
+⚡ Quick access to most diaspora* features
+👉 Share content to and from the app
+🌎 Proxy support (Tor/Orbot supported)
+📰 In-app-browser to view articles
+🎨 Customizeable colors
+🌆 Night/AMOLED mode
+🈴 Localized in many languages
+✔️ Allows system independent language
+
+Support the project:
+ ✋ Translate using Stringlate
+ ✋ Join discussion on Matrix
+ ✋ More information about contributions
+ ✋ Android Contribution Guide (gsantner blog)
+ ✋ Support main developer
diff --git a/metadata/en-US/icon.png b/metadata/en-US/icon.png
new file mode 100644
index 000000000..64cba8290
Binary files /dev/null and b/metadata/en-US/icon.png differ
diff --git a/metadata/en-US/phoneScreenshots/01.png b/metadata/en-US/phoneScreenshots/01.png
new file mode 100644
index 000000000..414cb929e
Binary files /dev/null and b/metadata/en-US/phoneScreenshots/01.png differ
diff --git a/metadata/en-US/phoneScreenshots/02.png b/metadata/en-US/phoneScreenshots/02.png
new file mode 100644
index 000000000..1cf5d7b3e
Binary files /dev/null and b/metadata/en-US/phoneScreenshots/02.png differ
diff --git a/metadata/en-US/phoneScreenshots/03.png b/metadata/en-US/phoneScreenshots/03.png
new file mode 100644
index 000000000..cc327b58f
Binary files /dev/null and b/metadata/en-US/phoneScreenshots/03.png differ
diff --git a/metadata/en-US/phoneScreenshots/04.png b/metadata/en-US/phoneScreenshots/04.png
new file mode 100644
index 000000000..309fed3c3
Binary files /dev/null and b/metadata/en-US/phoneScreenshots/04.png differ
diff --git a/metadata/en-US/phoneScreenshots/05.png b/metadata/en-US/phoneScreenshots/05.png
new file mode 100644
index 000000000..894366d54
Binary files /dev/null and b/metadata/en-US/phoneScreenshots/05.png differ
diff --git a/metadata/en-US/phoneScreenshots/06.png b/metadata/en-US/phoneScreenshots/06.png
new file mode 100644
index 000000000..8b5da3eda
Binary files /dev/null and b/metadata/en-US/phoneScreenshots/06.png differ
diff --git a/metadata/en-US/phoneScreenshots/07.png b/metadata/en-US/phoneScreenshots/07.png
new file mode 100644
index 000000000..2d264cc35
Binary files /dev/null and b/metadata/en-US/phoneScreenshots/07.png differ
diff --git a/metadata/en-US/phoneScreenshots/08.png b/metadata/en-US/phoneScreenshots/08.png
new file mode 100644
index 000000000..fe82bc93d
Binary files /dev/null and b/metadata/en-US/phoneScreenshots/08.png differ
diff --git a/metadata/en-US/phoneScreenshots/09.png b/metadata/en-US/phoneScreenshots/09.png
new file mode 100644
index 000000000..0b8e47839
Binary files /dev/null and b/metadata/en-US/phoneScreenshots/09.png differ
diff --git a/metadata/en-US/promoGraphic.png b/metadata/en-US/promoGraphic.png
new file mode 100644
index 000000000..a750adc95
Binary files /dev/null and b/metadata/en-US/promoGraphic.png differ
diff --git a/metadata/en-US/short_description.txt b/metadata/en-US/short_description.txt
new file mode 100644
index 000000000..e4886420b
--- /dev/null
+++ b/metadata/en-US/short_description.txt
@@ -0,0 +1 @@
+Client for the diaspora* social network
diff --git a/metadata/en-US/title.txt b/metadata/en-US/title.txt
new file mode 100644
index 000000000..969b23b73
--- /dev/null
+++ b/metadata/en-US/title.txt
@@ -0,0 +1 @@
+dandelion*