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

24 Commits

Author SHA1 Message Date
c43911fc77 prepare v0.3.3 2020-08-31 21:33:01 +02:00
3cbfc9b9c1 add changelog.md 2020-08-31 21:17:57 +02:00
71169dae76 update README 2020-08-31 18:31:47 +02:00
5b412733b4 Merge branch 'master' of github.com:akaessens/NoFbEventScraper into master 2020-08-31 18:28:51 +02:00
a3f2990c61 update about 2020-08-31 18:25:21 +02:00
1c112f3e4e Update README.md 2020-08-31 17:59:12 +02:00
2faf8aa003 get high-res preview also from videos 2020-08-31 17:10:10 +02:00
643ac62788 prevent activityNotFound crash on calendar intent 2020-08-31 16:45:34 +02:00
d5e12c4d88 Merge branch 'master' of github.com:akaessens/NoFbEventScraper into master 2020-08-31 16:13:53 +02:00
320d1787aa improve high-res image url scraping 2020-08-31 16:13:43 +02:00
c7a4d9b027 Update README.md
closes #13
2020-08-31 15:26:11 +02:00
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
39 changed files with 385 additions and 156 deletions

40
CHANGELOG.md Normal file
View File

@ -0,0 +1,40 @@
# Changelog
## v0.3.3 (9)
- Update about section with download and changelog information.
- Improve high-res preview scraping.
- Fix potential crash on calendar intent.
## v0.3.2 (8)
- 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.
## v0.3.1 (7)
- Fix invalid default uri.
- Update about section.
## v0.3.0 (6)
- 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.
## v0.2.2 (5)
- Fix for event links with querystrings.
- New icon and banner as default toolbar image.
## v0.2.1 (4)
- Fixes for new design.
- New button layout.
- Improve input error display.
## v0.2.0 (3)
- Fix for timezone calculation.
- Material design Interface, will be improved further.
- Display the event picture in the toolbar.
## v0.1.1 (2)
- Added help and reports section.
- Fix for some locations not scraped correctly.
- Resolve fb-internal links.
## v0.1.0 (1)
- Initial release.

View File

@ -1,18 +1,37 @@
![Android CI](https://github.com/akaessens/NoFbEventScraper/workflows/Android%20CI/badge.svg)
[![Android CI](https://github.com/akaessens/NoFbEventScraper/workflows/Android%20CI/badge.svg)](https://github.com/akaessens/NoFbEventScraper/actions)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://github.com/akaessens/NoFbEventScraper/blob/master/LICENSE)
[![Release](https://img.shields.io/github/release/akaessens/nofbeventscraper.svg?logo=github)](https://github.com/akaessens/NoFbEventScraper/releases/latest)
[![F-Droid](https://img.shields.io/f-droid/v/com.akdev.nofbeventscraper.svg)](https://f-droid.org/en/packages/com.akdev.nofbeventscraper)
# NoFb Event Scraper
This application was developed to be used without a facebook account.
<img src="https://github.com/akaessens/NoFbEventScraper/raw/master/fastlane/metadata/android/en-US/images/featureGraphic.png" alt="Feature Graphic" width="max-width">
Therefore it does not use the facebook API.
Instead it opens the facebook event URI and downloads the website source code.
# Description
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.
# Download
Currently this application is in the submitting process to FDroid. see also [#2](/../../issues/2).
Until it is published at FDroid, use [release v0.2.2](https://github.com/akaessens/NoFbEventScraper/releases/download/v0.2.2/app-release.apk).
<a href="https://f-droid.org/en/packages/com.akdev.nofbeventscraper">
<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="75">
</a>
# Building
This Android app is written in Java and is using the Gradle build system. To compile it, i recommend using Android Studio.
```
./gradlew build
```
# 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">
# Donations
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>.
<a title="PayPal" href="https://www.paypal.me/andreaskaessens"><img src="https://raw.githubusercontent.com/stefan-niedermann/paypal-donate-button/master/paypal-donate-button.png" height="75" /></a>

View File

@ -6,10 +6,10 @@ android {
defaultConfig {
applicationId "com.akdev.nofbeventscraper"
minSdkVersion 26
minSdkVersion 23
targetSdkVersion 29
versionCode 6
versionName "0.3.0"
versionCode 9
versionName "0.3.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@ -30,7 +30,6 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation 'androidx.navigation:navigation-fragment:2.3.0'
implementation 'androidx.navigation:navigation-ui:2.3.0'
@ -40,6 +39,8 @@ dependencies {
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.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

View File

@ -42,15 +42,16 @@ public class ScraperUnitTest {
FbScraper scraper = new FbScraper(null, "");
String exp = "2020-10-23T05:00+02:00";
String in = "2020-10-23T05:00:00+0200";
String act = scraper.toZonedDateTime(in).toString();
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 = null;
exp = "";
in = "";
ZonedDateTime act2 = scraper.toZonedDateTime(in);
String act2 = FbEvent.dateTimeToString(scraper.parseToDate(in));
assertEquals(exp, act2);
}

View File

@ -32,8 +32,10 @@
</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" />

View File

@ -1,9 +0,0 @@
<!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>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

@ -1,15 +1,13 @@
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.appcompat.widget.Toolbar;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebView;
import android.widget.ImageView;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;
public class AboutActivity extends AppCompatActivity {
@ -24,7 +22,14 @@ public class AboutActivity extends AppCompatActivity {
WebView webview_about = findViewById(R.id.webview_about);
webview_about.loadUrl("file:////android_asset/about.html");
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,8 +1,8 @@
package com.akdev.nofbeventscraper;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* Objects of this class store immutable information about
@ -12,14 +12,14 @@ public class FbEvent {
public final String url;
public final String name;
public final ZonedDateTime start_date;
public final ZonedDateTime end_date;
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, ZonedDateTime start_date, ZonedDateTime end_date,
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;
@ -33,28 +33,28 @@ public class FbEvent {
/**
* Converts datetime to epoch.
*
* @param zoned_date_time ZonedDateTime object
* @param date Date object
* @return Event begin time in milliseconds from the epoch for calendar intent or null
*/
static Long dateTimeToEpoch(ZonedDateTime zoned_date_time) {
static Long dateTimeToEpoch(Date date) {
try {
return zoned_date_time.toEpochSecond() * 1000;
return date.getTime();
} catch (Exception e) {
return null;
}
}
/**
* Returns a RFC formatted String representation of a ZonedDateTime
* Returns a locally formatted String representation of a Date
*
* @param zoned_date_time
* @return RFC-1123 formatted String of zoned_date_time or empty String
* @param date
* @return locally formatted String of date or empty String
*/
static String dateTimeToString(ZonedDateTime zoned_date_time) {
static String dateTimeToString(Date date) {
try {
return DateTimeFormatter
.RFC_1123_DATE_TIME
.format(zoned_date_time);
SimpleDateFormat formatter = new SimpleDateFormat("E, dd MMM yyyy HH:mm z",
Locale.getDefault());
return formatter.format(date);
} catch (Exception e) {
return "";
}

View File

@ -2,8 +2,6 @@ package com.akdev.nofbeventscraper;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import androidx.preference.PreferenceManager;
@ -17,8 +15,9 @@ import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -28,7 +27,7 @@ import java.util.regex.Pattern;
*/
public class FbScraper extends AsyncTask<Void, Void, Void> {
private String error;
private int error;
private String input_url;
private WeakReference<MainActivity> main; // no context leak with WeakReference
private FbEvent event;
@ -85,6 +84,7 @@ public class FbScraper extends AsyncTask<Void, Void, Void> {
/**
* 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.
*/
@ -122,21 +122,18 @@ public class FbScraper extends AsyncTask<Void, Void, Void> {
}
/**
* Parses a time string from the facebook event.
* Corrects format to ISO date time and parse into ZonedDateTime
* Parses a time string from the facebook event into a Date
*
* @param time_in time string from the event
* @return ZonedDateTime parsed from input or null
* @return Date parsed from input or null
*/
protected ZonedDateTime toZonedDateTime(String time_in) {
protected Date parseToDate(String time_in) {
try {
// time in is missing a : in the timezone offset
Editable editable = new SpannableStringBuilder(time_in);
String time_str = editable.insert(22, ":").toString();
// parse e.g. 2011-12-03T10:15:30+0100
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
// parse e.g. 2011-12-03T10:15:30+01:00
return ZonedDateTime.parse(time_str, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
return sdf.parse(time_in);
} catch (Exception e) {
e.printStackTrace();
@ -168,6 +165,7 @@ public class FbScraper extends AsyncTask<Void, Void, Void> {
/**
* 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
@ -184,6 +182,7 @@ public class FbScraper extends AsyncTask<Void, Void, Void> {
/**
* Started by scraper.execute().
* Gets the HTML doc from the input string and scrapes the event information from it.
*
* @param voids
* @return
*/
@ -192,33 +191,52 @@ public class FbScraper extends AsyncTask<Void, Void, Void> {
try {
String url = fixURI(input_url);
// useragent needed with Jsoup > 1.12
Document document = Jsoup.connect(url).userAgent("Mozilla").get();
// 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);
event = new FbEvent(
url,
readFromJson(reader, "name"),
toZonedDateTime(readFromJson(reader, "startDate")),
toZonedDateTime(readFromJson(reader, "endDate")),
fixDescriptionLinks(readFromJson(reader, "description")),
fixLocation(readFromJson(reader, "location")),
readFromJson(reader, "image")
);
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 {
// possibly get higher res image from event header
image_url = document.select("div[id=event_header_primary]")
.select("img").first().attr("src");
} catch (Exception e) {
// ignore
}
event = new FbEvent(url, name, start_date, end_date, description, location, image_url);
} catch (URISyntaxException | MalformedURLException e) {
e.printStackTrace();
this.error = "Error: URL invalid.";
this.error = R.string.error_url;
} catch (JSONException e) {
e.printStackTrace();
this.error = "Error: Scraping event data failed";
this.error = R.string.error_scraping;
} catch (IOException e) {
e.printStackTrace();
this.error = "Error: Unable to connect.";
this.error = R.string.error_connection;
} catch (Exception e) {
e.printStackTrace();
this.error = R.string.error_unknown;
}
return null;
@ -232,6 +250,7 @@ public class FbScraper extends AsyncTask<Void, Void, Void> {
/**
* 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) {

View File

@ -1,10 +1,13 @@
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 {
@ -19,7 +22,17 @@ public class HelpActivity extends AppCompatActivity {
WebView webview_help = findViewById(R.id.webview_help);
webview_help.loadUrl("file:////android_asset/help.html");
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,5 +1,6 @@
package com.akdev.nofbeventscraper;
import android.annotation.SuppressLint;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
@ -111,15 +112,17 @@ public class MainActivity extends AppCompatActivity {
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);
edit_text_uri_input.setText(str);
} catch (NullPointerException e) {
e.printStackTrace();
error("Error: Clipboard empty");
}
startScraping();
} catch (Exception e) {
e.printStackTrace();
error(R.string.error_clipboard_empty);
}
}
});
@ -171,8 +174,11 @@ public class MainActivity extends AppCompatActivity {
// prepend url in description
String desc = event.url + "\n\n" + event.description;
intent.putExtra(CalendarContract.Events.DESCRIPTION, desc);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
});
/*
@ -222,8 +228,13 @@ public class MainActivity extends AppCompatActivity {
scraper.execute();
}
public void error(String str) {
layout_uri_input.setError(str);
public void error(Integer resId) {
if (resId != null) {
layout_uri_input.setError(getString(resId));
} else {
layout_uri_input.setError(null);
}
}
/**
@ -276,34 +287,34 @@ public class MainActivity extends AppCompatActivity {
edit_text_uri_input.setText(event.url);
if (event.name.equals("")) {
edit_text_event_name.setError("no event name detected");
edit_text_event_name.setError(getString(R.string.error_no_name));
} else {
edit_text_event_name.setText(event.name);
}
if (event.start_date == null) {
edit_text_event_start.setError("no event start date detected");
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("no event end date detected");
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("no event location detected");
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("no event description detected");
edit_text_event_description.setError(getString(R.string.error_no_description));
} else {
edit_text_event_description.setText(event.description);
}
@ -317,6 +328,7 @@ public class MainActivity extends AppCompatActivity {
}
@SuppressLint("RestrictedApi")
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.

View File

@ -1,15 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<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">
<WebView
android:id="@+id/webview_about"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</RelativeLayout>

View File

@ -1,15 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<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" />
</androidx.constraintlayout.widget.ConstraintLayout>
</RelativeLayout>

View File

@ -59,7 +59,9 @@
app:layout_anchor="@id/app_bar"
app:layout_anchorGravity="bottom|end"
app:icon="@drawable/ic_content_paste"
android:text="@string/paste_button"/>
android:text="@android:string/paste"
android:tooltipText="@string/tooltip_paste"
tools:ignore="UnusedAttribute" />
<include layout="@layout/content_main" />
@ -80,7 +82,7 @@
android:layout_marginEnd="16dp"
android:layout_weight="1"
android:padding="12dp"
android:text="@string/add_to_calendar"
android:text="@string/button_add"
android:textColor="#FFFFFF"
app:cornerRadius="24dp"
app:icon="@drawable/ic_event_available"

View File

@ -37,7 +37,7 @@
app:errorIconDrawable="@drawable/ic_backspace_black"
app:endIconMode="custom"
app:helperText="@string/add_link_helper"
app:helperText="@string/helper_add_link"
app:helperTextEnabled="true">
<com.google.android.material.textfield.TextInputEditText
@ -46,7 +46,7 @@
android:layout_height="wrap_content"
android:autoLink="web"
android:cursorVisible="true"
android:hint="@string/add_link_hint"
android:hint="@string/hint_add_link"
android:inputType="textNoSuggestions"
android:singleLine="true"
android:textColorLink="@color/material_on_background_emphasis_high_type" />
@ -63,7 +63,7 @@
android:layout_height="wrap_content"
android:focusable="false"
android:cursorVisible="false"
android:hint="Event name"
android:hint="@string/hint_event_name"
android:inputType="textNoSuggestions"
android:singleLine="true"
android:textColorLink="@color/material_on_background_emphasis_high_type" />
@ -80,7 +80,7 @@
android:layout_height="wrap_content"
android:focusable="false"
android:cursorVisible="false"
android:hint="Event start"
android:hint="@string/hint_event_start"
android:inputType="textNoSuggestions"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
@ -96,7 +96,7 @@
android:layout_height="wrap_content"
android:focusable="false"
android:cursorVisible="false"
android:hint="Event end"
android:hint="@string/hint_event_end"
android:inputType="textNoSuggestions"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
@ -114,10 +114,9 @@
android:id="@+id/edit_text_event_location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="map"
android:focusable="false"
android:cursorVisible="false"
android:hint="Event location"
android:hint="@string/hint_event_location"
android:inputType="textNoSuggestions"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
@ -134,7 +133,7 @@
android:autoLink="web"
android:focusable="false"
android:cursorVisible="false"
android:hint="Event description"
android:hint="@string/hint_event_description"
android:inputType="textNoSuggestions|textMultiLine"
android:singleLine="false"
android:textColorLink="@color/material_on_background_emphasis_high_type" />

View File

@ -0,0 +1,14 @@
<!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>Updates</h3>
<p>Diese App ist bei <a href="https://f-droid.org/de/packages/com.akdev.nofbeventscraper">F-Droid</a> zum Download verfügbar. Das Änderungsprotokoll ist auf <a href="https://github.com/akaessens/NoFbEventScraper/blob/master/CHANGELOG.md">GitHub</a> verfügbar.</p>
<p><a href="https://f-droid.org/en/packages/com.akdev.nofbeventscraper"> <img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="75" /> </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://raw.githubusercontent.com/stefan-niedermann/paypal-donate-button/master/paypal-donate-button.png" height="75" /></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,14 @@
<!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>Updates</h3>
<p>This application is available for download at <a href="https://f-droid.org/de/packages/com.akdev.nofbeventscraper">F-Droid</a>. The changelog is at <a href="https://github.com/akaessens/NoFbEventScraper/blob/master/CHANGELOG.md">GitHub</a>.</p>
<p><a href="https://f-droid.org/en/packages/com.akdev.nofbeventscraper"> <img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="75" /> </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://raw.githubusercontent.com/stefan-niedermann/paypal-donate-button/master/paypal-donate-button.png" height="75" /></a></p>

View File

@ -8,7 +8,7 @@
<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 publicy available. If the event does not offer for example the location without a login, it will not be available in this application. 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>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&amp;hl=de">Etar Calendar</a> because it is Open Source.</p>
<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

@ -1,19 +1,42 @@
<resources>
<string name="app_name">NoFb Event Scraper</string>
<!-- Action names -->
<string name="action_about">About</string>
<string name="action_help">Help</string>
<string name="action_settings">Settings</string>
<string name="add_link_hint">Event link</string>
<string name="add_link_helper">Paste facebook link to the event.</string>
<string name="add_to_calendar">Add to calendar</string>
<string name="paste_button">Paste from clipboard</string>
<!-- Preference Titles -->
<string name="scraper_header">Scraper</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>
<string name="url_setting">Which URL to scrape</string>
<string name="url_setting_summary">
Using m.facebook.com is more stable and faster. Using www.facebook.com gives high-res images and works better with mutiple instance events but will eventually break when facebook disables the classic design.
</string>
</resources>

View File

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

21
create_changelog.sh Executable file
View File

@ -0,0 +1,21 @@
#!/bin/bash
changelog_location="fastlane/metadata/android/en-US/changelogs"
count_txt=$(ls -r $changelog_location/*.txt | wc -l)
count_tag=$(git tag | wc -l)
if (( count_txt != count_tag )); then
echo "Tag count and txt-file count not matching."
exit 1
fi
echo "# Changelog" > CHANGELOG.md
for i in $(seq "$count_txt" -1 1)
do
tag=$(git tag | sed "$i!d")
echo "## $tag ($i)" >> CHANGELOG.md
cat $changelog_location/"$i".txt >> CHANGELOG.md
done

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

@ -1 +1 @@
Initial release.
- Initial release.

View File

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

View File

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

View File

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

View File

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

View File

@ -1,8 +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.
- 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

@ -0,0 +1,3 @@
- Update about section with download and changelog information.
- Improve high-res preview scraping.
- Fix potential crash on calendar intent.

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 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.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 629 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 484 KiB

After

Width:  |  Height:  |  Size: 84 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.