1
0
mirror of https://github.com/etesync/android synced 2024-12-23 15:18:14 +00:00

Add a collection view activity and move the journal viewer there

This commit is contained in:
Tom Hacohen 2017-03-10 15:18:00 +00:00
parent 3d217f47af
commit b0b5891e40
12 changed files with 311 additions and 101 deletions

View File

@ -181,7 +181,7 @@
android:name=".ui.AccountActivity"
android:parentActivityName=".ui.AccountsActivity">
</activity>
<activity android:name=".ui.JournalViewerActivity"/>
<activity android:name=".ui.ViewCollectionActivity"/>
<activity android:name=".ui.AccountSettingsActivity"/>
<activity android:name=".ui.CreateCollectionActivity"/>
<activity android:name=".ui.EditCollectionActivity"/>

View File

@ -180,13 +180,13 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
final ArrayAdapter<CollectionInfo> adapter = (ArrayAdapter)list.getAdapter();
final CollectionInfo info = adapter.getItem(position);
startActivity(EditCollectionActivity.newIntent(AccountActivity.this, account, info, (adapter.getCount() > 1)));
startActivity(ViewCollectionActivity.newIntent(AccountActivity.this, account, info));
}
};
public void onChangeJournalClick(View view) {
Intent intent = new Intent(this, JournalViewerActivity.class);
intent.putExtra(JournalViewerActivity.EXTRA_ACCOUNT, account);
Intent intent = new Intent(this, ViewCollectionActivity.class);
intent.putExtra(ViewCollectionActivity.EXTRA_ACCOUNT, account);
startActivity(intent);
}

View File

@ -18,20 +18,20 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import com.etesync.syncadapter.App;
import com.etesync.syncadapter.R;
import com.etesync.syncadapter.model.CollectionInfo;
import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.resource.LocalCalendar;
import io.requery.Persistable;
import io.requery.sql.EntityDataStore;
public class EditCollectionActivity extends CreateCollectionActivity {
private final static String EXTRA_ALLOW_DELETE = "allowDelete";
protected boolean allowDelete;
public static Intent newIntent(Context context, Account account, CollectionInfo info, boolean allowDelete) {
public static Intent newIntent(Context context, Account account, CollectionInfo info) {
Intent intent = new Intent(context, EditCollectionActivity.class);
intent.putExtra(CreateCollectionActivity.EXTRA_ACCOUNT, account);
intent.putExtra(CreateCollectionActivity.EXTRA_COLLECTION_INFO, info);
intent.putExtra(EXTRA_ALLOW_DELETE, allowDelete);
return intent;
}
@ -39,8 +39,6 @@ public class EditCollectionActivity extends CreateCollectionActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
allowDelete = getIntent().getExtras().getBoolean(EXTRA_ALLOW_DELETE, false);
setTitle(R.string.edit_collection);
if (info.type == CollectionInfo.Type.CALENDAR) {
@ -70,7 +68,10 @@ public class EditCollectionActivity extends CreateCollectionActivity {
}
public void onDeleteCollection(MenuItem item) {
if (!allowDelete) {
EntityDataStore<Persistable> data = ((App) getApplication()).getData();
int journalCount = data.count(JournalEntity.class).where(JournalEntity.SERVICE.eq(info.serviceID)).get().value();
if (journalCount < 2) {
new AlertDialog.Builder(this)
.setIcon(R.drawable.ic_error_dark)
.setTitle(R.string.account_delete_collection_last_title)

View File

@ -1,69 +0,0 @@
/*
* 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
*/
package com.etesync.syncadapter.ui;
import android.accounts.Account;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.NavUtils;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import com.etesync.syncadapter.App;
import com.etesync.syncadapter.ui.journalviewer.ListJournalsFragment;
public class JournalViewerActivity extends AppCompatActivity {
public final static String EXTRA_ACCOUNT = "account";
private Account account;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (savedInstanceState == null) {
account = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);
// first call, add fragment
getSupportFragmentManager().beginTransaction()
.replace(android.R.id.content, ListJournalsFragment.newInstance(account))
.commit();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
if (!getSupportFragmentManager().popBackStackImmediate()) {
finish();
}
return true;
}
return false;
}
@Override
protected void onResume() {
super.onResume();
App app = (App)getApplicationContext();
if (app.getCertManager() != null)
app.getCertManager().appInForeground = true;
}
@Override
protected void onPause() {
super.onPause();
App app = (App)getApplicationContext();
if (app.getCertManager() != null)
app.getCertManager().appInForeground = false;
}
}

View File

@ -0,0 +1,160 @@
/*
* 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
*/
package com.etesync.syncadapter.ui;
import android.accounts.Account;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import com.etesync.syncadapter.App;
import com.etesync.syncadapter.R;
import com.etesync.syncadapter.model.CollectionInfo;
import com.etesync.syncadapter.model.EntryEntity;
import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.resource.LocalAddressBook;
import com.etesync.syncadapter.resource.LocalCalendar;
import com.etesync.syncadapter.ui.journalviewer.ListEntriesFragment;
import java.util.Locale;
import at.bitfire.ical4android.CalendarStorageException;
import at.bitfire.vcard4android.ContactsStorageException;
import io.requery.Persistable;
import io.requery.sql.EntityDataStore;
public class ViewCollectionActivity extends AppCompatActivity {
public final static String EXTRA_ACCOUNT = "account",
EXTRA_COLLECTION_INFO = "collectionInfo";
private Account account;
protected CollectionInfo info;
public static Intent newIntent(Context context, Account account, CollectionInfo info) {
Intent intent = new Intent(context, ViewCollectionActivity.class);
intent.putExtra(ViewCollectionActivity.EXTRA_ACCOUNT, account);
intent.putExtra(ViewCollectionActivity.EXTRA_COLLECTION_INFO, info);
return intent;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_collection_activity);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
account = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);
info = (CollectionInfo) getIntent().getExtras().getSerializable(EXTRA_COLLECTION_INFO);
getSupportFragmentManager().beginTransaction()
.add(R.id.list_entries_container, ListEntriesFragment.newInstance(info))
.commit();
final TextView stats = (TextView) findViewById(R.id.stats);
final View colorSquare = findViewById(R.id.color);
if (info.type == CollectionInfo.Type.CALENDAR) {
if (info.color != null) {
colorSquare.setBackgroundColor(info.color);
} else {
colorSquare.setBackgroundColor(LocalCalendar.defaultColor);
}
try {
LocalCalendar resource = (LocalCalendar) LocalCalendar.find(account, this.getContentResolver().acquireContentProviderClient(CalendarContract.CONTENT_URI),
LocalCalendar.Factory.INSTANCE, CalendarContract.Calendars.NAME + "=?", new String[]{info.url})[0];
long count = resource.count();
EntityDataStore<Persistable> data = ((App) getApplication()).getData();
int entryCount = -1;
final JournalEntity journalEntity = data.select(JournalEntity.class).where(JournalEntity.UID.eq(info.url)).limit(1).get().firstOrNull();
if (journalEntity != null) {
entryCount = data.count(EntryEntity.class).where(EntryEntity.JOURNAL.eq(journalEntity)).get().value();
}
stats.setText(String.format(Locale.getDefault(), "Events: %d, Journal entries: %d", count, entryCount));
} catch (CalendarStorageException e) {
e.printStackTrace();
stats.setText("Stats loading error.");
}
} else {
colorSquare.setVisibility(View.GONE);
try {
LocalAddressBook resource = new LocalAddressBook(account, this.getContentResolver().acquireContentProviderClient(ContactsContract.Contacts.CONTENT_URI));
long count = resource.count();
EntityDataStore<Persistable> data = ((App) getApplication()).getData();
int entryCount = -1;
final JournalEntity journalEntity = data.select(JournalEntity.class).where(JournalEntity.UID.eq(info.url)).limit(1).get().firstOrNull();
if (journalEntity != null) {
entryCount = data.count(EntryEntity.class).where(EntryEntity.JOURNAL.eq(journalEntity)).get().value();
};
stats.setText(String.format(Locale.getDefault(), "Contacts: %d, Journal Entries: %d", count, entryCount));
} catch (ContactsStorageException e) {
e.printStackTrace();
stats.setText("Stats loading error.");
}
}
final TextView title = (TextView) findViewById(R.id.display_name);
title.setText(info.displayName);
final TextView desc = (TextView) findViewById(R.id.description);
desc.setText(info.description);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_view_collection, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
if (!getSupportFragmentManager().popBackStackImmediate()) {
finish();
}
return true;
}
return false;
}
@Override
protected void onResume() {
super.onResume();
App app = (App) getApplicationContext();
if (app.getCertManager() != null)
app.getCertManager().appInForeground = true;
}
@Override
protected void onPause() {
super.onPause();
App app = (App) getApplicationContext();
if (app.getCertManager() != null)
app.getCertManager().appInForeground = false;
}
public void onEditCollection(MenuItem item) {
startActivity(EditCollectionActivity.newIntent(this, account, info));
// FIXME: Handle it more gracefully
finish();
}
}

View File

@ -10,6 +10,7 @@ package com.etesync.syncadapter.ui.journalviewer;
import android.content.Context;
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;
@ -21,6 +22,7 @@ import android.widget.TextView;
import com.etesync.syncadapter.App;
import com.etesync.syncadapter.R;
import com.etesync.syncadapter.model.CollectionInfo;
import com.etesync.syncadapter.model.EntryEntity;
import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.model.JournalModel;
@ -31,15 +33,15 @@ import io.requery.Persistable;
import io.requery.sql.EntityDataStore;
public class ListEntriesFragment extends ListFragment implements AdapterView.OnItemClickListener {
protected static final String EXTRA_JOURNAL = "journal";
protected static final String EXTRA_COLLECTION_INFO = "collectionInfo";
private EntityDataStore<Persistable> data;
private JournalEntity journalEntity;
public static ListEntriesFragment newInstance(String journal) {
public static ListEntriesFragment newInstance(CollectionInfo info) {
ListEntriesFragment frag = new ListEntriesFragment();
Bundle args = new Bundle(1);
args.putSerializable(EXTRA_JOURNAL, journal);
args.putSerializable(EXTRA_COLLECTION_INFO, info);
frag.setArguments(args);
return frag;
}
@ -48,14 +50,14 @@ public class ListEntriesFragment extends ListFragment implements AdapterView.OnI
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
data = ((App) getContext().getApplicationContext()).getData();
String name = getArguments().getString(EXTRA_JOURNAL);
journalEntity = JournalModel.Journal.fetch(data, name);
CollectionInfo info = (CollectionInfo) getArguments().getSerializable(EXTRA_COLLECTION_INFO);
journalEntity = JournalModel.Journal.fetch(data, info.url);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
getActivity().setTitle(String.format(Locale.getDefault(), "%s (%d)", journalEntity.getInfo().displayName, data.count(EntryEntity.class).where(EntryEntity.JOURNAL.eq(journalEntity)).get().value()));
return inflater.inflate(R.layout.journal_viewer_list_entries, container, false);
getActivity().setTitle(journalEntity.getInfo().displayName);
return inflater.inflate(R.layout.journal_viewer_list, container, false);
}
@Override
@ -74,24 +76,52 @@ public class ListEntriesFragment extends ListFragment implements AdapterView.OnI
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
EntryEntity entry = (EntryEntity) getListAdapter().getItem(position);
new AlertDialog.Builder(getActivity())
.setTitle("Raw dump: " + entry.getUid())
.setMessage("Action: " + entry.getContent().getAction().toString() + "\nUid: " + entry.getUid() + "\n" + entry.getContent().getContent()).show();
.setTitle("Raw dump")
.setMessage("Action: " + entry.getContent().getAction().toString() + "\nIntegrity: " + entry.getUid() + "\n" + entry.getContent().getContent()).show();
}
static class EntriesListAdapter extends ArrayAdapter<EntryEntity> {
public EntriesListAdapter(Context context) {
super(context, R.layout.journal_viewer_list_journals_item);
class EntriesListAdapter extends ArrayAdapter<EntryEntity> {
EntriesListAdapter(Context context) {
super(context, R.layout.journal_viewer_list_item);
}
private String getLine(String content, String prefix) {
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
public View getView(int position, View v, ViewGroup parent) {
@NonNull
public View getView(int position, View v, @NonNull ViewGroup parent) {
if (v == null)
v = LayoutInflater.from(getContext()).inflate(R.layout.journal_viewer_list_journals_item, parent, false);
v = LayoutInflater.from(getContext()).inflate(R.layout.journal_viewer_list_item, parent, false);
EntryEntity entryEntity = getItem(position);
TextView tv = (TextView) v.findViewById(R.id.title);
tv.setText(String.format(Locale.getDefault(), "%s: %s", entryEntity.getContent().getAction().toString(), entryEntity.getUid()));
// 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);
return v;
}

View File

@ -87,7 +87,7 @@ public class ListJournalsFragment extends ListFragment implements AdapterView.On
if (item.getViewType() == 1) {
CollectionInfo info = ((ListItem) item).info;
getFragmentManager().beginTransaction()
.replace(android.R.id.content, ListEntriesFragment.newInstance(info.url))
.replace(android.R.id.content, ListEntriesFragment.newInstance(info))
.addToBackStack(null)
.commitAllowingStateLoss();
}

View File

@ -7,9 +7,7 @@
<ListView
android:id="@id/android:list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
android:layout_height="match_parent" />
<TextView
android:id="@id/android:empty"

View File

@ -19,6 +19,11 @@
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
tools:text="Title"/>
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Subtitle"/>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/activity_margin">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/display_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:hint="@string/create_calendar_display_name_hint"
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceMedium" />
<View
android:id="@+id/color"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginLeft="16dp"
android:background="@color/orangeA700" />
</LinearLayout>
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/activity_margin"
android:text="@string/change_journal_title"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/stats"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/activity_margin"
android:text="Stats:" />
<LinearLayout
android:id="@+id/list_entries_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical">
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:title="@string/view_collection_edit"
android:onClick="onEditCollection"
app:showAsAction="always"/>
</menu>

View File

@ -187,6 +187,7 @@
<string name="create_collection_display_name">Display name (title) of this collection:</string>
<string name="create_collection_display_name_required">Title is required</string>
<string name="create_collection_description">Description (optional):</string>
<string name="view_collection_edit">Edit</string>
<string name="create_collection_create">Save</string>
<string name="delete_collection">Delete</string>
<string name="delete_collection_confirm_title">Are you sure?</string>