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

15 Commits

Author SHA1 Message Date
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
23 changed files with 622 additions and 226 deletions

View File

@ -11,8 +11,8 @@ 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.1.0](https://github.com/akaessens/NoFbEventScraper/releases/download/v0.1.0/app-release.apk).
Until it is published at FDroid, use [release v0.2.0](https://github.com/akaessens/NoFbEventScraper/releases/download/v0.2.0/app-release.apk).
# Screenshot
<img src="/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png" alt="Screenshot" width="250">
<img src="https://github.com/akaessens/NoFbEventScraper/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png" alt="Screenshot 1" width="250"> <img src="https://github.com/akaessens/NoFbEventScraper/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png" alt="Screenshot 2" width="250"> <img src="https://github.com/akaessens/NoFbEventScraper/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png" alt="Screenshot 3" width="250">

View File

@ -8,8 +8,8 @@ android {
applicationId "com.akdev.nofbeventscraper"
minSdkVersion 26
targetSdkVersion 29
versionCode 2
versionName "0.1.1"
versionCode 4
versionName "0.2.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@ -24,16 +24,20 @@ 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.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
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'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'

View File

@ -0,0 +1,106 @@
package com.akdev.nofbeventscraper;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
@RunWith(AndroidJUnit4.class)
public class MainActivityUnitTest {
@Test
public void TestSubdomainUrl() {
Instrumentation mInstrumentation = getInstrumentation();
// We register our interest in the activity
Instrumentation.ActivityMonitor monitor = mInstrumentation.addMonitor(MainActivity.class.getName(), null, false);
// We launch it
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName(mInstrumentation.getTargetContext(), MainActivity.class.getName());
mInstrumentation.startActivitySync(intent);
MainActivity mainActivity = (MainActivity) getInstrumentation().waitForMonitor(monitor);
// We register our interest in the next activity from the sequence in this use case
mInstrumentation.removeMonitor(monitor);
final String exp = "https://www.facebook.com/events/261145401687844";
String url = "https://www.facebook.com/events/261145401687844";
String act = mainActivity.checkURI(url);
assertEquals(exp, act);
url = "https://de-de.facebook.com/events/261145401687844";
act = mainActivity.checkURI(url);
assertEquals(exp, act);
url = "https://m.facebook.com/events/261145401687844";
act = mainActivity.checkURI(url);
assertEquals(exp, act);
url = "https://www.facebook.com/events/261145401687844/?active_tab=discussion";
act = mainActivity.checkURI(url);
assertEquals(exp, act);
}
@Test
public void TestTimeToEpoch() {
Instrumentation mInstrumentation = getInstrumentation();
// We register our interest in the activity
Instrumentation.ActivityMonitor monitor = mInstrumentation.addMonitor(MainActivity.class.getName(), null, false);
// We launch it
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName(mInstrumentation.getTargetContext(), MainActivity.class.getName());
mInstrumentation.startActivitySync(intent);
MainActivity mainActivity = (MainActivity) getInstrumentation().waitForMonitor(monitor);
// We register our interest in the next activity from the sequence in this use case
mInstrumentation.removeMonitor(monitor);
String in = "2020-07-29T12:00:00+00:00";
Long exp = new Long(1596024000);
exp = exp* 1000;
Long act = mainActivity.convertTimeToEpoch(in);
assertEquals(exp, act);
in = "2020-07-29T12:00:00+02:00";
exp = new Long(1596016800);
exp = exp* 1000;
act = mainActivity.convertTimeToEpoch(in);
assertEquals(exp, act);
in = "1970-01-01T00:00:00+00:00";
exp = new Long(0);
exp = exp* 1000;
act = mainActivity.convertTimeToEpoch(in);
assertEquals(exp, act);
in = "1970-01-01T02:00:00+02:00";
exp = new Long(0);
exp = exp* 1000;
act = mainActivity.convertTimeToEpoch(in);
assertEquals(exp, act);
}
}

View File

@ -0,0 +1,68 @@
package com.akdev.nofbeventscraper;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
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 exp = "2020-10-23T05:00:00+02:00";
String in = "2020-10-23T05:00:00+0200";
String act = scraper.fixTimezone(in);
assertEquals(exp, act);
exp = "";
in = "";
act = scraper.fixTimezone(in);
assertEquals(exp, act);
}
@Test
public void TestLinks() {
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.fixLinks(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.fixLinks(in);
assertEquals(exp, act);
}
}

View File

@ -1,6 +1,7 @@
package com.akdev.nofbeventscraper;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.content.Intent;
import android.net.Uri;
@ -14,7 +15,7 @@ public class AboutActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about);
this.getSupportActionBar().hide();
ImageView img = (ImageView)findViewById(R.id.paypal_image);

View File

@ -1,15 +1,9 @@
package com.akdev.nofbeventscraper;
import android.app.Activity;
import android.content.Context;
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 org.json.JSONException;
import org.json.JSONObject;
@ -30,7 +24,7 @@ public class FbScraper extends AsyncTask<Void, Void, Void> {
this.main = main;
}
private String fixLocation(String location_json) {
protected String fixLocation(String location_json) {
String name = "";
@ -63,7 +57,7 @@ public class FbScraper extends AsyncTask<Void, Void, Void> {
}
}
private String fixTimezone(String time_in) {
protected String fixTimezone(String time_in) {
try {
@ -77,10 +71,11 @@ public class FbScraper extends AsyncTask<Void, Void, Void> {
}
}
private String fixLinks(String description_in) {
protected String fixLinks(String description_in) {
try {
// @[152580919265:274:MagentaMusik 360] -> m.facebook.com/152580919265
return description_in.replaceAll("@\\[([0-9]{10,}):[0-9]{3}:([^\\]]*)\\]", "m.facebook.com/$1 ($2)");
return description_in.replaceAll("@\\[([0-9]{10,}):[0-9]{3}:([^\\]]*)\\]",
"$2 [m.facebook.com/$1]");
} catch (Exception e) {
e.printStackTrace();
@ -103,7 +98,7 @@ public class FbScraper extends AsyncTask<Void, Void, Void> {
Document document = null;
try {
document = Jsoup.connect(url).get();
document = Jsoup.connect(url).userAgent("Mozilla").get();
try {
String json = document.select("script[type = application/ld+json]").first().data();
@ -117,19 +112,31 @@ public class FbScraper extends AsyncTask<Void, Void, Void> {
String event_description = fixLinks(readFromJson(reader, "description"));
String location = fixLocation(readFromJson(reader, "location"));
String image_url = "";
try {
image_url = readFromJson(reader, "image"); // get from json
// get from event header
image_url = document.getElementsByClass("scaledImageFitWidth").first().attr("src");
} catch (Exception e) {
e.printStackTrace();
this.error = "Error: no image found";
}
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);
this.event = new FbEvent(event_name, event_start, event_end, event_description, location, image_url);
//this.event = new FbEvent("", "", "", "", "", "");
}
} catch (Exception e) {
e.printStackTrace();
this.error = "Error: Scraping event data failed";
}
} catch (IOException e) {
} catch (Exception e) {
e.printStackTrace();
this.error = "Error: URL not available";
}
@ -148,7 +155,7 @@ public class FbScraper extends AsyncTask<Void, Void, Void> {
this.main.update(event);
}
else {
main.toast(error);
main.error(error);
this.main.clear(false);
}
}

View File

@ -10,5 +10,6 @@ public class HelpActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_help);
this.getSupportActionBar().hide();
}
}

View File

@ -1,12 +1,12 @@
package com.akdev.nofbeventscraper;
import android.app.AlertDialog;
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.appbar.CollapsingToolbarLayout;
import com.google.android.material.textfield.TextInputEditText;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
@ -16,11 +16,14 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import com.google.android.material.textfield.TextInputLayout;
import com.squareup.picasso.Picasso;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
@ -36,6 +39,11 @@ public class MainActivity extends AppCompatActivity {
private TextInputEditText field_event_end;
private TextInputEditText field_event_location;
private TextInputEditText field_event_description;
private ImageView toolbar_image_view;
private CollapsingToolbarLayout toolbar_layout;
private TextInputLayout input_layout;
private FbScraper scraper;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -44,16 +52,21 @@ 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);
input_layout = (TextInputLayout) findViewById(R.id.textInputLayout);
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);
toolbar_image_view = (ImageView) findViewById(R.id.image_view);
toolbar_layout = (CollapsingToolbarLayout) findViewById(R.id.toolbar_layout);
ok_button.setEnabled(false);
paste_button.setOnClickListener(new View.OnClickListener() {
@Override
@ -68,13 +81,20 @@ public class MainActivity extends AppCompatActivity {
}
catch (NullPointerException e) {
e.printStackTrace();
toast("Error: Clipboard empty");
error("Error: Clipboard empty");
}
startScraping();
}
});
cancel_button.setOnClickListener(new View.OnClickListener() {
input_layout.setEndIconOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
clear(true);
}
});
input_layout.setErrorIconOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
clear(true);
@ -85,10 +105,11 @@ public class MainActivity extends AppCompatActivity {
@Override
public void onClick(View view) {
try {
Long start_epoch = convertTimeToEpoch(field_event_start);
Long end_epoch = convertTimeToEpoch(field_event_end);
Long start_epoch = convertTimeToEpoch(field_event_start.getText().toString());
Long end_epoch = convertTimeToEpoch(field_event_end.getText().toString());
String name = parseField(field_event_name);
String location = parseField(field_event_location);
String description = parseField(field_event_description);
String uri = parseField(field_uri_input);
@ -99,13 +120,12 @@ public class MainActivity extends AppCompatActivity {
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);
intent.putExtra(CalendarContract.Events.DESCRIPTION, uri + "\n\n" + description);
startActivity(intent);
}
catch (Exception e )
{
e.printStackTrace();
toast("Error: Invalid fields");
}
}
@ -149,12 +169,11 @@ public class MainActivity extends AppCompatActivity {
}
private Long convertTimeToEpoch (TextInputEditText field) {
Long convertTimeToEpoch(String time_str) {
try {
String time_str = field.getText().toString();
ZonedDateTime datetime = ZonedDateTime.parse(time_str, DateTimeFormatter.ISO_DATE_TIME);
LocalDateTime datetime = LocalDateTime.parse(time_str, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
return datetime.atZone(ZoneId.of("UTC")).toEpochSecond() * 1000;
return datetime.toEpochSecond() * 1000;
} catch (Exception e)
{
e.printStackTrace();
@ -169,16 +188,14 @@ public class MainActivity extends AppCompatActivity {
try {
double d = Double.parseDouble(strNum);
} catch (NumberFormatException e) {
e.printStackTrace();
//e.printStackTrace();
return false;
}
return true;
}
public void startScraping() {
String checkURI(String str)
{
try {
String str = field_uri_input.getText().toString();
// check for a valid uri
new URL(str).toURI();
@ -190,7 +207,7 @@ public class MainActivity extends AppCompatActivity {
// find event id
String[] separated = str.split("/");
for (int i = 0; i< separated.length; i++) {
for (int i = 0; i < separated.length; i++) {
if (separated[i].length() > 8 && isNumeric(separated[i])) {
eventId = separated[i];
break;
@ -200,46 +217,120 @@ public class MainActivity extends AppCompatActivity {
{
throw new Exception();
}
}
else {
} else {
throw new Exception();
}
String input_uri = "https://m.facebook.com/events/"+ eventId;
field_uri_input.setText(input_uri);
String input_uri = "https://www.facebook.com/events/" + eventId;
str = input_uri;
}
catch (Exception e) {
e.printStackTrace();
clear(false);
error("Error: Invalid URL");
str = "";
}
return str;
}
FbScraper scraper = new FbScraper(this, field_uri_input.getText().toString());
scraper.execute();
public void startScraping() {
error(null);
try {
String str = checkURI(field_uri_input.getText().toString());
if (!str.equals(""))
{
field_uri_input.setText(str);
scraper = new FbScraper(this, field_uri_input.getText().toString());
scraper.execute();
}
}
catch (Exception e) {
e.printStackTrace();
clear(true);
toast("Invalid URL");
clear(false);
error("Error: Invalid URL");
}
}
public void toast(String str) {
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
public void error(String str) {
//Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
input_layout.setError(str);
}
public void clear(boolean clearUri) {
if (clearUri) {
field_uri_input.setText("");
}
field_event_name.setText("");
field_event_name.setError(null);
field_event_start.setText("");
field_event_start.setError(null);
field_event_end.setText("");
field_event_end.setError(null);
field_event_location.setText("");
field_event_location.setError(null);
field_event_description.setText("");
field_event_description.setError(null);
toolbar_image_view.setImageDrawable(null);
toolbar_layout.setTitle(getString(R.string.app_name));
if (scraper!=null)
{
scraper.cancel(true);
scraper = null;
}
ok_button.setEnabled(false);
}
public void update(FbEvent event) {
field_event_name.setText(event.name);
input_layout.setError(null);
if (event.name.equals(""))
{
field_event_name.setError("no event name detected");
}
field_event_start.setText(event.start_date);
if (event.start_date.equals(""))
{
field_event_start.setError("no event start date detected");
}
field_event_end.setText(event.end_date);
if (event.end_date.equals(""))
{
field_event_end.setError("no event end date detected");
}
field_event_location.setText(event.location);
if (event.location.equals(""))
{
field_event_location.setError("no event location detected");
}
field_event_description.setText(event.description);
if (event.description.equals(""))
{
field_event_description.setError("no event description detected");
}
try {
Picasso.get().load(event.image_url).into(toolbar_image_view);
toolbar_layout.setTitle(" ");
} catch (Exception e)
{
e.printStackTrace();
}
ok_button.setEnabled(true);
}

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="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,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

@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".AboutActivity">
<ScrollView

View File

@ -1,29 +1,92 @@
<?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: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="200dp"
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/toolbar_layout"
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"/>
<include layout="@layout/content_main" />
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bottomButtonLayout"
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/add_to_calendar"
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,183 @@
<?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:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".MainActivity"
tools:showIn="@layout/activity_main"
android:id="@+id/nested_scroll_view" >
<LinearLayout
android:id="@+id/linearLayout4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="64dp"
android:layout_marginEnd="16dp"
android:gravity="center"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Button
android:id="@+id/paste_button"
style="@style/Widget.AppCompat.Button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@android:string/paste" />
</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">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraint_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:id="@+id/linearLayout4"
android:layout_width="0dp"
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>
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:gravity="center"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</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"
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@android:string/cancel" />
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
<Button
android:id="@+id/ok_button"
android:layout_width="wrap_content"
android:layout_marginEnd="16dp"
app:endIconCheckable="false"
app:endIconDrawable="@drawable/ic_backspace_black"
app:endIconMode="custom"
app:errorIconDrawable="@drawable/ic_backspace_black"
app:helperText="@string/add_link_helper"
app:helperTextEnabled="true"
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:inputType="textNoSuggestions"
android:singleLine="true"
android:textColorLink="@color/material_on_background_emphasis_high_type" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@android:string/ok" />
</LinearLayout>
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toTopOf="@+id/spacer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textInputLayout">
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="@drawable/divider"
android:orientation="vertical"
android:showDividers="middle">
<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/field_event_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:cursorVisible="true"
android: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/field_event_start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:cursorVisible="true"
android:inputType="textNoSuggestions"
android:hint="Event start"
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/field_event_end"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:cursorVisible="true"
android:inputType="textNoSuggestions"
android:hint="Event end"
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/field_event_location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="map"
android:clickable="true"
android:cursorVisible="true"
android:inputType="textNoSuggestions"
android:hint="Event location"
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/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:inputType="textNoSuggestions|textMultiLine"
android:singleLine="false"
android:textColorLink="@color/material_on_background_emphasis_high_type" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</LinearLayout>
<Space
android:id="@+id/spacer"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="@id/constraint_layout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

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

@ -2,7 +2,9 @@
<string name="app_name">NoFb Event Scraper</string>
<string name="action_about">About</string>
<string name="action_help">Help</string>
<string name="add_link_hint">Paste facebook link to the event.</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="description_heading">Description</string>
<string name="description_text">This application was developed to be used without a facebook account.

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,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.
Mew button layout.
Improve input error display.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB