1
0
mirror of https://github.com/akaessens/NoFbEventScraper synced 2025-06-05 23:29:13 +02:00

58 Commits

Author SHA1 Message Date
bb1e7579d8 prepare v0.3.2 2020-08-31 14:45:13 +02:00
ba2ec36666 add german error translation 2020-08-31 12:05:24 +02:00
81fd1f3ebb enable dark mode for help and about webviews 2020-08-31 11:32:22 +02:00
8b5263db63 MinSdk down to 23, replace ZonedDateTime with Date 2020-08-30 21:41:58 +02:00
76f56434e8 add german translation 2020-08-30 20:20:52 +02:00
9540b252a5 move html to res/raw to enable localization 2020-08-30 19:09:44 +02:00
7a44e467f0 add high res image preview, fix reset on activity return 2020-08-30 19:03:17 +02:00
1570742462 prepare v0.3.1 2020-08-29 12:32:28 +02:00
0950da98e1 use correct user agent and add legal info 2020-08-29 12:28:28 +02:00
15ef35ba6c fix default scraper setting 2020-08-29 12:07:23 +02:00
55e7ad44d8 Merge branch 'master' of github.com:akaessens/NoFbEventScraper into master 2020-08-28 21:54:10 +02:00
2d3d2a2ab6 fix url settings string 2020-08-28 21:53:58 +02:00
dd36c1fb00 Update README.md 2020-08-28 21:43:45 +02:00
268a6bc650 prepare v0.3.0 2020-08-28 21:37:09 +02:00
f9a711300e use activity names as titles 2020-08-28 21:24:23 +02:00
793a9e3549 prevent nullpointer exception on tests 2020-08-28 21:13:21 +02:00
ec62cb6347 switch to webview display in help and about
add icons to overflow menu
add settings with scrape url prefix selection
rewrite help and about
2020-08-28 20:47:52 +02:00
023b7f951a use rfc time format instead of local 2020-08-28 17:52:36 +02:00
4e319d3807 add javadoc 2020-08-28 17:32:39 +02:00
41eac31dba remove unneeded try catch error handling 2020-08-28 16:59:11 +02:00
6c0c2d23fe make fbevent properties immutable 2020-08-28 16:19:08 +02:00
16d390094e much refactoring:
-move event formatting logic to event class
-disable editing of event output, it's available in the calendar app
-replace string datetimes with ZonedDateZime
-move uri checking logic to scraper
-update exception handling and error messages
-reformatting and renaming
-fix messy xml layouts
-update tests
-add comments
2020-08-28 16:14:03 +02:00
05f3ba9a33 use regex find instead of replace for url matching 2020-08-26 18:47:44 +02:00
57b00e2e57 add maps intent
not gmaps exclusive
closes #9
2020-08-26 17:57:51 +02:00
9fd1a975ec prepare v0.2.2 2020-08-26 17:15:05 +02:00
d60717f0b7 switch to m.facebook scraping
more descriptive toolbar button string
update unit tests
2020-08-26 17:01:13 +02:00
98ab900b3d set banner as default toolbar image 2020-08-26 15:51:10 +02:00
5af23c1962 use regex to check if URL valid
fix querystrings issue, closes #17
2020-08-26 14:25:53 +02:00
868a5340e9 update icons and banners 2020-08-26 13:55:20 +02:00
c3d1227d4d update Screenshots 2020-08-15 19:42:53 +02:00
f9843b63bf Update README.md 2020-08-15 19:32:47 +02:00
fc9fb169ef prepare v0.2.1 2020-08-15 19:29:56 +02:00
a1c7720836 links in text color for better visibility 2020-08-15 16:38:12 +02:00
9ab18b496f update jsoup 2020-08-15 15:31:05 +02:00
40a2c7d18f cancel scraping if clear is pressed to prevent inconsitent cleared state
disable add button to prevent creating empty events
2020-08-15 13:51:12 +02:00
fd874d7cb0 fix calendar icon color for dark layout 2020-08-15 13:27:17 +02:00
eafa1bfc1b Merge branch 'master' of github.com:akaessens/NoFbEventScraper 2020-08-15 13:15:50 +02:00
bc9cf04f10 use helper error instead of toast
move clear fuction to text input
more concise add button
remove autocorrect o textfields
closes #10
2020-08-15 13:15:01 +02:00
ac98a4a43c fix tests,
minor design fixes
2020-08-15 11:42:56 +02:00
b9f9038f6e Update README.md 2020-08-15 01:00:03 +02:00
22591424fa prepare v0.2.0 2020-08-15 00:56:39 +02:00
e08497fd2d fix overlap of bottom nav bar, add error messages to fields 2020-08-15 00:31:43 +02:00
7c975b8d7e material design and image display 2020-08-14 23:58:29 +02:00
423642f7f7 Merge branch 'master' of github.com:akaessens/NoFbEventScraper 2020-07-29 19:39:51 +02:00
8a1f5f8c53 add unit tests, closes #12
fix for datetime timezone calculation
2020-07-29 19:38:39 +02:00
ef42b404e9 Update README.md 2020-07-29 16:37:02 +02:00
dbde1188dc version 0.1.1 ready 2020-07-29 16:29:29 +02:00
d37a8362a6 fix location detection
closes #4
2020-07-29 13:53:13 +02:00
53ff8e4609 add link name after fixing 2020-07-29 11:51:10 +02:00
b88d429af1 implement Links to pages
closes #3
2020-07-29 11:42:50 +02:00
bbadfef7d3 generate icons and banners
add donation button
2020-06-04 18:32:38 +02:00
3f37c9b2b3 add new help section 2020-06-04 14:04:15 +02:00
25f763ae1f fix gradle 2020-06-04 13:02:49 +02:00
3c119f8209 Update README.md 2020-06-04 09:57:20 +02:00
c9db3d61cd update gradle sha256 sum for github actions 2020-06-04 09:28:09 +02:00
43365350b7 Create android.yml 2020-06-04 09:24:20 +02:00
ed27561506 update to gradle 6.5 and remove .idea 2020-06-03 22:34:46 +02:00
5e90eea230 update gradle wrapper 2020-05-23 15:30:12 +02:00
88 changed files with 1533 additions and 773 deletions

21
.github/workflows/android.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: Android CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Gradle
run: ./gradlew build

1
.gitignore vendored
View File

@ -14,3 +14,4 @@ app/release
/captures
.externalNativeBuild
.cxx
.idea

1
.idea/.name generated
View File

@ -1 +0,0 @@
NoFb Event Scraper

View File

@ -1,116 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

20
.idea/gradle.xml generated
View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="PLATFORM" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>

9
.idea/misc.xml generated
View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

6
.idea/vcs.xml generated
View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -1,4 +1,8 @@
# NoFbEventScraper
![Android CI](https://github.com/akaessens/NoFbEventScraper/workflows/Android%20CI/badge.svg)
<img src="https://github.com/akaessens/NoFbEventScraper/raw/master/fastlane/metadata/android/en-US/images/featureGraphic.png" alt="Feature Graphic" width="max-width">
# NoFb Event Scraper
This application was developed to be used without a facebook account.
@ -6,7 +10,11 @@ Therefore it does not use the facebook API.
Instead it opens the facebook event URI and downloads the website source code.
This source contains the information which is used to create a calendar entry.
# Download
Currently this application is in the submitting process to FDroid. see also [#2](/../../issues/2).
# Screenshot
Until it is published at FDroid, use [release v0.3.1](https://github.com/akaessens/NoFbEventScraper/releases/download/v0.3.1/app-release.apk).
![Screenshot](https://raw.githubusercontent.com/akaessens/NoFbEventScraper/master/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png)
# Screenshots
<img src="https://github.com/akaessens/NoFbEventScraper/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png" alt="Screenshot 1" width="200"> <img src="https://github.com/akaessens/NoFbEventScraper/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png" alt="Screenshot 2" width="200"> <img src="https://github.com/akaessens/NoFbEventScraper/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png" alt="Screenshot 3" width="200"> <img src="https://github.com/akaessens/NoFbEventScraper/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png" alt="Screenshot 4" width="200">

View File

@ -6,10 +6,10 @@ android {
defaultConfig {
applicationId "com.akdev.nofbeventscraper"
minSdkVersion 26
minSdkVersion 23
targetSdkVersion 29
versionCode 1
versionName "0.1.0"
versionCode 8
versionName "0.3.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@ -24,18 +24,24 @@ android {
}
dependencies {
'androidx.coordinatorlayout:coordinatorlayout:1.1.0'
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.navigation:navigation-fragment:2.2.1'
implementation 'androidx.navigation:navigation-ui:2.2.1'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.0'
implementation 'androidx.navigation:navigation-fragment:2.3.0'
implementation 'androidx.navigation:navigation-ui:2.3.0'
// jsoup HTML parser library @ https://jsoup.org/
implementation 'org.jsoup:jsoup:1.11.1'
implementation 'org.jsoup:jsoup:1.13.1'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'androidx.preference:preference:1.1.1'
implementation "androidx.webkit:webkit:1.3.0"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

View File

@ -0,0 +1,88 @@
package com.akdev.nofbeventscraper;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import static org.junit.Assert.assertEquals;
@RunWith(AndroidJUnit4.class)
public class ScraperUnitTest {
@Test
public void testLocation() {
FbScraper scraper = new FbScraper(null, "");
String exp = "Deutschland";
String json = "{'@type': 'Place', 'name': 'Deutschland'}";
String act = scraper.fixLocation(json);
assertEquals(exp, act);
exp = "Example name, Example Street 1, 12345 Example city";
json = "{'@type': 'Place', 'name': 'Example name', 'address': {'@type': 'PostalAddress', 'addressCountry': 'DE', 'addressLocality': 'Example city', 'postalCode': '12345', 'streetAddress': 'Example Street 1'}}";
act = scraper.fixLocation(json);
assertEquals(exp, act);
exp = "";
json = "";
act = scraper.fixLocation(json);
assertEquals(exp, act);
}
@Test
public void testTimezone() {
FbScraper scraper = new FbScraper(null, "");
String in = "2020-01-01T12:00:00+0100";
String exp = "Mi., 01 Jan. 2020 12:00 MEZ";
String act = FbEvent.dateTimeToString(scraper.parseToDate(in));
assertEquals(exp, act);
exp = "";
in = "";
String act2 = FbEvent.dateTimeToString(scraper.parseToDate(in));
assertEquals(exp, act2);
}
@Test
public void testDescriptionLinks() {
FbScraper scraper = new FbScraper(null, "");
String in = "foo @[152580919265:274:MagentaMusik 360] bar";
String exp = "foo MagentaMusik 360 [m.facebook.com/152580919265] bar";
String act = scraper.fixDescriptionLinks(in);
assertEquals(exp, act);
in = "foo @[152580919265:274:MagentaMusik 360] bar @[666666666666:274:NoOfTheBeast]";
exp = "foo MagentaMusik 360 [m.facebook.com/152580919265] bar NoOfTheBeast [m.facebook.com/666666666666]";
act = scraper.fixDescriptionLinks(in);
assertEquals(exp, act);
}
@Test
public void testURI() throws MalformedURLException, URISyntaxException {
FbScraper scraper = new FbScraper(null, "");
String in = "https://www.facebook.com/events/1234324522341432?refsomething";
String exp = "https://m.facebook.com/events/1234324522341432";
String act = scraper.fixURI(in);
assertEquals(exp, act);
in = "https://de-de.facebook.com/events/1234324522341432/?active_tab=discussion";
act = scraper.fixURI(in);
assertEquals(exp, act);
}
}

View File

@ -11,11 +11,31 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".AboutActivity"></activity>
<activity android:name=".SettingsActivity"
android:label="@string/action_settings" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity"/>
</activity>
<activity android:name=".HelpActivity"
android:label="@string/action_help" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity"/>
</activity>
<activity android:name=".AboutActivity"
android:label="@string/action_about" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity"/>
</activity>
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,8 +1,13 @@
package com.akdev.nofbeventscraper;
import androidx.appcompat.app.AppCompatActivity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.webkit.WebView;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;
public class AboutActivity extends AppCompatActivity {
@ -10,5 +15,22 @@ public class AboutActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about);
ActionBar action_bar = getSupportActionBar();
if (action_bar != null) {
action_bar.setDisplayHomeAsUpEnabled(true);
}
WebView webview_about = findViewById(R.id.webview_about);
int night_mode_flags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
if (night_mode_flags == Configuration.UI_MODE_NIGHT_YES) {
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
WebSettingsCompat.setForceDark(webview_about.getSettings(),
WebSettingsCompat.FORCE_DARK_ON);
}
}
webview_about.loadUrl("file:///android_res/raw/about.html");
}
}

View File

@ -1,14 +1,27 @@
package com.akdev.nofbeventscraper;
public class FbEvent {
public String name;
public String start_date;
public String end_date;
public String description;
public String location;
public String image_url;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public FbEvent (String name, String start_date, String end_date, String description, String location, String image_url) {
/**
* Objects of this class store immutable information about
* a single event that was created by the FbScraper.
*/
public class FbEvent {
public final String url;
public final String name;
public final Date start_date;
public final Date end_date;
public final String description;
public final String location;
public final String image_url;
public FbEvent(String url, String name, Date start_date, Date end_date,
String description, String location, String image_url) {
this.url = url;
this.name = name;
this.start_date = start_date;
this.end_date = end_date;
@ -16,4 +29,33 @@ public class FbEvent {
this.location = location;
this.image_url = image_url;
}
/**
* Converts datetime to epoch.
*
* @param date Date object
* @return Event begin time in milliseconds from the epoch for calendar intent or null
*/
static Long dateTimeToEpoch(Date date) {
try {
return date.getTime();
} catch (Exception e) {
return null;
}
}
/**
* Returns a locally formatted String representation of a Date
*
* @param date
* @return locally formatted String of date or empty String
*/
static String dateTimeToString(Date date) {
try {
SimpleDateFormat formatter = new SimpleDateFormat("E, dd MMM yyyy HH:mm z", Locale.getDefault());
return formatter.format(date);
} catch (Exception e) {
return "";
}
}
}

View File

@ -1,15 +1,9 @@
package com.akdev.nofbeventscraper;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.google.android.material.textfield.TextInputEditText;
import androidx.preference.PreferenceManager;
import org.json.JSONException;
import org.json.JSONObject;
@ -17,102 +11,231 @@ import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This class can asynchronously scrape public facebook events
* and gather the most important information. It is stored in a FbEvent object.
*/
public class FbScraper extends AsyncTask<Void, Void, Void> {
private String url;
private String error;
private MainActivity main;
private int error;
private String input_url;
private WeakReference<MainActivity> main; // no context leak with WeakReference
private FbEvent event;
FbScraper(MainActivity main, String url) {
this.url = url;
/**
* Constructor with WeakReference to the main activity, to update it's text fields.
*
* @param main WeakReference of main activity to prevent context leak
* @param input_url Input url to scrape from
*/
FbScraper(WeakReference<MainActivity> main, String input_url) {
this.main = main;
this.input_url = input_url;
}
/**
* Strips the facebook event link of the input url.
*
* @param url input url
* @return facebook event url String if one was found
* @throws URISyntaxException if event not found
* @throws MalformedURLException
*/
protected String fixURI(String url) throws URISyntaxException, MalformedURLException {
// check for url format
new URL(url).toURI();
String regex = "(facebook.com/events/[0-9]*)(/\\?event_time_id=[0-9]*)?";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(url);
if (matcher.find()) {
String url_prefix = "https://m.";
if (main != null){
SharedPreferences shared_prefs = PreferenceManager.getDefaultSharedPreferences(main.get());
url_prefix = shared_prefs.getString("url_preference", url_prefix);
}
// rewrite url to m.facebook and dismiss any query strings or referrals
String ret = url_prefix + matcher.group(1);
if (matcher.group(2) != null) {
ret += matcher.group(2);
}
return ret;
} else {
throw new URISyntaxException(url, "Does not contain event.");
}
}
private String readFromLocJson(String location_json) {
String name = "";
String street_address = "";
String postal_code = "";
String address_locality = "";
/**
* Strips the event location from the json string.
* This can be a name only or a complete postal address.
* @param location_json JSON formatted string
* @return String representation of the location.
*/
protected String fixLocation(String location_json) {
String location_name = "";
try {
JSONObject reader = new JSONObject(location_json);
name = reader.getString("name");
location_name = reader.getString("name");
JSONObject address = reader.getJSONObject("address");
street_address = ", " + address.getString("streetAddress");
postal_code = ", " + address.getString("postalCode");
address_locality = " " + address.getString("addressLocality");
String type = address.getString("@type");
if (type.equals("PostalAddress")) {
String postal_code = address.getString("postalCode");
String address_locality = address.getString("addressLocality");
String street_address = address.getString("streetAddress");
// included in locality
//String address_country = address.getString("addressCountry");
return location_name + ", "
+ street_address + ", "
+ postal_code + " "
+ address_locality;
} else {
return location_name;
}
} catch (JSONException e) {
e.printStackTrace();
return location_name;
}
return name + street_address + postal_code + address_locality;
}
private String fixTimezone(String time_in) {
/**
* Parses a time string from the facebook event into a Date
*
* @param time_in time string from the event
* @return Date parsed from input or null
*/
protected Date parseToDate(String time_in) {
try {
// parse e.g. 2011-12-03T10:15:30+0100
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
Editable editable = new SpannableStringBuilder(time_in);
return editable.insert(22, ":").toString();
return sdf.parse(time_in);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* Replaces all occurrences of a facebook internal links in
* an event description into an actual URL.
*
* @param description_in description string from the event
* @return corrected String with internal links resolved
*/
protected String fixDescriptionLinks(String description_in) {
try {
/* @[152580919265:274:SiteDescription]
* to
* SiteDescription [m.facebook.com/152580919265] */
return description_in.replaceAll("@\\[([0-9]{10,}):[0-9]{3}:([^]]*)]",
"$2 [m.facebook.com/$1]");
} catch (Exception e) {
e.printStackTrace();
return description_in;
}
}
/**
* Read a single field from a JSONObject
* @param reader JSONObject to read from
* @param field Which field to read
* @return String of the value of the field or empty string
*/
private String readFromJson(JSONObject reader, String field) {
try {
return reader.getString(field);
}
catch (Exception e) {
} catch (Exception e) {
e.printStackTrace();
return null;
return "";
}
}
/**
* Started by scraper.execute().
* Gets the HTML doc from the input string and scrapes the event information from it.
* @param voids
* @return
*/
@Override
protected Void doInBackground(Void... voids) {
Document document = null;
try {
document = Jsoup.connect(url).get();
String url = fixURI(input_url);
// use default android user agent
String user_agent = "Mozilla/5.0 (X11; Linux x86_64)";
Document document = Jsoup.connect(url).userAgent(user_agent).get();
if (document == null) {
throw new IOException();
}
String json = document
.select("script[type = application/ld+json]")
.first().data();
JSONObject reader = new JSONObject(json);
String name = readFromJson(reader, "name");
Date start_date = parseToDate(readFromJson(reader, "startDate"));
Date end_date = parseToDate(readFromJson(reader, "endDate"));
String description = fixDescriptionLinks(readFromJson(reader, "description"));
String location = fixLocation(readFromJson(reader, "location"));
String image_url = readFromJson(reader, "image"); // get from json
try {
String json = document.select("script[type = application/ld+json]").first().data();
JSONObject reader = new JSONObject(json);
String event_name = readFromJson(reader, "name");
String event_start = fixTimezone(readFromJson(reader, "startDate"));
String event_end = fixTimezone(readFromJson(reader, "endDate"));
String event_description = readFromJson(reader, "description");
String location_json = readFromJson(reader, "location");
String location = readFromLocJson(location_json);
if (event_name == null) {
this.event = null;
throw new Exception();
} else {
this.event = new FbEvent(event_name, event_start, event_end, event_description, location, null);
}
// possibly get higher res image from event header
image_url = document.getElementsByClass("scaledImageFitWidth")
.first().attr("src");
} catch (Exception e) {
e.printStackTrace();
this.error = "Error: Scraping event data failed";
// ignore
}
event = new FbEvent(url, name,start_date, end_date, description, location, image_url);
} catch (URISyntaxException | MalformedURLException e) {
e.printStackTrace();
this.error = R.string.error_url;
} catch (JSONException e) {
e.printStackTrace();
this.error = R.string.error_scraping;
} catch (IOException e) {
e.printStackTrace();
this.error = "Error: URL not available";
this.error = R.string.error_connection;
} catch (Exception e) {
e.printStackTrace();
this.error = R.string.error_unknown;
}
return null;
}
@ -121,15 +244,21 @@ public class FbScraper extends AsyncTask<Void, Void, Void> {
super.onPreExecute();
}
/**
* When scraping is finished, main activity will be updated.
* If an error occurred, main activity is given an error string.
* @param aVoid
*/
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if (this.event != null) {
this.main.update(event);
}
else {
main.toast(error);
this.main.clear(false);
if (main != null) {
if (this.event != null) {
main.get().update(event);
} else {
main.get().error(error);
main.get().clear(false);
}
}
}
}

View File

@ -0,0 +1,38 @@
package com.akdev.nofbeventscraper;
import android.content.res.Configuration;
import android.os.Bundle;
import android.webkit.WebView;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;
public class HelpActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_help);
ActionBar action_bar = getSupportActionBar();
if (action_bar != null) {
action_bar.setDisplayHomeAsUpEnabled(true);
}
WebView webview_help = findViewById(R.id.webview_help);
int night_mode_flags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
if (night_mode_flags == Configuration.UI_MODE_NIGHT_YES) {
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
WebSettingsCompat.setForceDark(webview_help.getSettings(),
WebSettingsCompat.FORCE_DARK_ON);
}
}
webview_help.loadUrl("file:////android_res/raw/help.html");
}
}

View File

@ -1,41 +1,54 @@
package com.akdev.nofbeventscraper;
import android.app.AlertDialog;
import android.annotation.SuppressLint;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import com.google.android.material.textfield.TextInputEditText;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.provider.CalendarContract;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import android.widget.ImageView;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.menu.MenuBuilder;
import androidx.appcompat.widget.Toolbar;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import com.squareup.picasso.Picasso;
import java.lang.ref.WeakReference;
import java.util.Objects;
import static com.akdev.nofbeventscraper.FbEvent.dateTimeToEpoch;
public class MainActivity extends AppCompatActivity {
private Button paste_button;
private Button cancel_button;
private Button ok_button;
protected Button ok_button;
protected Button paste_button;
private TextInputEditText field_uri_input;
private TextInputEditText field_event_name;
private TextInputEditText field_event_start;
private TextInputEditText field_event_end;
private TextInputEditText field_event_location;
private TextInputEditText field_event_description;
protected TextInputEditText edit_text_uri_input;
protected TextInputEditText edit_text_event_name;
protected TextInputEditText edit_text_event_start;
protected TextInputEditText edit_text_event_end;
protected TextInputEditText edit_text_event_location;
protected TextInputEditText edit_text_event_description;
protected TextInputLayout layout_uri_input;
protected TextInputLayout layout_event_location;
protected ImageView image_view_toolbar;
protected CollapsingToolbarLayout layout_toolbar;
protected FbScraper scraper;
protected FbEvent event;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -44,77 +57,134 @@ public class MainActivity extends AppCompatActivity {
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
cancel_button = (Button) findViewById(R.id.cancel_button);
ok_button = (Button) findViewById(R.id.ok_button);
paste_button = (Button) findViewById(R.id.paste_button);
field_uri_input = (TextInputEditText) findViewById(R.id.field_uri_input);
field_event_name = (TextInputEditText) findViewById(R.id.field_event_name);
field_event_start = (TextInputEditText) findViewById(R.id.field_event_start);
field_event_end = (TextInputEditText) findViewById(R.id.field_event_end);
field_event_location = (TextInputEditText) findViewById(R.id.field_event_location);
field_event_description = (TextInputEditText) findViewById(R.id.field_event_description);
edit_text_uri_input = (TextInputEditText) findViewById(R.id.edit_text_uri_input);
edit_text_event_name = (TextInputEditText) findViewById(R.id.edit_text_event_name);
edit_text_event_start = (TextInputEditText) findViewById(R.id.edit_text_event_start);
edit_text_event_end = (TextInputEditText) findViewById(R.id.edit_text_event_end);
edit_text_event_location = (TextInputEditText) findViewById(R.id.edit_text_event_location);
edit_text_event_description = (TextInputEditText) findViewById(R.id.edit_text_event_description);
layout_uri_input = (TextInputLayout) findViewById(R.id.layout_uri_input);
layout_event_location = (TextInputLayout) findViewById(R.id.layout_event_location);
layout_toolbar = (CollapsingToolbarLayout) findViewById(R.id.layout_toolbar);
image_view_toolbar = (ImageView) findViewById(R.id.image_view);
/*
* Default view settings
*/
ok_button.setEnabled(false);
layout_event_location.setEndIconVisible(false);
image_view_toolbar.setImageResource(R.drawable.ic_banner_foreground);
/*
* Display title only when toolbar is collapsed
*/
AppBarLayout app_bar_layout = (AppBarLayout) findViewById(R.id.app_bar);
app_bar_layout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
boolean show = true;
int scroll_range = -1;
@Override
public void onOffsetChanged(AppBarLayout app_bar_layout, int vertical_offset) {
if (scroll_range == -1) {
scroll_range = app_bar_layout.getTotalScrollRange();
}
if (scroll_range + vertical_offset == 0) {
layout_toolbar.setTitle(getString(R.string.app_name));
show = true;
} else if (show) {
layout_toolbar.setTitle(" ");
show = false;
}
}
});
/*
* Paste button: get last entry from clipboard
*/
paste_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
String str = clipboard.getPrimaryClip().getItemAt(0).getText().toString();
String str = Objects.requireNonNull(clipboard.getPrimaryClip())
.getItemAt(0).getText().toString();
clear(true);
field_uri_input.setText(str);
}
catch (NullPointerException e) {
edit_text_uri_input.setText(str);
startScraping();
} catch (Exception e) {
e.printStackTrace();
toast("Error: Clipboard empty");
error(R.string.error_clipboard_empty);
}
startScraping();
}
});
cancel_button.setOnClickListener(new View.OnClickListener() {
/*
* Clear button: delete all text in all fields
*/
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View view) {
clear(true);
}
});
};
layout_uri_input.setEndIconOnClickListener(listener);
layout_uri_input.setErrorIconOnClickListener(listener);
ok_button.setOnClickListener(new View.OnClickListener() {
/*
* Maps button: launch maps intent
*/
layout_event_location.setEndIconOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
Long start_epoch = convertTimeToEpoch(field_event_start);
Long end_epoch = convertTimeToEpoch(field_event_end);
String map_search = "geo:0,0?q=" + edit_text_event_location.getText();
String name = parseField(field_event_name);
String location = parseField(field_event_location);
String description = parseField(field_event_description);
String uri = parseField(field_uri_input);
Intent intent = new Intent(Intent.ACTION_EDIT);
intent.setType("vnd.android.cursor.item/event");
intent.putExtra(CalendarContract.Events.TITLE, name);
intent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, start_epoch);
intent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end_epoch);
intent.putExtra(CalendarContract.Events.EVENT_LOCATION, location);
intent.putExtra(CalendarContract.Events.DESCRIPTION, uri + "\n" + description);
startActivity(intent);
Uri intent_uri = Uri.parse(map_search);
Intent map_intent = new Intent(Intent.ACTION_VIEW, intent_uri);
if (map_intent.resolveActivity(getPackageManager()) != null) {
startActivity(map_intent);
}
catch (Exception e )
{
e.printStackTrace();
toast("Error: Invalid fields");
}
}
});
field_uri_input.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View view, int keyCode, KeyEvent keyevent) {
//If the keyevent is a key-down event on the "enter" button
if ((keyevent.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
/*
* Add to calendar button: launch calendar application
*/
ok_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Long start_epoch = dateTimeToEpoch(event.start_date);
Long end_epoch = dateTimeToEpoch(event.end_date);
Intent intent = new Intent(Intent.ACTION_EDIT);
intent.setType("vnd.android.cursor.item/event");
intent.putExtra(CalendarContract.Events.TITLE, event.name);
intent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, start_epoch);
intent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end_epoch);
intent.putExtra(CalendarContract.Events.EVENT_LOCATION, event.location);
// prepend url in description
String desc = event.url + "\n\n" + event.description;
intent.putExtra(CalendarContract.Events.DESCRIPTION, desc);
startActivity(intent);
}
});
/*
* Enter button in uri input: start scraping
*/
edit_text_uri_input.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View view, int keycode, KeyEvent keyevent) {
//If the key event is a key-down event on the "enter" button
if ((keyevent.getAction() == KeyEvent.ACTION_DOWN) && (keycode == KeyEvent.KEYCODE_ENTER)) {
startScraping();
return true;
}
@ -122,131 +192,150 @@ public class MainActivity extends AppCompatActivity {
}
});
//get data from deep link
/*
* Get data from intent: if launched by other application
* via "share to" or "open with"
*/
Intent intent = getIntent();
Uri data = intent.getData();
String shared_text = intent.getStringExtra(Intent.EXTRA_TEXT);
if (data != null) {
// opening external fb link
field_uri_input.setText(data.toString());
edit_text_uri_input.setText(data.toString());
startScraping();
}
else if (shared_text != null) {
} else if (shared_text != null) {
//share to nofb
field_uri_input.setText(shared_text);
edit_text_uri_input.setText(shared_text);
startScraping();
}
}
private String parseField(TextInputEditText field) {
try {
return field.getText().toString();
}
catch (Exception e) {
return null;
}
}
private Long convertTimeToEpoch (TextInputEditText field) {
try {
String time_str = field.getText().toString();
LocalDateTime datetime = LocalDateTime.parse(time_str, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
return datetime.atZone(ZoneId.of("UTC")).toEpochSecond() * 1000;
} catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public static boolean isNumeric(String strNum) {
if (strNum == null) {
return false;
}
try {
double d = Double.parseDouble(strNum);
} catch (NumberFormatException e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* launch the FbScraper asynchronous task with the current text in the input text field.
*/
public void startScraping() {
error(null);
String url = Objects.requireNonNull(edit_text_uri_input.getText()).toString();
scraper = new FbScraper(new WeakReference<>(this), url);
scraper.execute();
}
public void error(Integer resId) {
if (resId != null) {
layout_uri_input.setError(getString(resId));
} else {
layout_uri_input.setError(null);
}
}
/**
* Clears all event text field strings and errors and also the input field depending if wanted.
* Loads the default banner into the toolbar image view and disables unneeded buttons.
*
* @param clear_uri Choose whether to clear the input uri field, too
*/
public void clear(boolean clear_uri) {
if (clear_uri) {
edit_text_uri_input.setText("");
layout_uri_input.setError(null);
}
edit_text_event_name.setText("");
edit_text_event_start.setText("");
edit_text_event_end.setText("");
edit_text_event_location.setText("");
edit_text_event_description.setText("");
edit_text_event_name.setError(null);
edit_text_event_start.setError(null);
edit_text_event_end.setError(null);
edit_text_event_location.setError(null);
edit_text_event_description.setError(null);
try {
String str = field_uri_input.getText().toString();
// check for a valid uri
new URL(str).toURI();
String eventId = null;
// check for facebook uri
if (str.contains("facebook.com/events/")) {
// find event id
String[] separated = str.split("/");
for (int i = 0; i< separated.length; i++) {
if (separated[i].length() > 8 && isNumeric(separated[i])) {
eventId = separated[i];
break;
}
}
if (eventId == null) // no event id found
{
throw new Exception();
}
}
else {
throw new Exception();
}
String input_uri = "https://m.facebook.com/events/"+ eventId;
field_uri_input.setText(input_uri);
FbScraper scraper = new FbScraper(this, field_uri_input.getText().toString());
scraper.execute();
}
catch (Exception e) {
scraper.cancel(true);
scraper = null;
} catch (Exception e) {
e.printStackTrace();
clear(true);
toast("Invalid URL");
}
ok_button.setEnabled(false);
layout_event_location.setEndIconVisible(false);
image_view_toolbar.setImageResource(R.drawable.ic_banner_foreground);
}
public void toast(String str) {
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
}
public void clear(boolean clearUri) {
if (clearUri) {
field_uri_input.setText("");
/**
* Updates the text fields with the event information provided.
* If something is missing, the corresponding test field will show an error.
*
* @param scraped_event the event information that was scraped by FbScraper
*/
public void update(FbEvent scraped_event) {
this.event = scraped_event;
edit_text_uri_input.setText(event.url);
if (event.name.equals("")) {
edit_text_event_name.setError(getString(R.string.error_no_name));
} else {
edit_text_event_name.setText(event.name);
}
field_event_name.setText("");
field_event_start.setText("");
field_event_end.setText("");
field_event_location.setText("");
field_event_description.setText("");
}
public void update(FbEvent event) {
field_event_name.setText(event.name);
field_event_start.setText(event.start_date);
field_event_end.setText(event.end_date);
field_event_location.setText(event.location);
field_event_description.setText(event.description);
if (event.start_date == null) {
edit_text_event_start.setError(getString(R.string.error_no_start_date));
} else {
String str = FbEvent.dateTimeToString(event.start_date);
edit_text_event_start.setText(str);
}
if (event.end_date == null) {
edit_text_event_end.setError(getString(R.string.error_no_end_date));
} else {
String str = FbEvent.dateTimeToString(event.end_date);
edit_text_event_end.setText(str);
}
if (event.location.equals("")) {
edit_text_event_location.setError(getString(R.string.error_no_location));
} else {
edit_text_event_location.setText(event.location);
layout_event_location.setEndIconVisible(true);
}
if (event.description.equals("")) {
edit_text_event_description.setError(getString(R.string.error_no_description));
} else {
edit_text_event_description.setText(event.description);
}
Picasso.get()
.load(event.image_url)
.placeholder(R.drawable.ic_banner_foreground)
.into(image_view_toolbar);
ok_button.setEnabled(true);
}
@SuppressLint("RestrictedApi")
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
if(menu instanceof MenuBuilder){
MenuBuilder m = (MenuBuilder) menu;
//noinspection RestrictedApi
m.setOptionalIconsVisible(true);
}
return true;
}
@ -262,6 +351,14 @@ public class MainActivity extends AppCompatActivity {
startActivity(new Intent(this, AboutActivity.class));
return true;
}
if (id == R.id.action_help) {
startActivity(new Intent(this, HelpActivity.class));
return true;
}
if (id == R.id.action_settings) {
startActivity(new Intent(this, SettingsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}

View File

@ -0,0 +1,31 @@
package com.akdev.nofbeventscraper;
import android.os.Bundle;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.PreferenceFragmentCompat;
public class SettingsActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.settings_activity);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.settings, new SettingsFragment())
.commit();
ActionBar action_bar = getSupportActionBar();
if (action_bar != null) {
action_bar.setDisplayHomeAsUpEnabled(true);
}
}
public static class SettingsFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.root_preferences, rootKey);
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size
android:height="8dp"
android:width="0dp"/>
</shape>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,2C8.14,2 5,5.14 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.86 -3.14,-7 -7,-7zM16,10h-3v3h-2v-3L8,10L8,8h3L11,5h2v3h3v2z"
android:fillColor="#000000"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M22,3L7,3c-0.69,0 -1.23,0.35 -1.59,0.88L0.37,11.45c-0.22,0.34 -0.22,0.77 0,1.11l5.04,7.56c0.36,0.52 0.9,0.88 1.59,0.88h15c1.1,0 2,-0.9 2,-2L24,5c0,-1.1 -0.9,-2 -2,-2zM18.3,16.3c-0.39,0.39 -1.02,0.39 -1.41,0L14,13.41l-2.89,2.89c-0.39,0.39 -1.02,0.39 -1.41,0 -0.39,-0.39 -0.39,-1.02 0,-1.41L12.59,12 9.7,9.11c-0.39,-0.39 -0.39,-1.02 0,-1.41 0.39,-0.39 1.02,-0.39 1.41,0L14,10.59l2.89,-2.89c0.39,-0.39 1.02,-0.39 1.41,0 0.39,0.39 0.39,1.02 0,1.41L15.41,12l2.89,2.89c0.38,0.38 0.38,1.02 0,1.41z"
android:fillColor="#000000"/>
</vector>

View File

@ -0,0 +1,111 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="320dp"
android:height="180dp"
android:viewportWidth="320"
android:viewportHeight="180">
<group android:scaleX="0.6666667"
android:scaleY="0.6666667"
android:translateX="53.333332"
android:translateY="30">
<group android:scaleX="0.3480663"
android:scaleY="0.3480663"
android:translateX="10.774034"
android:translateY="27">
<path
android:pathData="M90.7,80.5L270.3,80.5A10.2,10.2 0,0 1,280.5 90.7L280.5,130.3A10.2,10.2 0,0 1,270.3 140.5L90.7,140.5A10.2,10.2 0,0 1,80.5 130.3L80.5,90.7A10.2,10.2 0,0 1,90.7 80.5z"
android:fillColor="#74b42b"
android:strokeColor="#00000000"/>
<path
android:pathData="M80.5,130.5h200v100h-200z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
<path
android:pathData="M90.9,150.5L270.1,150.5A10.4,10.4 0,0 1,280.5 160.9L280.5,270.1A10.4,10.4 0,0 1,270.1 280.5L90.9,280.5A10.4,10.4 0,0 1,80.5 270.1L80.5,160.9A10.4,10.4 0,0 1,90.9 150.5z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
<path
android:pathData="M100.5,150.5h40v40h-40z"
android:fillColor="#b3b3b3"
android:strokeColor="#00000000"/>
<path
android:pathData="M160.5,150.5h40v40h-40z"
android:fillColor="#cccccc"
android:strokeColor="#00000000"/>
<path
android:pathData="M220.5,150.5h40v40h-40z"
android:fillColor="#e6e6e6"
android:strokeColor="#00000000"/>
<path
android:pathData="M100.5,210.5h40v40h-40z"
android:fillColor="#cccccc"
android:strokeColor="#00000000"/>
<path
android:pathData="M160.5,210.5h40v40h-40z"
android:fillColor="#e6e6e6"
android:strokeColor="#00000000"/>
<path
android:pathData="M220.5,210.5h40v40h-40z"
android:fillColor="#ebebeb"
android:strokeColor="#00000000"/>
<path
android:pathData="M215.5,245.5a40,40 0,1 0,80 0a40,40 0,1 0,-80 0z"
android:strokeWidth="2"
android:fillColor="#ffffff"
android:strokeColor="#cccccc"/>
<path
android:pathData="M228,238.17L249.07,238.17L255.5,218L261.93,238.17L283,238.17L266.21,251.67L272.87,273L255.5,259.8L238.13,273L244.79,251.67Z"
android:fillColor="#297da6"
android:strokeColor="#00000000"/>
<path
android:pathData="M223.5,70.5L237.5,70.5A3,3 0,0 1,240.5 73.5L240.5,107.5A3,3 0,0 1,237.5 110.5L223.5,110.5A3,3 0,0 1,220.5 107.5L220.5,73.5A3,3 0,0 1,223.5 70.5z"
android:strokeWidth="2"
android:fillColor="#cccccc"
android:strokeColor="#ffffff"/>
<path
android:pathData="M123.5,70.5L137.5,70.5A3,3 0,0 1,140.5 73.5L140.5,107.5A3,3 0,0 1,137.5 110.5L123.5,110.5A3,3 0,0 1,120.5 107.5L120.5,73.5A3,3 0,0 1,123.5 70.5z"
android:strokeWidth="2"
android:fillColor="#cccccc"
android:strokeColor="#ffffff"/>
</group>
<group android:scaleX="0.1252506"
android:scaleY="0.1252506"
android:translateX="128"
android:translateY="78.106575">
<group android:translateY="150.89062">
<path android:pathData="M88.8125,-0L80.4375,-0L22.296875,-88.03125L21.875,-87.96875L21.875,-0L13.4375,-0L13.4375,-102.375L21.875,-102.375L80.015625,-14.484375L80.4375,-14.5625L80.4375,-102.375L88.8125,-102.375L88.8125,-0Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M108.40625,-39.171875Q108.40625,-55.96875,117.609375,-66.71875Q126.828125,-77.484375,142.01562,-77.484375Q157.26562,-77.484375,166.46875,-66.71875Q175.6875,-55.96875,175.6875,-39.171875L175.6875,-36.84375Q175.6875,-19.96875,166.51562,-9.234375Q157.34375,1.484375,142.15625,1.484375Q126.828125,1.484375,117.609375,-9.234375Q108.40625,-19.96875,108.40625,-36.84375L108.40625,-39.171875ZM116.84375,-36.84375Q116.84375,-23.625,123.515625,-14.625Q130.20312,-5.625,142.15625,-5.625Q153.89062,-5.625,160.60938,-14.625Q167.32812,-23.625,167.32812,-36.84375L167.32812,-39.171875Q167.32812,-52.171875,160.57812,-61.234375Q153.82812,-70.3125,142.01562,-70.3125Q130.20312,-70.3125,123.515625,-61.234375Q116.84375,-52.171875,116.84375,-39.171875L116.84375,-36.84375Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M253.57812,-47.953125L203.79688,-47.953125L203.79688,0L195.4375,0L195.4375,-102.375L260.75,-102.375L260.75,-95.203125L203.79688,-95.203125L203.79688,-55.203125L253.57812,-55.203125L253.57812,-47.953125Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M337.42188,-35.9375Q337.42188,-18.84375,329.6875,-8.671875Q321.95312,1.484375,308.39062,1.484375Q300.01562,1.484375,293.96875,-1.859375Q287.92188,-5.203125,284.20312,-11.46875L283.28125,0L276.04688,0L276.04688,-109.6875L284.48438,-109.6875L284.48438,-64.125Q288.28125,-70.53125,294.1875,-74Q300.09375,-77.484375,308.25,-77.484375Q321.95312,-77.484375,329.6875,-66.546875Q337.42188,-55.625,337.42188,-37.40625L337.42188,-35.9375ZM328.92188,-37.40625Q328.92188,-51.96875,323.29688,-61.0625Q317.67188,-70.171875,306.625,-70.171875Q297.84375,-70.171875,292.45312,-65.8125Q287.07812,-61.453125,284.48438,-54.640625L284.48438,-20.25Q287.29688,-13.640625,292.875,-9.734375Q298.46875,-5.84375,306.76562,-5.84375Q317.8125,-5.84375,323.35938,-14Q328.92188,-22.15625,328.92188,-35.9375L328.92188,-37.40625Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M451.09375,-49.578125L401.79688,-49.578125L401.79688,-7.109375L458.26562,-7.109375L458.26562,0L393.4375,0L393.4375,-102.375L457.90625,-102.375L457.90625,-95.203125L401.79688,-95.203125L401.79688,-56.75L451.09375,-56.75L451.09375,-49.578125Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M494.76562,-19.0625L497.23438,-10.125L497.65625,-10.125L500.32812,-19.0625L521,-76.078125L529.71875,-76.078125L500.8125,0L494.0625,0L465.09375,-76.078125L473.8125,-76.078125L494.76562,-19.0625Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M571.65625,1.484375Q557.3906,1.484375,547.9219,-9.0625Q538.46875,-19.625,538.46875,-35.796875L538.46875,-39.65625Q538.46875,-55.96875,547.8594,-66.71875Q557.25,-77.484375,570.6094,-77.484375Q584.59375,-77.484375,592.6094,-68.625Q600.625,-59.765625,600.625,-45L600.625,-37.828125L546.90625,-37.828125L546.90625,-35.796875Q546.90625,-22.921875,553.71875,-14.265625Q560.5469,-5.625,571.65625,-5.625Q579.53125,-5.625,585.0781,-7.796875Q590.6406,-9.984375,594.5781,-14.0625L598.1719,-8.296875Q593.875,-3.796875,587.2969,-1.15625Q580.7344,1.484375,571.65625,1.484375ZM570.6094,-70.3125Q561.25,-70.3125,554.9219,-63.171875Q548.59375,-56.046875,547.5469,-45.28125L547.6875,-44.9375L592.2656,-44.9375L592.2656,-47.046875Q592.2656,-56.953125,586.4219,-63.625Q580.59375,-70.3125,570.6094,-70.3125Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M624.5625,-76.078125L625.34375,-62.796875Q629,-69.828125,635.21875,-73.65625Q641.4375,-77.484375,649.9531,-77.484375Q662.3281,-77.484375,668.7969,-69.921875Q675.2656,-62.375,675.2656,-46.34375L675.2656,0L666.8281,0L666.8281,-46.265625Q666.8281,-59.34375,661.9375,-64.75Q657.0469,-70.171875,648.0469,-70.171875Q639.1875,-70.171875,633.59375,-65.5Q628.0156,-60.828125,625.5469,-53.09375L625.5469,0L617.1094,0L617.1094,-76.078125L624.5625,-76.078125Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M710.3281,-95.5625L710.3281,-76.078125L727.0625,-76.078125L727.0625,-69.1875L710.3281,-69.1875L710.3281,-19.625Q710.3281,-12.171875,713.03125,-9.03125Q715.75,-5.90625,720.25,-5.90625Q722.28125,-5.90625,724.21875,-6.078125Q726.15625,-6.265625,728.6875,-6.6875L729.9531,-0.421875Q727.84375,0.5,724.8125,0.984375Q721.7969,1.484375,718.7656,1.484375Q710.75,1.484375,706.28125,-3.578125Q701.8281,-8.65625,701.8281,-19.625L701.8281,-69.1875L688.6719,-69.1875L688.6719,-76.078125L701.8281,-76.078125L701.8281,-95.5625L710.3281,-95.5625Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M840,-25.109375Q840,-33.46875,833.7344,-38.78125Q827.4844,-44.09375,811.9531,-48.171875Q795.1406,-52.390625,786.4844,-59.140625Q777.84375,-65.890625,777.84375,-77.34375Q777.84375,-89.09375,787.5156,-96.46875Q797.1875,-103.859375,812.375,-103.859375Q828.1875,-103.859375,837.8906,-95.0625Q847.59375,-86.28125,847.3125,-73.625L847.1094,-73.203125L839.375,-73.203125Q839.375,-83.46875,832.09375,-90.078125Q824.8125,-96.6875,812.375,-96.6875Q800.0625,-96.6875,793.1406,-91.203125Q786.21875,-85.71875,786.21875,-77.5625Q786.21875,-69.828125,792.6094,-64.65625Q799.0156,-59.484375,814.6875,-55.546875Q831.21875,-51.328125,839.8281,-44.15625Q848.4375,-36.984375,848.4375,-25.25Q848.4375,-13.015625,838.4219,-5.765625Q828.40625,1.484375,812.8594,1.484375Q797.53125,1.484375,786.1094,-6.21875Q774.6875,-13.921875,775.03125,-28.6875L775.1719,-29.109375L782.90625,-29.109375Q782.90625,-17.15625,791.8281,-11.390625Q800.7656,-5.625,812.8594,-5.625Q825.03125,-5.625,832.5156,-10.96875Q840,-16.3125,840,-25.109375Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M893.3125,-5.625Q901.6719,-5.625,908.0625,-10.40625Q914.46875,-15.1875,914.46875,-23.703125L921.9219,-23.703125L922.0625,-23.28125Q922.4219,-12.734375,913.625,-5.625Q904.84375,1.484375,893.3125,1.484375Q877.7656,1.484375,869.2969,-9.171875Q860.8281,-19.828125,860.8281,-36.5625L860.8281,-39.515625Q860.8281,-56.109375,869.3281,-66.796875Q877.84375,-77.484375,893.2344,-77.484375Q905.75,-77.484375,914.0781,-70.09375Q922.4219,-62.71875,922.1406,-50.703125L922,-50.28125L914.46875,-50.28125Q914.46875,-59.421875,908.3125,-64.859375Q902.1719,-70.3125,893.2344,-70.3125Q881,-70.3125,875.125,-61.5625Q869.2656,-52.8125,869.2656,-39.515625L869.2656,-36.5625Q869.2656,-23.0625,875.09375,-14.34375Q880.9375,-5.625,893.3125,-5.625Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M974.125,-68.84375L967.875,-69.265625Q959.9219,-69.265625,954.8281,-64.796875Q949.7344,-60.328125,947.5469,-52.53125L947.5469,0L939.1094,0L939.1094,-76.078125L946.5,-76.078125L947.5469,-63.421875L947.5469,-62.578125Q950.8594,-69.609375,956.40625,-73.546875Q961.96875,-77.484375,969.625,-77.484375Q971.25,-77.484375,972.75,-77.234375Q974.2656,-77,975.25,-76.71875L974.125,-68.84375Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M1036.2812,0Q1035.4375,-3.875,1035.0781,-6.6875Q1034.7344,-9.5,1034.7344,-12.375Q1030.5156,-6.40625,1023.4844,-2.453125Q1016.4531,1.484375,1007.65625,1.484375Q996.5469,1.484375,990.3906,-4.421875Q984.25,-10.34375,984.25,-20.25Q984.25,-30.796875,993.34375,-37.125Q1002.4531,-43.453125,1017.9219,-43.453125L1034.7344,-43.453125L1034.7344,-52.875Q1034.7344,-61.03125,1029.4844,-65.671875Q1024.25,-70.3125,1014.8281,-70.3125Q1006.0469,-70.3125,1000.2344,-65.875Q994.4375,-61.453125,994.4375,-55.0625L986.7031,-55.125L986.5625,-55.546875Q986.1406,-64.0625,994.3281,-70.765625Q1002.53125,-77.484375,1015.1875,-77.484375Q1027.7656,-77.484375,1035.4219,-71.078125Q1043.0938,-64.6875,1043.0938,-52.734375L1043.0938,-15.328125Q1043.0938,-11.328125,1043.5469,-7.53125Q1044.0156,-3.734375,1045.0625,0L1036.2812,0ZM1008.6406,-5.984375Q1017.71875,-5.984375,1024.6719,-10.234375Q1031.6406,-14.484375,1034.7344,-21.09375L1034.7344,-36.984375L1017.78125,-36.984375Q1006.3281,-36.984375,999.46875,-32.09375Q992.6094,-27.21875,992.6094,-19.96875Q992.6094,-13.78125,996.8594,-9.875Q1001.125,-5.984375,1008.6406,-5.984375Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M1126.3594,-35.9375Q1126.3594,-18.84375,1118.625,-8.671875Q1110.8906,1.484375,1097.3125,1.484375Q1089.2969,1.484375,1083.2812,-1.546875Q1077.2812,-4.578125,1073.4844,-10.203125L1073.4844,29.25L1065.0469,29.25L1065.0469,-76.078125L1072.1406,-76.078125L1073.2031,-64.265625Q1077,-70.59375,1083,-74.03125Q1089.0156,-77.484375,1097.1719,-77.484375Q1110.8906,-77.484375,1118.625,-66.546875Q1126.3594,-55.625,1126.3594,-37.40625L1126.3594,-35.9375ZM1117.9219,-37.40625Q1117.9219,-51.890625,1112.1562,-61.03125Q1106.3906,-70.171875,1095.2812,-70.171875Q1086.7656,-70.171875,1081.4531,-66.125Q1076.1562,-62.09375,1073.4844,-55.625L1073.4844,-19.0625Q1076.3594,-12.796875,1081.8125,-9.203125Q1087.2656,-5.625,1095.4219,-5.625Q1106.4531,-5.625,1112.1875,-13.921875Q1117.9219,-22.21875,1117.9219,-35.9375L1117.9219,-37.40625Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M1173.6562,1.484375Q1159.3906,1.484375,1149.9219,-9.0625Q1140.4688,-19.625,1140.4688,-35.796875L1140.4688,-39.65625Q1140.4688,-55.96875,1149.8594,-66.71875Q1159.25,-77.484375,1172.6094,-77.484375Q1186.5938,-77.484375,1194.6094,-68.625Q1202.625,-59.765625,1202.625,-45L1202.625,-37.828125L1148.9062,-37.828125L1148.9062,-35.796875Q1148.9062,-22.921875,1155.7188,-14.265625Q1162.5469,-5.625,1173.6562,-5.625Q1181.5312,-5.625,1187.0781,-7.796875Q1192.6406,-9.984375,1196.5781,-14.0625L1200.1719,-8.296875Q1195.875,-3.796875,1189.2969,-1.15625Q1182.7344,1.484375,1173.6562,1.484375ZM1172.6094,-70.3125Q1163.25,-70.3125,1156.9219,-63.171875Q1150.5938,-56.046875,1149.5469,-45.28125L1149.6875,-44.9375L1194.2656,-44.9375L1194.2656,-47.046875Q1194.2656,-56.953125,1188.4219,-63.625Q1182.5938,-70.3125,1172.6094,-70.3125Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M1254.125,-68.84375L1247.875,-69.265625Q1239.9219,-69.265625,1234.8281,-64.796875Q1229.7344,-60.328125,1227.5469,-52.53125L1227.5469,0L1219.1094,0L1219.1094,-76.078125L1226.5,-76.078125L1227.5469,-63.421875L1227.5469,-62.578125Q1230.8594,-69.609375,1236.4062,-73.546875Q1241.9688,-77.484375,1249.625,-77.484375Q1251.25,-77.484375,1252.75,-77.234375Q1254.2656,-77,1255.25,-76.71875L1254.125,-68.84375Z"
android:fillColor="#FFFFFF"/>
</group>
</group>
</group>
</vector>

View File

@ -0,0 +1,63 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="120dp"
android:height="120dp"
android:viewportWidth="120"
android:viewportHeight="120">
<group android:scaleX="0.078457944"
android:scaleY="0.078457944"
android:translateX="34.815"
android:translateY="34.815">
<path
android:pathData="M0.5,0.5h640v640h-640z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
<path
android:pathData="M0.5,120.5h640v410h-640z"
android:fillColor="#297da6"
android:strokeColor="#00000000"/>
<path
android:pathData="M6.5,40.5L634.5,40.5A6,6 0,0 1,640.5 46.5L640.5,134.5A6,6 0,0 1,634.5 140.5L6.5,140.5A6,6 0,0 1,0.5 134.5L0.5,46.5A6,6 0,0 1,6.5 40.5z"
android:fillColor="#74b42b"
android:strokeColor="#00000000"/>
<path
android:pathData="M519.5,0.5L561.5,0.5A9,9 0,0 1,570.5 9.5L570.5,101.5A9,9 0,0 1,561.5 110.5L519.5,110.5A9,9 0,0 1,510.5 101.5L510.5,9.5A9,9 0,0 1,519.5 0.5z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
<path
android:pathData="M399.5,0.5L441.5,0.5A9,9 0,0 1,450.5 9.5L450.5,101.5A9,9 0,0 1,441.5 110.5L399.5,110.5A9,9 0,0 1,390.5 101.5L390.5,9.5A9,9 0,0 1,399.5 0.5z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
<path
android:pathData="M199.5,0.5L241.5,0.5A9,9 0,0 1,250.5 9.5L250.5,101.5A9,9 0,0 1,241.5 110.5L199.5,110.5A9,9 0,0 1,190.5 101.5L190.5,9.5A9,9 0,0 1,199.5 0.5z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
<path
android:pathData="M79.5,0.5L121.5,0.5A9,9 0,0 1,130.5 9.5L130.5,101.5A9,9 0,0 1,121.5 110.5L79.5,110.5A9,9 0,0 1,70.5 101.5L70.5,9.5A9,9 0,0 1,79.5 0.5z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
<path
android:pathData="M526.5,10.5L554.5,10.5A6,6 0,0 1,560.5 16.5L560.5,94.5A6,6 0,0 1,554.5 100.5L526.5,100.5A6,6 0,0 1,520.5 94.5L520.5,16.5A6,6 0,0 1,526.5 10.5z"
android:fillColor="#42a3d2"
android:strokeColor="#00000000"/>
<path
android:pathData="M406.5,10.5L434.5,10.5A6,6 0,0 1,440.5 16.5L440.5,94.5A6,6 0,0 1,434.5 100.5L406.5,100.5A6,6 0,0 1,400.5 94.5L400.5,16.5A6,6 0,0 1,406.5 10.5z"
android:fillColor="#42a3d2"
android:strokeColor="#00000000"/>
<path
android:pathData="M86.5,10.5L114.5,10.5A6,6 0,0 1,120.5 16.5L120.5,94.5A6,6 0,0 1,114.5 100.5L86.5,100.5A6,6 0,0 1,80.5 94.5L80.5,16.5A6,6 0,0 1,86.5 10.5z"
android:fillColor="#42a3d2"
android:strokeColor="#00000000"/>
<path
android:pathData="M206.5,10.5L234.5,10.5A6,6 0,0 1,240.5 16.5L240.5,94.5A6,6 0,0 1,234.5 100.5L206.5,100.5A6,6 0,0 1,200.5 94.5L200.5,16.5A6,6 0,0 1,206.5 10.5z"
android:fillColor="#42a3d2"
android:strokeColor="#00000000"/>
<path
android:pathData="M9.5,490.5L631.5,490.5A9,9 0,0 1,640.5 499.5L640.5,631.5A9,9 0,0 1,631.5 640.5L9.5,640.5A9,9 0,0 1,0.5 631.5L0.5,499.5A9,9 0,0 1,9.5 490.5z"
android:fillColor="#297da6"
android:strokeColor="#00000000"/>
<path
android:pathData="M155.5,295.5L281.94,295.5L320.5,174.5L359.06,295.5L485.5,295.5L384.76,376.53L424.71,504.5L320.5,425.3L216.29,504.5L256.24,376.53Z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
</group>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M19,2h-4.18C14.4,0.84 13.3,0 12,0c-1.3,0 -2.4,0.84 -2.82,2L5,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM19,20L5,20L5,4h2v3h10L17,4h2v16z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M16.53,11.06L15.47,10l-4.88,4.88 -2.12,-2.12 -1.06,1.06L10.59,17l5.94,-5.94zM19,3h-1L18,1h-2v2L8,3L8,1L6,1v2L5,3c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19L5,19L5,8h14v11z"
android:fillColor="#000000"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,19h-2v-2h2v2zM15.07,11.25l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2L8,9c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z"
android:fillColor="#000000"/>
</vector>

View File

@ -1,58 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="642dp"
android:height="642dp"
android:viewportWidth="642"
android:viewportHeight="642">
<path
android:pathData="M0.5,0.5h640v640h-640z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
<path
android:pathData="M0.5,120.5h640v410h-640z"
android:fillColor="#297da6"
android:strokeColor="#00000000"/>
<path
android:pathData="M6.5,40.5L634.5,40.5A6,6 0,0 1,640.5 46.5L640.5,134.5A6,6 0,0 1,634.5 140.5L6.5,140.5A6,6 0,0 1,0.5 134.5L0.5,46.5A6,6 0,0 1,6.5 40.5z"
android:fillColor="#74b42b"
android:strokeColor="#00000000"/>
<path
android:pathData="M519.5,0.5L561.5,0.5A9,9 0,0 1,570.5 9.5L570.5,101.5A9,9 0,0 1,561.5 110.5L519.5,110.5A9,9 0,0 1,510.5 101.5L510.5,9.5A9,9 0,0 1,519.5 0.5z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
<path
android:pathData="M399.5,0.5L441.5,0.5A9,9 0,0 1,450.5 9.5L450.5,101.5A9,9 0,0 1,441.5 110.5L399.5,110.5A9,9 0,0 1,390.5 101.5L390.5,9.5A9,9 0,0 1,399.5 0.5z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
<path
android:pathData="M199.5,0.5L241.5,0.5A9,9 0,0 1,250.5 9.5L250.5,101.5A9,9 0,0 1,241.5 110.5L199.5,110.5A9,9 0,0 1,190.5 101.5L190.5,9.5A9,9 0,0 1,199.5 0.5z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
<path
android:pathData="M79.5,0.5L121.5,0.5A9,9 0,0 1,130.5 9.5L130.5,101.5A9,9 0,0 1,121.5 110.5L79.5,110.5A9,9 0,0 1,70.5 101.5L70.5,9.5A9,9 0,0 1,79.5 0.5z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
<path
android:pathData="M526.5,10.5L554.5,10.5A6,6 0,0 1,560.5 16.5L560.5,94.5A6,6 0,0 1,554.5 100.5L526.5,100.5A6,6 0,0 1,520.5 94.5L520.5,16.5A6,6 0,0 1,526.5 10.5z"
android:fillColor="#42a3d2"
android:strokeColor="#00000000"/>
<path
android:pathData="M406.5,10.5L434.5,10.5A6,6 0,0 1,440.5 16.5L440.5,94.5A6,6 0,0 1,434.5 100.5L406.5,100.5A6,6 0,0 1,400.5 94.5L400.5,16.5A6,6 0,0 1,406.5 10.5z"
android:fillColor="#42a3d2"
android:strokeColor="#00000000"/>
<path
android:pathData="M86.5,10.5L114.5,10.5A6,6 0,0 1,120.5 16.5L120.5,94.5A6,6 0,0 1,114.5 100.5L86.5,100.5A6,6 0,0 1,80.5 94.5L80.5,16.5A6,6 0,0 1,86.5 10.5z"
android:fillColor="#42a3d2"
android:strokeColor="#00000000"/>
<path
android:pathData="M206.5,10.5L234.5,10.5A6,6 0,0 1,240.5 16.5L240.5,94.5A6,6 0,0 1,234.5 100.5L206.5,100.5A6,6 0,0 1,200.5 94.5L200.5,16.5A6,6 0,0 1,206.5 10.5z"
android:fillColor="#42a3d2"
android:strokeColor="#00000000"/>
<path
android:pathData="M9.5,490.5L631.5,490.5A9,9 0,0 1,640.5 499.5L640.5,631.5A9,9 0,0 1,631.5 640.5L9.5,640.5A9,9 0,0 1,0.5 631.5L0.5,499.5A9,9 0,0 1,9.5 490.5z"
android:fillColor="#297da6"
android:strokeColor="#00000000"/>
<path
android:pathData="M155.5,295.5L281.94,295.5L320.5,174.5L359.06,295.5L485.5,295.5L384.76,376.53L424.71,504.5L320.5,425.3L216.29,504.5L256.24,376.53Z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"
android:fillColor="#000000"/>
</vector>

View File

@ -1,61 +1,66 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="1605"
android:viewportHeight="1605">
<group android:translateX="481.5"
android:translateY="481.5">
android:viewportWidth="108"
android:viewportHeight="108">
<group android:scaleX="0.21480663"
android:scaleY="0.21480663"
android:translateX="15.227404"
android:translateY="15.12">
<path
android:pathData="M0.5,0.5h640v640h-640z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
<path
android:pathData="M0.5,120.5h640v410h-640z"
android:fillColor="#297da6"
android:strokeColor="#00000000"/>
<path
android:pathData="M6.5,40.5L634.5,40.5A6,6 0,0 1,640.5 46.5L640.5,134.5A6,6 0,0 1,634.5 140.5L6.5,140.5A6,6 0,0 1,0.5 134.5L0.5,46.5A6,6 0,0 1,6.5 40.5z"
android:pathData="M90.7,80.5L270.3,80.5A10.2,10.2 0,0 1,280.5 90.7L280.5,130.3A10.2,10.2 0,0 1,270.3 140.5L90.7,140.5A10.2,10.2 0,0 1,80.5 130.3L80.5,90.7A10.2,10.2 0,0 1,90.7 80.5z"
android:fillColor="#74b42b"
android:strokeColor="#00000000"/>
<path
android:pathData="M519.5,0.5L561.5,0.5A9,9 0,0 1,570.5 9.5L570.5,101.5A9,9 0,0 1,561.5 110.5L519.5,110.5A9,9 0,0 1,510.5 101.5L510.5,9.5A9,9 0,0 1,519.5 0.5z"
android:pathData="M80.5,130.5h200v100h-200z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
<path
android:pathData="M399.5,0.5L441.5,0.5A9,9 0,0 1,450.5 9.5L450.5,101.5A9,9 0,0 1,441.5 110.5L399.5,110.5A9,9 0,0 1,390.5 101.5L390.5,9.5A9,9 0,0 1,399.5 0.5z"
android:pathData="M90.9,150.5L270.1,150.5A10.4,10.4 0,0 1,280.5 160.9L280.5,270.1A10.4,10.4 0,0 1,270.1 280.5L90.9,280.5A10.4,10.4 0,0 1,80.5 270.1L80.5,160.9A10.4,10.4 0,0 1,90.9 150.5z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
<path
android:pathData="M199.5,0.5L241.5,0.5A9,9 0,0 1,250.5 9.5L250.5,101.5A9,9 0,0 1,241.5 110.5L199.5,110.5A9,9 0,0 1,190.5 101.5L190.5,9.5A9,9 0,0 1,199.5 0.5z"
android:pathData="M100.5,150.5h40v40h-40z"
android:fillColor="#b3b3b3"
android:strokeColor="#00000000"/>
<path
android:pathData="M160.5,150.5h40v40h-40z"
android:fillColor="#cccccc"
android:strokeColor="#00000000"/>
<path
android:pathData="M220.5,150.5h40v40h-40z"
android:fillColor="#e6e6e6"
android:strokeColor="#00000000"/>
<path
android:pathData="M100.5,210.5h40v40h-40z"
android:fillColor="#cccccc"
android:strokeColor="#00000000"/>
<path
android:pathData="M160.5,210.5h40v40h-40z"
android:fillColor="#e6e6e6"
android:strokeColor="#00000000"/>
<path
android:pathData="M220.5,210.5h40v40h-40z"
android:fillColor="#ebebeb"
android:strokeColor="#00000000"/>
<path
android:pathData="M215.5,245.5a40,40 0,1 0,80 0a40,40 0,1 0,-80 0z"
android:strokeWidth="2"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
android:strokeColor="#cccccc"/>
<path
android:pathData="M79.5,0.5L121.5,0.5A9,9 0,0 1,130.5 9.5L130.5,101.5A9,9 0,0 1,121.5 110.5L79.5,110.5A9,9 0,0 1,70.5 101.5L70.5,9.5A9,9 0,0 1,79.5 0.5z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
<path
android:pathData="M526.5,10.5L554.5,10.5A6,6 0,0 1,560.5 16.5L560.5,94.5A6,6 0,0 1,554.5 100.5L526.5,100.5A6,6 0,0 1,520.5 94.5L520.5,16.5A6,6 0,0 1,526.5 10.5z"
android:fillColor="#42a3d2"
android:strokeColor="#00000000"/>
<path
android:pathData="M406.5,10.5L434.5,10.5A6,6 0,0 1,440.5 16.5L440.5,94.5A6,6 0,0 1,434.5 100.5L406.5,100.5A6,6 0,0 1,400.5 94.5L400.5,16.5A6,6 0,0 1,406.5 10.5z"
android:fillColor="#42a3d2"
android:strokeColor="#00000000"/>
<path
android:pathData="M86.5,10.5L114.5,10.5A6,6 0,0 1,120.5 16.5L120.5,94.5A6,6 0,0 1,114.5 100.5L86.5,100.5A6,6 0,0 1,80.5 94.5L80.5,16.5A6,6 0,0 1,86.5 10.5z"
android:fillColor="#42a3d2"
android:strokeColor="#00000000"/>
<path
android:pathData="M206.5,10.5L234.5,10.5A6,6 0,0 1,240.5 16.5L240.5,94.5A6,6 0,0 1,234.5 100.5L206.5,100.5A6,6 0,0 1,200.5 94.5L200.5,16.5A6,6 0,0 1,206.5 10.5z"
android:fillColor="#42a3d2"
android:strokeColor="#00000000"/>
<path
android:pathData="M9.5,490.5L631.5,490.5A9,9 0,0 1,640.5 499.5L640.5,631.5A9,9 0,0 1,631.5 640.5L9.5,640.5A9,9 0,0 1,0.5 631.5L0.5,499.5A9,9 0,0 1,9.5 490.5z"
android:pathData="M228,238.17L249.07,238.17L255.5,218L261.93,238.17L283,238.17L266.21,251.67L272.87,273L255.5,259.8L238.13,273L244.79,251.67Z"
android:fillColor="#297da6"
android:strokeColor="#00000000"/>
<path
android:pathData="M155.5,295.5L281.94,295.5L320.5,174.5L359.06,295.5L485.5,295.5L384.76,376.53L424.71,504.5L320.5,425.3L216.29,504.5L256.24,376.53Z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"/>
android:pathData="M223.5,70.5L237.5,70.5A3,3 0,0 1,240.5 73.5L240.5,107.5A3,3 0,0 1,237.5 110.5L223.5,110.5A3,3 0,0 1,220.5 107.5L220.5,73.5A3,3 0,0 1,223.5 70.5z"
android:strokeWidth="2"
android:fillColor="#cccccc"
android:strokeColor="#ffffff"/>
<path
android:pathData="M123.5,70.5L137.5,70.5A3,3 0,0 1,140.5 73.5L140.5,107.5A3,3 0,0 1,137.5 110.5L123.5,110.5A3,3 0,0 1,120.5 107.5L120.5,73.5A3,3 0,0 1,123.5 70.5z"
android:strokeWidth="2"
android:fillColor="#cccccc"
android:strokeColor="#ffffff"/>
</group>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z"
android:fillColor="#000000"/>
</vector>

View File

@ -1,75 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".AboutActivity">
<ScrollView
<WebView
android:id="@+id/webview_about"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/about_heading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/action_about"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<TextView
android:id="@+id/about_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/about_text"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
<TextView
android:id="@+id/open_source_heading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:text="@string/open_source_heading"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<TextView
android:id="@+id/open_source_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:autoLink="web"
android:text="@string/open_source_text"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
<TextView
android:id="@+id/changelog_heading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:text="@string/changelog_heading"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<TextView
android:id="@+id/changelog_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:autoLink="web"
android:text="@string/changelog_text"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
</RelativeLayout>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".HelpActivity">
<WebView
android:id="@+id/webview_help"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>

View File

@ -1,29 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="180dp"
android:fitsSystemWindows="true"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/layout_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
android:layout_height="match_parent"
android:background="@color/colorPrimary"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleGravity="center"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:toolbarId="@+id/toolbar">
<!-- android:src="@drawable/ic_banner_foreground"-->
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:title="NoFb Event Scraper" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<include
layout="@layout/content_main"
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/paste_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="64dp"
app:layout_anchor="@id/app_bar"
app:layout_anchorGravity="bottom|end"
app:icon="@drawable/ic_content_paste"
android:text="@android:string/paste"
android:tooltipText="@string/tooltip_paste"/>
<include layout="@layout/content_main" />
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bottom_appbar"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_margin="0dp"
app:contentInsetStart="0dp">
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<Button
android:id="@+id/ok_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_weight="1"
android:padding="12dp"
android:text="@string/button_add"
android:textColor="#FFFFFF"
app:cornerRadius="24dp"
app:icon="@drawable/ic_event_available"
app:iconGravity="textStart"
app:iconPadding="16dp"
app:iconTint="#FFFFFF" />
</com.google.android.material.bottomappbar.BottomAppBar>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,171 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nested_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:animateLayoutChanges="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".MainActivity"
tools:showIn="@layout/activity_main">
<LinearLayout
android:id="@+id/linearLayout4"
android:layout_width="0dp"
android:id="@+id/linear_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="64dp"
android:layout_marginEnd="16dp"
android:gravity="center"
android:layout_marginTop="32dp"
android:layout_marginBottom="64dp"
android:divider="@drawable/divider"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
android:showDividers="middle">
<Button
android:id="@+id/paste_button"
style="@style/Widget.AppCompat.Button"
android:layout_width="wrap_content"
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/layout_uri_input"
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@android:string/paste" />
app:endIconCheckable="false"
app:endIconDrawable="@drawable/ic_backspace_black"
app:errorIconDrawable="@drawable/ic_backspace_black"
app:endIconMode="custom"
app:helperText="@string/helper_add_link"
app:helperTextEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edit_text_uri_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:cursorVisible="true"
android:hint="@string/hint_add_link"
android:inputType="textNoSuggestions"
android:singleLine="true"
android:textColorLink="@color/material_on_background_emphasis_high_type" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edit_text_event_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:cursorVisible="false"
android:hint="@string/hint_event_name"
android:inputType="textNoSuggestions"
android:singleLine="true"
android:textColorLink="@color/material_on_background_emphasis_high_type" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edit_text_event_start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:cursorVisible="false"
android:hint="@string/hint_event_start"
android:inputType="textNoSuggestions"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edit_text_event_end"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:cursorVisible="false"
android:hint="@string/hint_event_end"
android:inputType="textNoSuggestions"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/layout_event_location"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:endIconDrawable="@drawable/ic_add_location"
app:endIconMode="custom">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edit_text_event_location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:cursorVisible="false"
android:hint="@string/hint_event_location"
android:inputType="textNoSuggestions"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edit_text_event_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:focusable="false"
android:cursorVisible="false"
android:hint="@string/hint_event_description"
android:inputType="textNoSuggestions|textMultiLine"
android:singleLine="false"
android:textColorLink="@color/material_on_background_emphasis_high_type" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayout4">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/field_uri_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:clickable="true"
android:cursorVisible="true"
android:hint="@string/add_link_hint"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<ScrollView
android:id="@+id/scrollView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toTopOf="@+id/bottomButtonLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textInputLayout">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/field_event_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:cursorVisible="true"
android:hint="Event name"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/field_event_start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:cursorVisible="true"
android:hint="Event start"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/field_event_end"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:cursorVisible="true"
android:hint="Event end"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/field_event_location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="map"
android:clickable="true"
android:cursorVisible="true"
android:hint="Event location"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/field_event_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:clickable="true"
android:cursorVisible="true"
android:hint="Event description"
android:singleLine="false" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</ScrollView>
<LinearLayout
android:id="@+id/bottomButtonLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<Button
android:id="@+id/cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@android:string/cancel" />
<Button
android:id="@+id/ok_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@android:string/ok" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -0,0 +1,9 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/settings"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@ -4,7 +4,17 @@
tools:context="com.akdev.nofbeventscraper.MainActivity">
<item
android:id="@+id/action_about"
android:icon="@android:drawable/ic_menu_info_details"
android:icon="@drawable/ic_info"
android:orderInCategory="100"
android:title="@string/action_about" />
<item
android:id="@+id/action_help"
android:icon="@drawable/ic_help"
android:orderInCategory="100"
android:title="@string/action_help" />
<item
android:id="@+id/action_settings"
android:icon="@drawable/ic_settings"
android:orderInCategory="100"
android:title="@string/action_settings" />
</menu>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_banner_background"/>
<foreground android:drawable="@drawable/ic_banner_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_channel_background"/>
<foreground android:drawable="@drawable/ic_channel_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,11 @@
<!doctype html>
<h3>Beschreibung</h3>
<p>Diese Anwendung wurde entwickelt, um ohne ein Facebook-Konto verwendet werden zu können. Daher verwendet sie nicht die Facebook-API. Stattdessen öffnet sie die URI der Facebook-Veranstaltung und lädt den Quellcode der Website herunter. Dieser Quellcode enthält die Informationen, die zum Erstellen eines Kalendereintrags verwendet werden.</p>
<h3>Open Source</h3>
<p>Der Quellcode für diese Anwendung ist bei <a href=" https://github.com/akaessens/NoFbEventScraper">GitHub</a> verfügbar.<strong><br /></strong></p>
<p>Wenn Sie auf ein Problem stoßen, melden Sie es mir bitte anonym über den <a href="https://gitreports.com/issue/akaessens/NoFbEventScraper">Bugtracker</a> oder direkt bei <a href="https://github.com/akaessens/NoFbEventScraper/issues">GitHub</a>.</p>
<h3>Rechtliches</h3>
<p>Diese Anwendung ist für das Speichern einzelner, öffentlich zugänglicher Veranstaltungen in einem persönlichen Kalender vorgesehen. Verwenden Sie sie nicht zur automatischen Datenerfassung und halten Sie sich an die <a href="http://www.facebook.com/apps/site_scraping_tos_terms.php">Bedingungen für die automatische Datenerfassung</a> von Facebook.</p>
<h3>Spenden</h3>
<p>Ich entwickle diese Anwendung in meiner Freizeit. Wenn sie Ihnen gefällt, können Sie bei <a href="https://www.paypal.me/andreaskaessens">PayPal</a> spenden.</p>
<p><a title="PayPal" href="https://www.paypal.me/andreaskaessens"><img src="https://www.paypalobjects.com/webstatic/de_DE/i/de-pp-logo-100px.png" /></a></p>

View File

@ -0,0 +1,14 @@
<!doctype html>
<h3>Welche Links können mit dieser App verwendet werden?</h3>
<p>Alle Facebook-Subdomains werden unterstützt, ob mobil (m.facebook.com) oder sprachspezifisch (de-de.facebook.com). Der Link muss eine Ereignis-ID enthalten.<br />Wie kann diese Anwendung verwendet werden?</p>
<h3>Wie kann diese Anwendung verwendet werden?</h3>
<ul>
<li><strong>Einfügen-Knopf</strong>: Fügen Sie einen kopierten Link aus der Zwischenablage in die URL-Leiste ein.</li>
<li><strong>Teilen mit</strong>: Eingebaute Android-Freigabefunktion, z.B. von einem Browser aus.</li>
<li><strong>Öffnen mit</strong>: Die in Android eingebaute öffnen-mit-Funktion, z.B. beim Klicken auf einen Link in einem Messenger.</li>
</ul>
<h3>Warum funktioniert die Veranstaltung X nicht?</h3>
<p>Diese Anwendung greift auf öffentlich zugängliche Ereignisinformationen zu. Wenn die Veranstaltung z.B. den Ort ohne Login nicht anbietet, ist dies in dieser Anwendung nicht verfügbar. Auch bieten einige Veranstaltungen die Informationen einfach nicht in einem maschinenlesbaren Format an. Veranstaltungen mit mehreren Instanzen sind problematisch, da sie beim Scraping von m.facebook.com nicht das korrekte Start- und Enddatum angeben.</p>
<p>Wenn Sie Probleme mit einem bestimmten Ereignis haben, lassen Sie es mich bitte über den <a href="https://gitreports.com/issue/akaessens/NoFbEventScraper">anonymen Bugtracker</a> oder auf der <a href="https://github.com/akaessens/NoFbEventScraper/issues/">GitHub Problemseite</a>.</p>
<h3>Ist diese App kompatibel mit meinem Kalender?</h3>
<p>Ja. Diese Anwendung verwendet anwendungsunabhängige Kalenderfunktionen, wodurch sie mit jeder Kalenderanwendung kompatibel ist. Ich empfehle jedoch den <a href="https://play.google.com/store/apps/details?id=ws.xsoh.etar">Etar Kalender</a>, da er Open Source ist. </p>

View File

@ -0,0 +1,11 @@
<!doctype html>
<h3>Description</h3>
<p>This application was developed to be used without a Facebook account. Therefore it does not use the Facebook API. Instead it opens the Facebook event URI and downloads the website source code. This source contains the information which is used to create a calendar entry.</p>
<h3>Open Source</h3>
<p>The source code for this application is available at <a href=" https://github.com/akaessens/NoFbEventScraper">GitHub</a>.<strong><br /></strong></p>
<p>If you encounter an issue, please report it to me anonymously at the <a href="https://gitreports.com/issue/akaessens/NoFbEventScraper">Bugtracker</a> or directly at <a href="https://github.com/akaessens/NoFbEventScraper/issues">GitHub</a>.</p>
<h3>Legal</h3>
<p>This application is intended for saving single, publicly available events into a personal calendar. Do not use it to automatically collect data and comply with Facebook's <a href="http://www.facebook.com/apps/site_scraping_tos_terms.php">Automated Data Collection Terms</a>.</p>
<h3>Donations</h3>
<p>I develop this application in my free time. If you like it, you can donate at <a href="https://www.paypal.me/andreaskaessens">PayPal</a>.</p>
<p><a title="PayPal" href="https://www.paypal.me/andreaskaessens"><img src="https://www.paypalobjects.com/webstatic/de_DE/i/de-pp-logo-100px.png" /></a></p>

View File

@ -0,0 +1,14 @@
<!doctype html>
<h3>What links can be used with this app?</h3>
<p>All facebook subdomains are supported, whether mobile (m.facebook.com) or language-specific (de-de.facebook.com). The link must contain an event ID.</p>
<h3>How to use this application?</h3>
<ul>
<li><strong>Paste button</strong>: paste a copied link from the clipboard into the URL bar.</li>
<li><strong>Share to</strong>: Android's built-in share-function, e.g. from a browser.</li>
<li><strong>Open with</strong>: Android's built-in open-with-function, e.g. when clicking on a link in a messenger.</li>
</ul>
<h3>Why does event X not work?</h3>
<p>This app relies on event information that is publicly available. If the event does not offer for example the location without a login, it will not be available in this application. Also, some events just do not provide the information in a machine readable format. Events with multiple instances are problematic because they do not provide the correct start and end date when scraping from m.facebook.com.</p>
<p>If you encounter issues with a specific event, please let me know via the <a href="https://gitreports.com/issue/akaessens/NoFbEventScraper">anonymous bugtracker</a> or at the <a href="https://github.com/akaessens/NoFbEventScraper/issues/">GitHub issue page</a>.</p>
<h3>Is this compatible with my calendar app?</h3>
<p>Yes. This application makes use of application independent calendar functions, which makes it compatible to every calendar app. However, i recommend <a href="https://play.google.com/store/apps/details?id=ws.xsoh.etar">Etar Calendar</a> because it is Open Source.</p>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">NoFb Event Scraper</string>
<string name="action_about">Über</string>
<string name="action_help">Hilfe</string>
<string name="action_settings">Einstellungen</string>
<string name="hint_add_link">Veranstaltungslink</string>
<string name="hint_event_name">Veranstaltungsname</string>
<string name="hint_event_start">Veranstaltungsbeginn</string>
<string name="hint_event_end">Veranstantungsende</string>
<string name="hint_event_location">Veranstaltungsort</string>
<string name="hint_event_description">Veranstaltungsbeschreibung</string>
<string name="helper_add_link">Facebook-Link zur Veranstaltung einfügen</string>
<string name="button_add">Zum Kalender hinzufügen</string>
<string name="tooltip_paste">Einfügen von Inhalten aus der Zwischenablage in das URL-Eingabefeld</string>
<string name="preferences_url_setting">Welcher URL-Präfix ist zu verwenden?</string>
<string name="preferences_url_setting_summary">Die Nutzung von m.facebook.com ist stabiler und schneller. Die Verwendung von www.facebook.com funktioniert besser bei Ereignissen mit mehreren Instanzen und zeigt eine hochauflösende Vorschau an, geht aber irgendwann kaputt, wenn Facebook das klassische Design deaktiviert.</string>
<string name="error_clipboard_empty">Fehler: Zwischenablage leer</string>
<string name="error_no_name">Fehler: Veranstaltungsname nicht verfügbar</string>
<string name="error_no_start_date">Fehler: Veranstaltungsbeginn nicht verfügbar</string>
<string name="error_no_end_date">Fehler: Veranstaltungsende nicht verfügbar</string>
<string name="error_no_location">Fehler: Veranstaltungsort nicht verfügbar</string>
<string name="error_no_description">Fehler: Veranstaltungsbeschreibung nicht verfügbar</string>
<string name="error_scraping">Fehler: Veranstaltungsdaten nicht gefunden</string>
<string name="error_url">Fehler: URL ungültig</string>
<string name="error_connection">Fehler: Keine Verbindung möglich</string>
<string name="error_unknown">Fehler: Unbekannter Fehler</string>
</resources>

View File

@ -0,0 +1,13 @@
<resources>
<!-- Reply Preference -->
<string-array name="url_to_scrape">
<item>m.facebook.com</item>
<item>www.facebook.com</item>
</string-array>
<string-array name="url_prefix">
<item>https://m.</item>
<item>https://www.</item>
</string-array>
</resources>

View File

@ -2,5 +2,5 @@
<resources>
<color name="colorPrimary">#297DA6</color>
<color name="colorPrimaryDark">#23607E</color>
<color name="colorAccent">#297DA6</color>ink
<color name="colorAccent">#297DA6</color>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_banner_background">#23607E</color>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_channel_background">#FFFFFF</color>
</resources>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
<color name="ic_launcher_background">#23607E</color>
</resources>

View File

@ -1,15 +1,42 @@
<resources>
<string name="app_name">NoFb Event Scraper</string>
<string name="action_about">About</string>
<string name="add_link_hint">Paste facebook link to the event.</string>
<string name="about_text">This application was developed to be used without a facebook account.
\nTherefore it does not use the facebook API.
Instead it opens the facebook event URI and downloads the website source code.
This source contains the information which is used to create a calendar entry.
</string>
<string name="open_source_heading"> Open Source </string>
<string name="open_source_text"> The source code for this application can be found at https://github.com/akaessens/NoFbEventScraper .</string>
<string name="changelog_heading"> Changelog </string>
<string name="changelog_text"> v0.1.0\t\tInitial release.</string>
<!-- Action names -->
<string name="action_about">About</string>
<string name="action_help">Help</string>
<string name="action_settings">Settings</string>
<!-- Input hints -->
<string name="hint_add_link">Event link</string>
<string name="hint_event_name">Event name</string>
<string name="hint_event_start">Event start</string>
<string name="hint_event_end">Event end</string>
<string name="hint_event_location">Event location</string>
<string name="hint_event_description">Event description</string>
<string name="helper_add_link">Paste Facebook link to the event</string>
<string name="button_add">Add to calendar</string>
<string name="tooltip_paste">Paste content from clipboard into the URL input box</string>
<!-- Errors -->
<string name="error_clipboard_empty">Error: clipboard empty</string>
<string name="error_no_name">Error: Event name unavailable</string>
<string name="error_no_start_date">Error: Event start date unavailable</string>
<string name="error_no_end_date">Error: Event end date unavailable</string>
<string name="error_no_location">Error: Event location unavailable</string>
<string name="error_no_description">Error: Event description unavailable</string>
<string name="error_scraping">Error: Scraping event data failed</string>
<string name="error_url">Error: URL invalid</string>
<string name="error_connection">Error: Unable to connect</string>
<string name="error_unknown">Error: Unknown Error</string>
<!-- Preferences -->
<string name="preferences_scraper_header" translatable="false">Scraper</string>
<string name="preferences_url_setting">Which URL prefix to use</string>
<string name="preferences_url_setting_summary">"Using m.facebook.com is more stable and faster. Using www.facebook.com works better with multiple instance events and will display a high resolution preview but will eventually break when Facebook disables the classic design. "</string>
</resources>

View File

@ -1,8 +1,8 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
<!-- Customize your theme here.-->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
@ -13,8 +13,8 @@
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.MaterialComponents.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.MaterialComponents.Light" />
</resources>

View File

@ -0,0 +1,21 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory app:title="@string/preferences_scraper_header">
<ListPreference
app:defaultValue="https://m."
app:entries="@array/url_to_scrape"
app:entryValues="@array/url_prefix"
app:key="url_preference"
app:title="@string/preferences_url_setting"
android:summary="@string/preferences_url_setting_summary"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@ -8,7 +8,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.1'
classpath 'com.android.tools.build:gradle:4.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@ -0,0 +1,4 @@
Diese Anwendung wurde entwickelt, um ohne ein Facebook-Konto verwendet werden zu können.
Daher verwendet sie nicht die Facebook-API.
Stattdessen öffnet sie die URI der Facebook-Veranstaltung und lädt den Quellcode der Website herunter.
Dieser Quellcode enthält die Informationen, die zum Erstellen eines Kalendereintrags verwendet werden.

View File

@ -0,0 +1 @@
Diese App durchsucht Facebook-Veranstaltungslinks und fügt sie zu deinem Kalender hinzu.

View File

@ -0,0 +1,3 @@
Added help and reports section.
Fix for some locations not scraped correctly.
Resolve fb-internal links.

View File

@ -0,0 +1,3 @@
Fix for timezone calculation.
Material design Interface, will be improved further.
Display the event picture in the toolbar.

View File

@ -0,0 +1,3 @@
Fixes for new design.
New button layout.
Improve input error display.

View File

@ -0,0 +1,2 @@
Fix for event links with querystrings.
New icon and banner as default toolbar image.

View File

@ -0,0 +1,8 @@
Rework help and about.
Add settings with URL prefix selector.
Add Maps button for location.
Disable text field inputs - all fields can be edited in the calendar app.
Change date/time format to RFC standard.
Minor interface changes.
Code cleanup and refactoring.
Add code documentation.

View File

@ -0,0 +1,2 @@
Fix invalid default uri.
Update about section.

View File

@ -0,0 +1,5 @@
Add german translation.
Decrease MinSDK to 23 (Android 6.0).
Replace Date implementation.
Enable Dark Mode for WebViews.
Add high-res preview for www prefix.

View File

@ -1,4 +1,4 @@
This application was developed to be used without a facebook account.
Therefore it does not use the facebook API.
Instead it opens the facebook event URI and downloads the website source code.
This source contains the information which is used to create a calendar entry.
This application was developed to be used without a Facebook account.
Therefore it does not use the Facebook API.
Instead it opens the Facebook event URI and downloads the website source code.
This source contains the information which is used to create a calendar entry.

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View File

@ -1 +1 @@
This app scrapes facebook event links and adds the event to your calendar.
This app scrapes Facebook event links and adds the event to your calendar.

Binary file not shown.

View File

@ -1,6 +1,7 @@
#Wed Mar 25 12:20:43 CET 2020
#Thu Jun 04 13:39:14 CEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
distributionSha256Sum=23e7d37e9bb4f8dabb8a3ea7fdee9dd0428b9b1a71d298aefd65b11dccea220f

22
gradlew vendored
View File

@ -1,5 +1,21 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@ -109,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`

18
gradlew.bat vendored
View File

@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@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 DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

1
icon.drawio Normal file
View File

@ -0,0 +1 @@
<mxfile host="app.diagrams.net" modified="2020-08-26T11:23:24.586Z" agent="5.0 (X11)" etag="aOyZbSqfGqBfpLj75A79" version="13.6.5" type="device"><diagram id="3duFXleReQLFlbFXyRML" name="Page-1">5ZjLbqMwFIafhmUrzL3LIW2ni+moUhaTrQMOWDGYgknIPP2YYAi3FNJCGmmCFOEfY+D7j48vkroIsp8xjPxX6iIiKbKbSeqjpCiGafL/XDgUgmrqheDF2C0kcBKW+C8SoizUFLsoaVRklBKGo6bo0DBEDmtoMI7pvlltQ0nzqRH0UEdYOpB01T/YZX6hWop50l8Q9vzyycB4KK4EsKwsviTxoUv3NUl9ktRFTCkrzoJsgUjOruRS3Pd85mr1YjEK2ZgbVq/r1EM7Y5umKwu/kzd20O/KZnaQpOKLxduyQ4lg72OGlhF08vKeuyypts8CwkuAn8IkKsBvcIb4s+zqQ2Ve8AhMEnGebBFz/LLAYrpFC0pozJWQhrx1e4MJaUni/VDMUHb2y0HFk8chogFi8YFXETFnCQf2JwNVQ2h+zbxKhCJovKqpE1d+ItD2Y/ZXobIL33dgYaZLc7N9+b1d3Y2gHNM0dHN+R6hDzGNHdBVgjmEp8X6n2Zpit104g7IHeJdu1qQrYAOjS1uRe2jPBtsahu1x2tHnWVTpBq7LFuVLGFXZrc5I72EErLkgaZdEpDwckb0h2CX6oWHDITcUU0Cei5c5cQ/u5XXq1la3Az8ff3NQLaNyZMcF6gSQ+0cjZeqoHDUUtUDbx+PjmB4Yj84bcjZFKK0U0TVD6/FCm80K9VusGDOSLY6/Ga1oZ+vvtmLyXD2ZFU9GfsxoBdBuzAv9Zr2YvVu0MlTPrPq6Vhg3a8Xs3cK6MSsumh1d1wo7P66Yob7dixHLH94MjhI0Yp35lbV9uV+idKy6RrYCqt7wpdx3qvli9fgyxYKr3xd52JecAnYg+QXXiLzRBDNMQ35pTRmjAYdVVvhBsJdfYLTbcaK8sSDz8u24+zVMsHOfMBh/djasPJiPP4xrz4aBZt43/bMqoeagrncdLLXpl8wXjf1j1oB1PxwaYGc4y9UXgt1u9vGU4EvbPO3hH+h9y8V5El2vHcrD/2wHuJ4dvHjapT5eq231q0//AA==</diagram></mxfile>

View File

@ -1,3 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" style="background-color: rgb(255, 255, 255);" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="642px" height="642px" viewBox="-0.5 -0.5 642 642" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2020-03-26T14:54:10.768Z&quot; agent=&quot;Mozilla/5.0 (X11; Linux x86_64; rv:74.0) Gecko/20100101 Firefox/74.0&quot; etag=&quot;XsL7PBsKGEcMwgVGFdAw&quot; version=&quot;12.9.3&quot; type=&quot;device&quot;&gt;&lt;diagram id=&quot;wDqTZysBFCi2kShiBJaS&quot; name=&quot;Page-1&quot;&gt;1VjdcqIwGH0aLuuQ8KeXQms7u223u+7MXu5EiJBpIA6Eqvv0m0gAKThVF7aqjoaTL8dwTr6ERDO8eHOfolX0xAJMNagHG8241SAEYGyJH4lsC8S27AIIUxKooBqYkz9YgbpCcxLgrBHIGaOcrJqgz5IE+7yBoTRl62bYktHmv65QiFvA3Ee0jf4iAY8KdAydGn/AJIzKfwb2pKiJURms7iSLUMDWDQhv+IwlXHXxOU8IZ5p1F3Eu726qwZn4LGXEKGQspBitSDbyWSxgPxMhsyWKCZXSqsaO94Mt2NksqrHjvfwU3ZvjlCzPZJIEbkHgeI8kF3RfCErEzxNi4ttD5/ZxR+YWZO6OzJVkjvc9J/5rhpLgTOK6veN9peiNvCLRUVeOmPMIKxJ3R1Kz/iMd1Iw7zfBSxnhRijcepjLvypQqxtfsQG01plOc8GMa8PzmObhPZmTzAMDLM/j2G09uyhR4QzRXyaIGOt+W2ZOyPAmwZNE1w11HhOP5Cvmydi3mC4FFPKbiCohilSAyNqRI3v2uLNQgvipnPGWv2GOUpQJJWCK43CWhtIQ0aMx2L4kLXfdwMJVvgauO45TjzUFJQCW0mNwwizFPtyJENRirFN42L9f1HGGbCov25ocKRCrpw4q51l8UlAUn2AGsvu3oUvqgR+8sgBPndmq3LSheg1gA9SM9MMFgHpxiAfjYApT6akW02xI7pmtC95BR+940TNtPpv5NAPBIE4A+mAl9u9Cpbw/SWZOP55Au5YYbvvqVKGc6F6bc5EqEg5cm3PhKhAPWhQln9Czcu8XFhFPjFh4SdH8NGXCBt/Wm6JO26F1Ly2QozU96wupb88bD1nCam+PL0tz+HxPEISMG1BnYl6XzSZu5Kx3b7x9WP1tz0Lfozf3CMUP9E/ZpltOxdnZuEazBhD/ieU3eMvERfUQLTF9YRjhhiahaMM5ZLJQpA6aUhLKCs/Z5xkqSxZtQno2OFigj/ijjSGjsNrfi5x999JEWhtUwCFpmyyDD6DCoAk8wSFzW51a7ur2DY+PuLw==&lt;/diagram&gt;&lt;/mxfile&gt;"><defs><style type="text/css">@import url(https://fonts.googleapis.com/css?family=Nunito);&#xa;@import url(https://fonts.googleapis.com/css?family=Roboto);&#xa;@import url(https://fonts.googleapis.com/css?family=PT+Serif);&#xa;@import url(https://fonts.googleapis.com/css?family=Liu+Jian+Mao+Cao);&#xa;@import url(https://fonts.googleapis.com/css?family=Quicksand);&#xa;@import url(https://fonts.googleapis.com/css?family=Klavika+Bold);&#xa;@import url(https://fonts.googleapis.com/css?family=Klavika);&#xa;</style></defs><g><rect x="0" y="0" width="640" height="640" fill="#ffffff" stroke="none" pointer-events="all"/><rect x="0" y="120" width="640" height="410" fill="#297da6" stroke="none" pointer-events="all"/><rect x="0" y="40" width="640" height="100" rx="6" ry="6" fill="#74b42b" stroke="none" pointer-events="all"/><rect x="510" y="0" width="60" height="110" rx="9" ry="9" fill="#ffffff" stroke="none" pointer-events="all"/><rect x="390" y="0" width="60" height="110" rx="9" ry="9" fill="#ffffff" stroke="none" pointer-events="all"/><rect x="190" y="0" width="60" height="110" rx="9" ry="9" fill="#ffffff" stroke="none" pointer-events="all"/><rect x="70" y="0" width="60" height="110" rx="9" ry="9" fill="#ffffff" stroke="none" pointer-events="all"/><rect x="520" y="10" width="40" height="90" rx="6" ry="6" fill="#42a3d2" stroke="none" pointer-events="all"/><rect x="400" y="10" width="40" height="90" rx="6" ry="6" fill="#42a3d2" stroke="none" pointer-events="all"/><rect x="80" y="10" width="40" height="90" rx="6" ry="6" fill="#42a3d2" stroke="none" pointer-events="all"/><rect x="200" y="10" width="40" height="90" rx="6" ry="6" fill="#42a3d2" stroke="none" pointer-events="all"/><rect x="0" y="490" width="640" height="150" rx="9" ry="9" fill="#297da6" stroke="none" pointer-events="all"/><path d="M 155 295 L 281.44 295 L 320 174 L 358.56 295 L 485 295 L 384.26 376.03 L 424.21 504 L 320 424.8 L 215.79 504 L 255.74 376.03 Z" fill="#ffffff" stroke="none" pointer-events="all"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="361px" height="362px" viewBox="-0.5 -0.5 361 362" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2020-08-26T11:23:42.246Z&quot; agent=&quot;5.0 (X11)&quot; etag=&quot;jgrgKsVBq6YiCG3Uvhtx&quot; version=&quot;13.6.5&quot; type=&quot;device&quot;&gt;&lt;diagram id=&quot;3duFXleReQLFlbFXyRML&quot; name=&quot;Page-1&quot;&gt;5ZjLbqMwFIafhmUrzL3LIW2ni+moUhaTrQMOWDGYgknIPP2YYAi3FNJCGmmCFOEfY+D7j48vkroIsp8xjPxX6iIiKbKbSeqjpCiGafL/XDgUgmrqheDF2C0kcBKW+C8SoizUFLsoaVRklBKGo6bo0DBEDmtoMI7pvlltQ0nzqRH0UEdYOpB01T/YZX6hWop50l8Q9vzyycB4KK4EsKwsviTxoUv3NUl9ktRFTCkrzoJsgUjOruRS3Pd85mr1YjEK2ZgbVq/r1EM7Y5umKwu/kzd20O/KZnaQpOKLxduyQ4lg72OGlhF08vKeuyypts8CwkuAn8IkKsBvcIb4s+zqQ2Ve8AhMEnGebBFz/LLAYrpFC0pozJWQhrx1e4MJaUni/VDMUHb2y0HFk8chogFi8YFXETFnCQf2JwNVQ2h+zbxKhCJovKqpE1d+ItD2Y/ZXobIL33dgYaZLc7N9+b1d3Y2gHNM0dHN+R6hDzGNHdBVgjmEp8X6n2Zpit104g7IHeJdu1qQrYAOjS1uRe2jPBtsahu1x2tHnWVTpBq7LFuVLGFXZrc5I72EErLkgaZdEpDwckb0h2CX6oWHDITcUU0Cei5c5cQ/u5XXq1la3Az8ff3NQLaNyZMcF6gSQ+0cjZeqoHDUUtUDbx+PjmB4Yj84bcjZFKK0U0TVD6/FCm80K9VusGDOSLY6/Ga1oZ+vvtmLyXD2ZFU9GfsxoBdBuzAv9Zr2YvVu0MlTPrPq6Vhg3a8Xs3cK6MSsumh1d1wo7P66Yob7dixHLH94MjhI0Yp35lbV9uV+idKy6RrYCqt7wpdx3qvli9fgyxYKr3xd52JecAnYg+QXXiLzRBDNMQ35pTRmjAYdVVvhBsJdfYLTbcaK8sSDz8u24+zVMsHOfMBh/djasPJiPP4xrz4aBZt43/bMqoeagrncdLLXpl8wXjf1j1oB1PxwaYGc4y9UXgt1u9vGU4EvbPO3hH+h9y8V5El2vHcrD/2wHuJ4dvHjapT5eq231q0//AA==&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><rect x="0" y="0" width="360" height="360" fill="none" stroke="none" pointer-events="all"/><rect x="80" y="80" width="200" height="60" rx="10.2" ry="10.2" fill="#74b42b" stroke="none" pointer-events="all"/><rect x="80" y="130" width="200" height="100" fill="#ffffff" stroke="none" pointer-events="all"/><rect x="80" y="150" width="200" height="130" rx="10.4" ry="10.4" fill="#ffffff" stroke="none" pointer-events="all"/><rect x="100" y="150" width="40" height="40" fill="#b3b3b3" stroke="none" pointer-events="all"/><rect x="160" y="150" width="40" height="40" fill="#cccccc" stroke="none" pointer-events="all"/><rect x="220" y="150" width="40" height="40" fill="#e6e6e6" stroke="none" pointer-events="all"/><rect x="100" y="210" width="40" height="40" fill="#cccccc" stroke="none" pointer-events="all"/><rect x="160" y="210" width="40" height="40" fill="#e6e6e6" stroke="none" pointer-events="all"/><rect x="220" y="210" width="40" height="40" fill="#ebebeb" stroke="none" pointer-events="all"/><ellipse cx="255" cy="245" rx="40" ry="40" fill="#ffffff" stroke="#cccccc" stroke-width="2" pointer-events="all"/><path d="M 227.5 237.67 L 248.57 237.67 L 255 217.5 L 261.43 237.67 L 282.5 237.67 L 265.71 251.17 L 272.37 272.5 L 255 259.3 L 237.63 272.5 L 244.29 251.17 Z" fill="#297da6" stroke="none" pointer-events="all"/><rect x="220" y="70" width="20" height="40" rx="3" ry="3" fill="#cccccc" stroke="#ffffff" stroke-width="2" pointer-events="all"/><rect x="120" y="70" width="20" height="40" rx="3" ry="3" fill="#cccccc" stroke="#ffffff" stroke-width="2" pointer-events="all"/></g></svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB