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:
commit
9f069692c9
@ -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"/>
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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
|
||||
|
17
app/src/main/res/drawable/action_add.xml
Normal file
17
app/src/main/res/drawable/action_add.xml
Normal 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>
|
5
app/src/main/res/drawable/action_change.xml
Normal file
5
app/src/main/res/drawable/action_change.xml
Normal 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>
|
5
app/src/main/res/drawable/action_delete.xml
Normal file
5
app/src/main/res/drawable/action_delete.xml
Normal 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>
|
78
app/src/main/res/layout/contact_info.xml
Normal file
78
app/src/main/res/layout/contact_info.xml
Normal 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>
|
38
app/src/main/res/layout/contact_info_item.xml
Normal file
38
app/src/main/res/layout/contact_info_item.xml
Normal 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>
|
43
app/src/main/res/layout/contact_info_item_group.xml
Normal file
43
app/src/main/res/layout/contact_info_item_group.xml
Normal 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>
|
116
app/src/main/res/layout/event_info.xml
Normal file
116
app/src/main/res/layout/event_info.xml
Normal 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>
|
82
app/src/main/res/layout/event_info_headline.xml
Normal file
82
app/src/main/res/layout/event_info_headline.xml
Normal 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>
|
27
app/src/main/res/layout/journal_item_activity.xml
Normal file
27
app/src/main/res/layout/journal_item_activity.xml
Normal 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>
|
@ -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"
|
||||
|
19
app/src/main/res/layout/text_fragment.xml
Normal file
19
app/src/main/res/layout/text_fragment.xml
Normal 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>
|
@ -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"
|
||||
|
@ -13,5 +13,4 @@
|
||||
<dimen name="fab_margin">16dp</dimen>
|
||||
|
||||
<dimen name="nav_header_vertical_spacing">16dp</dimen>
|
||||
|
||||
</resources>
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user