1
0
mirror of https://github.com/etesync/android synced 2024-12-23 07:08:16 +00:00

Journal item: implement showing calendar events (based on etar)

This shows the calendar events in a nicer way based on Etar[1] which is
in turn based on the AOSP calendar.

1: https://github.com/Etar-Group/Etar-Calendar/
This commit is contained in:
Tom Hacohen 2017-05-11 11:47:28 +01:00
parent 7dba220d06
commit b964b8dfe1
7 changed files with 358 additions and 5 deletions

View File

@ -2,12 +2,16 @@ package com.etesync.syncadapter.ui;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -20,6 +24,19 @@ import com.etesync.syncadapter.model.CollectionInfo;
import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.model.SyncEntry;
import net.fortuna.ical4j.model.component.VAlarm;
import net.fortuna.ical4j.model.property.Attendee;
import org.apache.commons.codec.Charsets;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Formatter;
import java.util.Locale;
import at.bitfire.ical4android.Event;
import at.bitfire.ical4android.InvalidCalendarException;
import io.requery.Persistable;
import io.requery.sql.EntityDataStore;
@ -102,7 +119,11 @@ public class JournalItemActivity extends BaseActivity implements Refreshable {
@Override
public Fragment getItem(int position) {
return TextFragment.newInstance(syncEntry);
if (position == 0) {
return EventFragment.newInstance(info, syncEntry);
} else {
return TextFragment.newInstance(syncEntry);
}
}
}
@ -128,6 +149,140 @@ public class JournalItemActivity extends BaseActivity implements Refreshable {
}
}
public static class EventFragment extends Fragment {
CollectionInfo info;
SyncEntry syncEntry;
public static EventFragment newInstance(CollectionInfo info, SyncEntry syncEntry) {
EventFragment frag = new EventFragment();
Bundle args = new Bundle(1);
args.putSerializable(Constants.KEY_COLLECTION_INFO, info);
args.putSerializable(KEY_SYNC_ENTRY, syncEntry);
frag.setArguments(args);
return frag;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.event_info, container, false);
info = (CollectionInfo) getArguments().getSerializable(Constants.KEY_COLLECTION_INFO);
syncEntry = (SyncEntry) getArguments().getSerializable(KEY_SYNC_ENTRY);
new LoadEventTask(v).execute();
return v;
}
private class LoadEventTask extends AsyncTask<Void, Void, Event> {
View view;
LoadEventTask(View v) {
super();
view = v;
}
@Override
protected Event doInBackground(Void... aVoids) {
InputStream is = new ByteArrayInputStream(syncEntry.getContent().getBytes(Charsets.UTF_8));
try {
Event event = Event.fromStream(is, Charsets.UTF_8, null)[0];
return event;
} catch (InvalidCalendarException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Event event) {
final View loader = view.findViewById(R.id.event_info_loading_msg);
loader.setVisibility(View.GONE);
final View contentContainer = view.findViewById(R.id.event_info_scroll_view);
contentContainer.setVisibility(View.VISIBLE);
setTextViewText(view, R.id.title, event.summary);
setTextViewText(view, R.id.when_datetime, getDisplayedDatetime(event.dtStart.getDate().getTime(), event.dtEnd.getDate().getTime(), event.isAllDay(), getContext()));
setTextViewText(view, R.id.where, event.location);
if (event.organizer != null) {
TextView tv = (TextView) view.findViewById(R.id.organizer);
tv.setText(event.organizer.getCalAddress().toString().replaceFirst("mailto:", ""));
} else {
View organizer = view.findViewById(R.id.organizer_container);
organizer.setVisibility(View.GONE);
}
setTextViewText(view, R.id.description, event.description);
boolean first = true;
StringBuilder sb = new StringBuilder();
for (Attendee attendee : event.attendees) {
if (first) {
first = false;
sb.append("Attendees: ");
} else {
sb.append(", ");
}
sb.append(attendee.getCalAddress().toString().replaceFirst("mailto:", ""));
}
setTextViewText(view, R.id.attendees, sb.toString());
first = true;
sb = new StringBuilder();
for (VAlarm alarm : event.alarms) {
if (first) {
first = false;
sb.append("Reminders: ");
} else {
sb.append(", ");
}
sb.append(alarm.getTrigger().getValue());
}
setTextViewText(view, R.id.reminders, sb.toString());
}
}
private static void setTextViewText(View parent, int id, String text) {
TextView tv = (TextView) parent.findViewById(id);
if (text == null) {
tv.setVisibility(View.GONE);
} else {
tv.setText(text);
}
}
public static String getDisplayedDatetime(long startMillis, long endMillis, boolean allDay, Context context) {
// Configure date/time formatting.
int flagsDate = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY;
int flagsTime = DateUtils.FORMAT_SHOW_TIME;
if (DateFormat.is24HourFormat(context)) {
flagsTime |= DateUtils.FORMAT_24HOUR;
}
String datetimeString = null;
if (allDay) {
// For multi-day allday events or single-day all-day events that are not
// today or tomorrow, use framework formatter.
Formatter f = new Formatter(new StringBuilder(50), Locale.getDefault());
datetimeString = DateUtils.formatDateRange(context, f, startMillis,
endMillis, flagsDate, Time.TIMEZONE_UTC).toString();
} else {
// For multiday events, shorten day/month names.
// Example format: "Fri Apr 6, 5:00pm - Sun, Apr 8, 6:00pm"
int flagsDatetime = flagsDate | flagsTime | DateUtils.FORMAT_ABBREV_MONTH |
DateUtils.FORMAT_ABBREV_WEEKDAY;
datetimeString = DateUtils.formatDateRange(context, startMillis, endMillis,
flagsDatetime);
}
return datetimeString;
}
}
@Override
protected void onResume() {
super.onResume();

View File

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2006 The Android Open Source Project
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
http://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.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#fafafa"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="0dp">
<RelativeLayout
android:id="@+id/event_info_loading_msg"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/event_info_progress_bar"
android:layout_width="100dip"
android:layout_height="100dip"
android:indeterminate="true"
android:layout_centerInParent="true" />
<TextView
android:layout_below="@id/event_info_progress_bar"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dip"
android:text="@string/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/event_info_scroll_view"
android:orientation="vertical"
android:layout_width="match_parent"
android:fadingEdge="none"
android:animateLayoutChanges="true"
android:visibility="gone"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- Container for the event's headline
Name, Date, Time & Location
-->
<include layout="@layout/event_info_headline" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dip"
android:paddingRight="16dip"
android:layout_marginTop="8dip"
android:orientation="vertical">
<!-- Organizer -->
<LinearLayout
android:id="@+id/organizer_container"
android:visibility="gone"
android:paddingRight="16dip"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/organizer_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/event_info_organizer"
style="?android:attr/textAppearanceSmall"
android:textSize="14sp"/>
<TextView
android:id="@+id/organizer"
android:layout_width="0px"
android:layout_height="wrap_content"
android:ellipsize="end"
android:layout_weight="1"
android:singleLine="true"
android:layout_marginLeft="2dip"
android:textIsSelectable="true"
style="?android:attr/textAppearanceSmall"
android:textSize="14sp"/>
</LinearLayout>
<!-- DESCRIPTION -->
<TextView
android:id="@+id/description"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
<TextView
android:id="@+id/attendees"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
<TextView
android:id="@+id/reminders"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
</LinearLayout>
</LinearLayout>
</ScrollView>
</FrameLayout>

View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright (C) 2013 The Android Open Source Project
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
http://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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/event_info_headline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@color/orange400"
android:orientation="vertical"
android:paddingBottom="16dip"
android:paddingLeft="16dip"
android:paddingRight="16dip"
android:paddingTop="8dip">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<!-- WHAT -->
<TextView
android:id="@+id/title"
style="?android:attr/textAppearanceLarge"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".8"
android:autoLink="all"
android:textColor="@color/White"
android:textIsSelectable="true"
android:textSize="24sp"
android:textStyle="bold" />
</LinearLayout>
<!-- WHEN -->
<TextView
android:id="@+id/when_datetime"
style="?android:attr/textAppearanceLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dip"
android:textColor="@color/White"
android:textIsSelectable="true"
android:textSize="14sp" />
<TextView
android:id="@+id/when_repeat"
style="?android:attr/textAppearanceLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-3dip"
android:textColor="@color/White"
android:textSize="14sp" />
<!-- WHERE -->
<TextView
android:id="@+id/where"
style="?android:attr/textAppearanceLarge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dip"
android:ellipsize="end"
android:singleLine="false"
android:textColor="@color/White"
android:textIsSelectable="true"
android:textSize="14sp" />
</LinearLayout>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#fafafa"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

View File

@ -25,7 +25,6 @@
</LinearLayout>
<LinearLayout
android:id="@+id/add_member"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/activity_margin"

View File

@ -13,5 +13,4 @@
<dimen name="fab_margin">16dp</dimen>
<dimen name="nav_header_vertical_spacing">16dp</dimen>
</resources>

View File

@ -7,7 +7,7 @@
~ http://www.gnu.org/licenses/gpl.html
-->
<resources xmlns:tools="http://schemas.android.com/tools">
<resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- common strings -->
<string name="app_name">EteSync</string>
@ -249,7 +249,6 @@
<string name="delete_collection_confirm_title">Are you sure?</string>
<string name="delete_collection_confirm_warning">This collection (%s) and all its data will be removed from the server.</string>
<string name="delete_collection_deleting_collection">Deleting collection</string>
<string name="collection_stats_title">Stats</string>
<!-- JournalViewer -->
<string name="journal_entries_list_empty">No entries found for this journal</string>
@ -300,4 +299,6 @@
<string name="import_button_local">From Account</string>
<string name="import_select_account">Select Account</string>
<!-- Event (from Etar) -->
<string name="event_info_organizer">Organizer:</string>
</resources>