1
0
mirror of https://github.com/etesync/android synced 2025-01-11 00:01:12 +00:00

Journal viewer: Improve the look and feel of the journal viewer

This includes using icons instead of text for the journal list, and
showing events and contacts in a prettier way.
This commit is contained in:
Tom Hacohen 2017-05-11 22:43:29 +01:00
commit 9f069692c9
18 changed files with 931 additions and 39 deletions

View File

@ -220,6 +220,7 @@
</activity>
<activity android:name=".ui.ViewCollectionActivity"/>
<activity android:name=".ui.CollectionMembersActivity"/>
<activity android:name=".ui.JournalItemActivity"/>
<activity android:name=".ui.importlocal.ImportActivity"/>
<activity android:name=".ui.AccountSettingsActivity"/>
<activity android:name=".ui.CreateCollectionActivity"/>

View File

@ -4,9 +4,11 @@ import com.etesync.syncadapter.GsonHelper;
import com.etesync.syncadapter.journalmanager.Crypto;
import com.etesync.syncadapter.journalmanager.JournalEntryManager;
import java.io.Serializable;
import lombok.Getter;
public class SyncEntry {
public class SyncEntry implements Serializable {
@Getter
private String content;
@Getter

View File

@ -0,0 +1,431 @@
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;
import android.widget.TextView;
import com.etesync.syncadapter.App;
import com.etesync.syncadapter.Constants;
import com.etesync.syncadapter.R;
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.List;
import java.util.Locale;
import at.bitfire.ical4android.Event;
import at.bitfire.ical4android.InvalidCalendarException;
import at.bitfire.vcard4android.Contact;
import at.bitfire.vcard4android.LabeledProperty;
import ezvcard.parameter.AddressType;
import ezvcard.parameter.EmailType;
import ezvcard.parameter.RelatedType;
import ezvcard.parameter.TelephoneType;
import ezvcard.property.Address;
import ezvcard.property.Email;
import ezvcard.property.Impp;
import ezvcard.property.Related;
import ezvcard.property.Telephone;
import ezvcard.property.Url;
import io.requery.Persistable;
import io.requery.sql.EntityDataStore;
import static com.etesync.syncadapter.ui.journalviewer.ListEntriesFragment.setJournalEntryView;
public class JournalItemActivity extends BaseActivity implements Refreshable {
private static final String KEY_SYNC_ENTRY = "syncEntry";
private JournalEntity journalEntity;
protected CollectionInfo info;
private SyncEntry syncEntry;
public static Intent newIntent(Context context, CollectionInfo info, SyncEntry syncEntry) {
Intent intent = new Intent(context, JournalItemActivity.class);
intent.putExtra(Constants.KEY_COLLECTION_INFO, info);
intent.putExtra(KEY_SYNC_ENTRY, syncEntry);
return intent;
}
@Override
public void refresh() {
EntityDataStore<Persistable> data = ((App) getApplicationContext()).getData();
journalEntity = JournalEntity.fetch(data, info.getServiceEntity(data), info.uid);
if ((journalEntity == null) || journalEntity.isDeleted()) {
finish();
return;
}
info = journalEntity.getInfo();
setTitle(info.displayName);
setJournalEntryView(findViewById(R.id.journal_list_item), info, syncEntry);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.journal_item_activity);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
info = (CollectionInfo) getIntent().getExtras().getSerializable(Constants.KEY_COLLECTION_INFO);
syncEntry = (SyncEntry) getIntent().getExtras().getSerializable(KEY_SYNC_ENTRY);
refresh();
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
viewPager.setAdapter(new TabsAdapter(getSupportFragmentManager(), info, syncEntry));
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
}
private static class TabsAdapter extends FragmentPagerAdapter {
private CollectionInfo info;
private SyncEntry syncEntry;
public TabsAdapter(FragmentManager fm, CollectionInfo info, SyncEntry syncEntry) {
super(fm);
this.info = info;
this.syncEntry = syncEntry;
}
@Override
public int getCount() {
// FIXME: Make it depend on info type (only have non-raw for known types)
return 2;
}
@Override
public CharSequence getPageTitle(int position) {
// FIXME: use string resources
if (position == 0) {
return "Main";
} else {
return "Raw";
}
}
@Override
public Fragment getItem(int position) {
if (position == 0) {
return PrettyFragment.newInstance(info, syncEntry);
} else {
return TextFragment.newInstance(syncEntry);
}
}
}
public static class TextFragment extends Fragment {
public static TextFragment newInstance(SyncEntry syncEntry) {
TextFragment frag = new TextFragment();
Bundle args = new Bundle(1);
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.text_fragment, container, false);
TextView tv = (TextView) v.findViewById(R.id.content);
SyncEntry syncEntry = (SyncEntry) getArguments().getSerializable(KEY_SYNC_ENTRY);
tv.setText(syncEntry.getContent());
return v;
}
}
public static class PrettyFragment extends Fragment {
CollectionInfo info;
SyncEntry syncEntry;
public static PrettyFragment newInstance(CollectionInfo info, SyncEntry syncEntry) {
PrettyFragment frag = new PrettyFragment();
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 = null;
info = (CollectionInfo) getArguments().getSerializable(Constants.KEY_COLLECTION_INFO);
syncEntry = (SyncEntry) getArguments().getSerializable(KEY_SYNC_ENTRY);
switch (info.type) {
case ADDRESS_BOOK:
v = inflater.inflate(R.layout.contact_info, container, false);
new LoadContactTask(v).execute();
break;
case CALENDAR:
v = inflater.inflate(R.layout.event_info, container, false);
new LoadEventTask(v).execute();
break;
}
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 {
return Event.fromStream(is, Charsets.UTF_8, null)[0];
} 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 class LoadContactTask extends AsyncTask<Void, Void, Contact> {
View view;
LoadContactTask(View v) {
super();
view = v;
}
@Override
protected Contact doInBackground(Void... aVoids) {
InputStream is = new ByteArrayInputStream(syncEntry.getContent().getBytes(Charsets.UTF_8));
try {
return Contact.fromStream(is, Charsets.UTF_8, null)[0];
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Contact contact) {
final View loader = view.findViewById(R.id.loading_msg);
loader.setVisibility(View.GONE);
final View contentContainer = view.findViewById(R.id.content_container);
contentContainer.setVisibility(View.VISIBLE);
TextView tv = (TextView) view.findViewById(R.id.display_name);
tv.setText(contact.displayName);
final ViewGroup mainCard = (ViewGroup) view.findViewById(R.id.main_card);
final ViewGroup aboutCard = (ViewGroup) view.findViewById(R.id.about_card);
aboutCard.findViewById(R.id.title_container).setVisibility(View.VISIBLE);
// TEL
for (LabeledProperty<Telephone> labeledPhone : contact.phoneNumbers) {
List<TelephoneType> types = labeledPhone.property.getTypes();
String type = (types.size() > 0) ? types.get(0).getValue() : null;
addInfoItem(view.getContext(), mainCard, "Phone", type, labeledPhone.property.getText());
}
// EMAIL
for (LabeledProperty<Email> labeledEmail : contact.emails) {
List<EmailType> types = labeledEmail.property.getTypes();
String type = (types.size() > 0) ? types.get(0).getValue() : null;
addInfoItem(view.getContext(), mainCard, "Email", type, labeledEmail.property.getValue());
}
// ORG, TITLE, ROLE
if (contact.organization != null) {
addInfoItem(view.getContext(), aboutCard, "Organization", contact.jobTitle, contact.organization.getValues().get(0));
}
if (contact.jobDescription != null) {
addInfoItem(view.getContext(), aboutCard, "Job Description", null, contact.jobTitle);
}
// IMPP
for (LabeledProperty<Impp> labeledImpp : contact.impps) {
addInfoItem(view.getContext(), mainCard, "Instant Messaging", labeledImpp.property.getProtocol(), labeledImpp.property.getHandle());
}
// NICKNAME
if (contact.nickName != null) {
addInfoItem(view.getContext(), aboutCard, "Nickname", null, contact.nickName.getValues().get(0));
}
// ADR
for (LabeledProperty<Address> labeledAddress : contact.addresses) {
List<AddressType> types = labeledAddress.property.getTypes();
String type = (types.size() > 0) ? types.get(0).getValue() : null;
addInfoItem(view.getContext(), mainCard, "Address", type, labeledAddress.property.getLabel());
}
// NOTE
if (contact.note != null) {
addInfoItem(view.getContext(), aboutCard, "Note", null, contact.note);
}
// URL
for (LabeledProperty<Url> labeledUrl : contact.urls) {
addInfoItem(view.getContext(), aboutCard, "Website", null, labeledUrl.property.getValue());
}
// ANNIVERSARY
if (contact.anniversary != null) {
String date = getDisplayedDatetime(contact.anniversary.getDate().getTime(), contact.anniversary.getDate().getTime(), true, getContext());
addInfoItem(view.getContext(), aboutCard, "Anniversary", null, date);
}
// BDAY
if (contact.birthDay != null) {
String date = getDisplayedDatetime(contact.birthDay.getDate().getTime(), contact.birthDay.getDate().getTime(), true, getContext());
addInfoItem(view.getContext(), aboutCard, "Birthday", null, date);
}
// RELATED
for (Related related : contact.relations) {
List<RelatedType> types = related.getTypes();
String type = (types.size() > 0) ? types.get(0).getValue() : null;
addInfoItem(view.getContext(), aboutCard, "Relation", type, related.getText());
}
// PHOTO
// if (contact.photo != null)
}
}
private static View addInfoItem(Context context, ViewGroup parent, String type, String label, String value) {
ViewGroup layout = (ViewGroup) parent.findViewById(R.id.container);
View infoItem = LayoutInflater.from(context).inflate(R.layout.contact_info_item, layout, false);
layout.addView(infoItem);
setTextViewText(infoItem, R.id.type, type);
setTextViewText(infoItem, R.id.title, label);
setTextViewText(infoItem, R.id.content, value);
parent.setVisibility(View.VISIBLE);
return infoItem;
}
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();
refresh();
}
}

View File

@ -13,12 +13,12 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ListFragment;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.etesync.syncadapter.App;
@ -27,9 +27,10 @@ import com.etesync.syncadapter.model.CollectionInfo;
import com.etesync.syncadapter.model.EntryEntity;
import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.model.JournalModel;
import com.etesync.syncadapter.model.SyncEntry;
import com.etesync.syncadapter.ui.JournalItemActivity;
import java.util.List;
import java.util.Locale;
import io.requery.Persistable;
import io.requery.sql.EntityDataStore;
@ -81,9 +82,7 @@ public class ListEntriesFragment extends ListFragment implements AdapterView.OnI
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
EntryEntity entry = (EntryEntity) getListAdapter().getItem(position);
new AlertDialog.Builder(getActivity())
.setTitle("Raw dump")
.setMessage("Action: " + entry.getContent().getAction().toString() + "\nIntegrity: " + entry.getUid() + "\n" + entry.getContent().getContent()).show();
startActivity(JournalItemActivity.newIntent(getContext(), info, entry.getContent()));
}
class EntriesListAdapter extends ArrayAdapter<EntryEntity> {
@ -91,20 +90,6 @@ public class ListEntriesFragment extends ListFragment implements AdapterView.OnI
super(context, R.layout.journal_viewer_list_item);
}
private String getLine(String content, String prefix) {
if (content == null) {
return null;
}
int start = content.indexOf(prefix);
if (start >= 0) {
int end = content.indexOf("\n", start);
content = content.substring(start + prefix.length(), end);
} else {
content = null;
}
return content;
}
@Override
@NonNull
public View getView(int position, View v, @NonNull ViewGroup parent) {
@ -117,26 +102,62 @@ public class ListEntriesFragment extends ListFragment implements AdapterView.OnI
// FIXME: hacky way to make it show sensible info
CollectionInfo info = journalEntity.getInfo();
String fullContent = entryEntity.getContent().getContent();
String prefix;
if (info.type == CollectionInfo.Type.CALENDAR) {
prefix = "SUMMARY:";
} else {
prefix = "FN:";
}
String content = getLine(fullContent, prefix);
content = (content != null) ? content : entryEntity.getUid().substring(0, 20);
tv.setText(String.format(Locale.getDefault(), "%s: %s", entryEntity.getContent().getAction().toString(), content));
tv = (TextView) v.findViewById(R.id.description);
content = getLine(fullContent, "UID:");
content = "UID: " + ((content != null) ? content : "Not found");
tv.setText(content);
setJournalEntryView(v, info, entryEntity.getContent());
return v;
}
}
private static String getLine(String content, String prefix) {
if (content == null) {
return null;
}
int start = content.indexOf(prefix);
if (start >= 0) {
int end = content.indexOf("\n", start);
content = content.substring(start + prefix.length(), end);
} else {
content = null;
}
return content;
}
public static void setJournalEntryView(View v, CollectionInfo info, SyncEntry syncEntry) {
TextView tv = (TextView) v.findViewById(R.id.title);
// FIXME: hacky way to make it show sensible info
String fullContent = syncEntry.getContent();
String prefix;
if (info.type == CollectionInfo.Type.CALENDAR) {
prefix = "SUMMARY:";
} else {
prefix = "FN:";
}
String content = getLine(fullContent, prefix);
content = (content != null) ? content : "Not found";
tv.setText(content);
tv = (TextView) v.findViewById(R.id.description);
content = getLine(fullContent, "UID:");
content = "UID: " + ((content != null) ? content : "Not found");
tv.setText(content);
ImageView action = (ImageView) v.findViewById(R.id.action);
switch (syncEntry.getAction()) {
case ADD:
action.setImageResource(R.drawable.action_add);
break;
case CHANGE:
action.setImageResource(R.drawable.action_change);
break;
case DELETE:
action.setImageResource(R.drawable.action_delete);
break;
}
}
private class JournalFetch extends AsyncTask<Void, Void, List<EntryEntity>> {
@Override

View File

@ -0,0 +1,17 @@
<!--
~ Copyright © 2013 2016 Ricki Hirner (bitfire web engineering).
~ All rights reserved. This program and the accompanying materials
~ are made available under the terms of the GNU Public License v3.0
~ which accompanies this distribution, and is available at
~ http://www.gnu.org/licenses/gpl.html
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF16B14B"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:alpha="0.54" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFEB115" android:pathData="M20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18,2.9 17.35,2.9 16.96,3.29L15.12,5.12L18.87,8.87M3,17.25V21H6.75L17.81,9.93L14.06,6.18L3,17.25Z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:alpha="0.54" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFF20C0C" android:pathData="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z"/>
</vector>

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fafafa"
android:padding="0dp">
<RelativeLayout
android:id="@+id/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:layout_centerInParent="true"
android:indeterminate="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/event_info_progress_bar"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dip"
android:text="@string/loading" />
</RelativeLayout>
<LinearLayout
android:id="@+id/content_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:id="@+id/display_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/orange400"
android:padding="16dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="@color/White"
android:textIsSelectable="true"
android:textStyle="bold" />
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:fadingEdge="none"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include
android:id="@+id/main_card"
layout="@layout/contact_info_item_group"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"
android:visibility="gone" />
<include
android:id="@+id/about_card"
layout="@layout/contact_info_item_group"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"
android:visibility="gone" />
</LinearLayout>
</ScrollView>
</LinearLayout>
</FrameLayout>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_margin"
android:layout_marginRight="@dimen/activity_margin"
android:layout_marginTop="@dimen/activity_margin"
android:orientation="vertical">
<TextView
android:id="@+id/type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:textIsSelectable="true"
android:textStyle="bold" />
<TextView
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:textIsSelectable="true" />
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:textIsSelectable="true" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="14dp"
android:background="?android:attr/listDivider" />
</LinearLayout>

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/title_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/activity_margin"
android:text="@string/about"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/orange400"
android:textStyle="bold" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
</LinearLayout>
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>

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

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
style="@style/tablayout"
app:tabMode="fixed"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<include
android:id="@+id/journal_list_item"
layout="@layout/journal_viewer_list_item"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</LinearLayout>

View File

@ -7,6 +7,11 @@
android:padding="8dp"
android:gravity="center_vertical">
<ImageView
android:id="@+id/action"
android:layout_width="32dp"
android:layout_height="32dp" />
<LinearLayout android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"

View File

@ -0,0 +1,19 @@
<?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">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/activity_margin" />
</ScrollView>
</LinearLayout>

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>
@ -128,6 +128,9 @@
<string name="collection_members_remove_title">Remove member</string>
<string name="collection_members_remove">Would you like to revoke %s\'s access?\nPlease be advised that a malicious user would potentially be able to retain access to encryption keys. Please refer to the FAQ for more information.</string>
<!-- JournalItemActivity -->
<string name="about">About</string>
<!-- PermissionsActivity -->
<string name="permissions_title">EteSync permissions</string>
<string name="permissions_calendar">Calendar permissions</string>
@ -247,7 +250,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>
@ -298,4 +300,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>