Merge pull request #1 from stefan-niedermann/master

get up to date
This commit is contained in:
HeaDBanGer 2016-01-04 14:23:18 +01:00
commit bc30f1fecb
25 changed files with 362 additions and 300 deletions

View file

@ -1,13 +1,15 @@
# ownCloud Notes # ownCloud Notes
An android client for OwnCloud Notes App. An android client for [ownCloud Notes App](https://github.com/owncloud/notes/).
:construction: **Warning:** This app is beta. This means it is not tested well (only by some VMs and my personal device). :construction: **Warning:** This app is beta. This means it is not tested well (only by some VMs and my personal device).
:arrow_forward: **Access:** Since this app is currently not available in any Appstore, i will provide some versions in [my ownCloud instance](http://owncloud.niedermann.it/index.php/s/BOM5V1VZwscFk1k). Feel free to download and distribute. But *be careful*: This kind of installation does not include any kind of automatic updates, so you will have to check manually for new security and feature updates.
## :rocket: Features ## :rocket: Features
* List, Create, Edit, Share and Delete Notes * List, Create, Edit, Share and Delete Notes
* Share Text and Links as new Note into the App * Share Text and Links as new Note into the App
* Bulk Delete * Bulk Delete
* Render MarkDown (using [AndDown](https://github.com/commonsguy/cwac-anddown)) * Render MarkDown (using [Bypass](https://github.com/Uncodin/bypass))
* English, German and Serbian UI * English, German and Serbian UI
## :checkered_flag: Planned Features ## :checkered_flag: Planned Features
@ -30,8 +32,8 @@ An android client for OwnCloud Notes App.
* Send me a bottle of your favorite beer :beers: :wink: * Send me a bottle of your favorite beer :beers: :wink:
## :link: Requirements ## :link: Requirements
* [OwnCloud](https://github.com/owncloud/) instance running * [ownCloud](https://github.com/owncloud/) instance running
* [OwnCloud Notes](https://github.com/owncloud/notes) app enabled * [ownCloud Notes](https://github.com/owncloud/notes) app enabled
## :notebook: License ## :notebook: License
This Project is licensed under the [GNU GENERAL PUBLIC LICENSE](/LICENSE). This Project is licensed under the [GNU GENERAL PUBLIC LICENSE](/LICENSE).

View file

@ -65,29 +65,23 @@
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/debug" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/debugAndroidTest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.0.1/jars" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.0.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/23.0.1/jars" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/23.0.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/gridlayout-v7/23.0.1/jars" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/gridlayout-v7/23.0.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.0.1/jars" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.0.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.commit451/bypasses/1.0.1/jars" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.commit451/bypasses/1.0.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/tmp" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" /> <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" /> <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content> </content>

View file

@ -8,8 +8,8 @@ android {
applicationId "it.niedermann.owncloud.notes" applicationId "it.niedermann.owncloud.notes"
minSdkVersion 22 minSdkVersion 22
targetSdkVersion 23 targetSdkVersion 23
versionCode 3 versionCode 4
versionName "0.3.0" versionName "0.4.0"
} }
buildTypes { buildTypes {
release { release {

View file

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest
package="it.niedermann.owncloud.notes" package="it.niedermann.owncloud.notes"
android:versionCode="3" xmlns:android="http://schemas.android.com/apk/res/android"
android:versionName="0.3.0"> android:versionCode="4"
android:versionName="0.4.0">
<uses-sdk <uses-sdk
android:minSdkVersion="11" android:minSdkVersion="11"

View file

@ -4,11 +4,12 @@ import android.os.Bundle;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.R;
public class AboutActivity extends AppCompatActivity { public class AboutActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about); setContentView(R.layout.activity_about);
} }
} }

View file

@ -8,7 +8,6 @@ import android.view.MenuItem;
import android.widget.EditText; import android.widget.EditText;
import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.model.Note;
import it.niedermann.owncloud.notes.persistence.NoteSQLiteOpenHelper; import it.niedermann.owncloud.notes.persistence.NoteSQLiteOpenHelper;
public class CreateNoteActivity extends AppCompatActivity { public class CreateNoteActivity extends AppCompatActivity {
@ -49,10 +48,8 @@ public class CreateNoteActivity extends AppCompatActivity {
editTextField.setEnabled(false); editTextField.setEnabled(false);
String content = editTextField.getText().toString(); String content = editTextField.getText().toString();
NoteSQLiteOpenHelper db = new NoteSQLiteOpenHelper(this); NoteSQLiteOpenHelper db = new NoteSQLiteOpenHelper(this);
db.addNoteAndSync(content);
Intent data = new Intent(); Intent data = new Intent();
//FIXME send correct note back to NotesListView data.putExtra(NotesListViewActivity.CREATED_NOTE, db.getNote(db.addNoteAndSync(content)));
data.putExtra(NotesListViewActivity.CREATED_NOTE, new Note(-1, null, "", content));
setResult(RESULT_OK, data); setResult(RESULT_OK, data);
finish(); finish();
return true; return true;

View file

@ -16,81 +16,103 @@ import java.util.concurrent.TimeUnit;
import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.model.Note; import it.niedermann.owncloud.notes.model.Note;
import it.niedermann.owncloud.notes.persistence.NoteSQLiteOpenHelper; import it.niedermann.owncloud.notes.persistence.NoteSQLiteOpenHelper;
import it.niedermann.owncloud.notes.util.ICallback;
public class EditNoteActivity extends AppCompatActivity { public class EditNoteActivity extends AppCompatActivity {
private final long DELAY = 1000; // in ms private final long DELAY = 1000; // in ms
private EditText content = null; private EditText content = null;
private Note note = null; private Note note = null;
private Timer timer = new Timer(); private Timer timer = new Timer();
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_edit); setContentView(R.layout.activity_edit);
note = (Note) getIntent().getSerializableExtra( note = (Note) getIntent().getSerializableExtra(
NoteActivity.EDIT_NOTE); NoteActivity.EDIT_NOTE);
content = (EditText) findViewById(R.id.editContent); content = (EditText) findViewById(R.id.editContent);
content.setEnabled(false); content.setEnabled(false);
content.setText(note.getContent()); content.setText(note.getContent());
content.setEnabled(true); content.setEnabled(true);
content.addTextChangedListener(new TextWatcher() { content.addTextChangedListener(new TextWatcher() {
@Override @Override
public void beforeTextChanged(CharSequence s, int start, int count, public void beforeTextChanged(CharSequence s, int start, int count,
int after) { int after) {
} }
@Override @Override
public void onTextChanged(final CharSequence s, int start, int before, public void onTextChanged(final CharSequence s, int start, int before,
int count) { int count) {
if (timer != null) if (timer != null)
timer.cancel(); timer.cancel();
} }
@Override @Override
public void afterTextChanged(final Editable s) { public void afterTextChanged(final Editable s) {
timer = new Timer(); timer = new Timer();
timer.schedule(new TimerTask() { timer.schedule(new TimerTask() {
@Override @Override
public void run() { public void run() {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
saveData(); saveData();
} }
}); });
} }
}, DELAY); }, DELAY);
} }
}); });
} }
@Override @Override
public void onBackPressed() { public void onBackPressed() {
content.setEnabled(false); content.setEnabled(false);
saveData(); saveData();
Intent data = new Intent(); Intent data = new Intent();
data.setAction(Intent.ACTION_VIEW); data.setAction(Intent.ACTION_VIEW);
data.putExtra(NoteActivity.EDIT_NOTE, note); data.putExtra(NoteActivity.EDIT_NOTE, note);
setResult(RESULT_OK, data); setResult(RESULT_OK, data);
finish(); finish();
} }
private void saveData() { private void saveData() {
ActionBar ab = getSupportActionBar(); ActionBar ab = getSupportActionBar();
if (ab != null) { if (ab != null) {
ab.setSubtitle(getResources().getString(R.string.action_edit_saving)); ab.setSubtitle(getResources().getString(R.string.action_edit_saving));
} }
note.setContent(((EditText) findViewById(R.id.editContent)).getText().toString()); note.setContent(((EditText) findViewById(R.id.editContent)).getText().toString());
NoteSQLiteOpenHelper db = new NoteSQLiteOpenHelper(this); NoteSQLiteOpenHelper db = new NoteSQLiteOpenHelper(this);
db.updateNoteAndSync(note); db.getNoteServerSyncHelper().addCallback(new ICallback() {
if (ab != null) { @Override
getSupportActionBar().setSubtitle(getResources().getString(R.string.action_edit_saved)); public void onFinish() {
} runOnUiThread(new Runnable() {
Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() { @Override
@Override public void run() {
public void run() { getSupportActionBar().setSubtitle(getResources().getString(R.string.action_edit_saved));
getSupportActionBar().setSubtitle(null); Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
} @Override
}, 1, TimeUnit.SECONDS); public void run() {
} runOnUiThread(new Runnable() {
@Override
public void run() {
getSupportActionBar().setSubtitle(null);
}
});
}
}, 1, TimeUnit.SECONDS);
}
});
/* TODO Notify widgets
int widgetIDs[] = AppWidgetManager.getInstance(getApplication()).getAppWidgetIds(new ComponentName(getApplication(), SingleNoteWidget.class));
for (int id : widgetIDs) {
AppWidgetManager.getInstance(getApplication()).notifyAppWidgetViewDataChanged(id, R.layout.widget_single_note);
}*/
}
});
db.updateNoteAndSync(note);
}
} }

View file

@ -69,6 +69,10 @@ public class NoteActivity extends AppCompatActivity implements View.OnClickListe
case R.id.menu_delete: case R.id.menu_delete:
db = new NoteSQLiteOpenHelper(this); db = new NoteSQLiteOpenHelper(this);
db.deleteNoteAndSync(note.getId()); db.deleteNoteAndSync(note.getId());
Intent data = new Intent();
data.putExtra(NotesListViewActivity.SELECTED_NOTE_POSITION,
notePosition);
setResult(RESULT_FIRST_USER, data);
finish(); finish();
return true; return true;
case R.id.menu_share: case R.id.menu_share:
@ -102,11 +106,11 @@ public class NoteActivity extends AppCompatActivity implements View.OnClickListe
Note editedNote = (Note) data.getExtras().getSerializable( Note editedNote = (Note) data.getExtras().getSerializable(
EDIT_NOTE); EDIT_NOTE);
if (editedNote != null) { if (editedNote != null) {
noteContent.setText(editedNote.getSpannableContent()); note = editedNote;
actionBar.setTitle(editedNote.getTitle()); noteContent.setText(note.getSpannableContent());
actionBar.setSubtitle(editedNote.getModified("dd.MM.yyyy HH:mm")); actionBar.setTitle(note.getTitle());
actionBar.setSubtitle(note.getModified("dd.MM.yyyy HH:mm"));
} }
// TODO Fire changed note to noteslistviewactivity
data.putExtra(NotesListViewActivity.SELECTED_NOTE_POSITION, data.putExtra(NotesListViewActivity.SELECTED_NOTE_POSITION,
notePosition); notePosition);
setResult(RESULT_OK, data); setResult(RESULT_OK, data);

View file

@ -7,7 +7,6 @@ import android.preference.PreferenceManager;
import android.support.v4.widget.SwipeRefreshLayout; import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ActionMode; import android.support.v7.view.ActionMode;
import android.util.Log;
import android.util.SparseBooleanArray; import android.util.SparseBooleanArray;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@ -54,7 +53,7 @@ public class NotesListViewActivity extends AppCompatActivity implements
// First Run Wizard // First Run Wizard
SharedPreferences preferences = PreferenceManager SharedPreferences preferences = PreferenceManager
.getDefaultSharedPreferences(getApplicationContext()); .getDefaultSharedPreferences(getApplicationContext());
if(preferences.getBoolean(SettingsActivity.SETTINGS_FIRST_RUN, true)) { if (preferences.getBoolean(SettingsActivity.SETTINGS_FIRST_RUN, true)) {
Intent settingsIntent = new Intent(this, SettingsActivity.class); Intent settingsIntent = new Intent(this, SettingsActivity.class);
startActivityForResult(settingsIntent, server_settings); startActivityForResult(settingsIntent, server_settings);
} }
@ -71,8 +70,6 @@ public class NotesListViewActivity extends AppCompatActivity implements
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override @Override
public void onRefresh() { public void onRefresh() {
Log.d("Swipe", "Refreshing Notes");
db.synchronizeWithServer();
db.getNoteServerSyncHelper().addCallback(new ICallback() { db.getNoteServerSyncHelper().addCallback(new ICallback() {
@Override @Override
public void onFinish() { public void onFinish() {
@ -80,6 +77,7 @@ public class NotesListViewActivity extends AppCompatActivity implements
setListView(db.getNotes()); setListView(db.getNotes());
} }
}); });
db.synchronizeWithServer();
} }
}); });
@ -196,7 +194,6 @@ public class NotesListViewActivity extends AppCompatActivity implements
Item item = adapter.getItem(position); Item item = adapter.getItem(position);
if (!item.isSection()) { if (!item.isSection()) {
listView.setItemChecked(position, !listView.isItemChecked(position)); listView.setItemChecked(position, !listView.isItemChecked(position));
Log.v("Note", "getCheckedItemCount " + listView.getCheckedItemCount());
if (listView.getCheckedItemCount() < 1) { if (listView.getCheckedItemCount() < 1) {
removeSelection(); removeSelection();
Intent intent = new Intent(getApplicationContext(), Intent intent = new Intent(getApplicationContext(),
@ -204,9 +201,6 @@ public class NotesListViewActivity extends AppCompatActivity implements
if (!item.isSection()) { if (!item.isSection()) {
intent.putExtra(SELECTED_NOTE, (Note) item); intent.putExtra(SELECTED_NOTE, (Note) item);
intent.putExtra(SELECTED_NOTE_POSITION, position); intent.putExtra(SELECTED_NOTE_POSITION, position);
Log.v("Note",
"notePosition | NotesListViewActivity wurde abgesendet "
+ position);
startActivityForResult(intent, show_single_note_cmd); startActivityForResult(intent, show_single_note_cmd);
} }
} else { // perform long click if already something is selected } else { // perform long click if already something is selected
@ -267,23 +261,30 @@ public class NotesListViewActivity extends AppCompatActivity implements
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK) {
Note createdNote = (Note) data.getExtras().getSerializable( Note createdNote = (Note) data.getExtras().getSerializable(
CREATED_NOTE); CREATED_NOTE);
adapter.add(createdNote); adapter.insert(createdNote, 0);
} }
} else if (requestCode == NoteActivity.EDIT_NOTE_CMD) { } else if (requestCode == show_single_note_cmd) {
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK || resultCode == RESULT_FIRST_USER) {
Note editedNote = (Note) data.getExtras().getSerializable(
NoteActivity.EDIT_NOTE);
int notePosition = data.getExtras().getInt( int notePosition = data.getExtras().getInt(
SELECTED_NOTE_POSITION); SELECTED_NOTE_POSITION);
adapter.remove(adapter.getItem(notePosition)); adapter.remove(adapter.getItem(notePosition));
adapter.add(editedNote); if (resultCode == RESULT_OK) {
Note editedNote = (Note) data.getExtras().getSerializable(
NoteActivity.EDIT_NOTE);
adapter.insert(editedNote, 0);
}
} }
} else if (requestCode == SettingsActivity.CREDENTIALS_CHANGED) { } else if (requestCode == server_settings) {
// Create new Instance with new URL and credentials
db = new NoteSQLiteOpenHelper(this); db = new NoteSQLiteOpenHelper(this);
db.synchronizeWithServer(); // Needed to instanciate new NotesClient with new URL db.getNoteServerSyncHelper().addCallback(new ICallback() {
@Override
public void onFinish() {
setListView(db.getNotes());
}
});
db.synchronizeWithServer();
} }
//TODO Maybe only if previous activity == settings activity?
setListView(db.getNotes());
} }
/** /**

View file

@ -11,9 +11,11 @@ import android.support.v7.app.AppCompatActivity;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView;
import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.persistence.NoteSQLiteOpenHelper; import it.niedermann.owncloud.notes.persistence.NoteSQLiteOpenHelper;
@ -91,20 +93,18 @@ public class SettingsActivity extends AppCompatActivity {
field_username.setText(preferences.getString(SETTINGS_USERNAME, DEFAULT_SETTINGS)); field_username.setText(preferences.getString(SETTINGS_USERNAME, DEFAULT_SETTINGS));
field_password.setText(preferences.getString(SETTINGS_PASSWORD, DEFAULT_SETTINGS)); field_password.setText(preferences.getString(SETTINGS_PASSWORD, DEFAULT_SETTINGS));
field_password.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
login();
return true;
}
});
btn_submit.setOnClickListener(new View.OnClickListener() { btn_submit.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
String url = field_url.getText().toString(); login();
String username = field_username.getText().toString();
String password = field_password.getText().toString();
if (!url.endsWith("/")) {
url += "/";
}
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "https://" + url;
}
new LoginValidatorAsyncTask().execute(url, username, password);
} }
}); });
} }
@ -119,6 +119,20 @@ public class SettingsActivity extends AppCompatActivity {
} }
} }
private void login() {
String url = field_url.getText().toString();
String username = field_username.getText().toString();
String password = field_password.getText().toString();
if (!url.endsWith("/")) {
url += "/";
}
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "https://" + url;
}
new LoginValidatorAsyncTask().execute(url, username, password);
}
/************************************ Async Tasks ************************************/ /************************************ Async Tasks ************************************/
/** /**
@ -175,7 +189,7 @@ public class SettingsActivity extends AppCompatActivity {
@Override @Override
protected void onPostExecute(Boolean isValidLogin) { protected void onPostExecute(Boolean isValidLogin) {
if(isValidLogin) { if (isValidLogin) {
SharedPreferences.Editor editor = preferences.edit(); SharedPreferences.Editor editor = preferences.edit();
editor.putString(SETTINGS_URL, url); editor.putString(SETTINGS_URL, url);
editor.putString(SETTINGS_USERNAME, username); editor.putString(SETTINGS_USERNAME, username);

View file

@ -5,6 +5,7 @@ import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider; import android.appwidget.AppWidgetProvider;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.util.Log; import android.util.Log;
import android.widget.RemoteViews; import android.widget.RemoteViews;
@ -25,6 +26,11 @@ public class SingleNoteWidget extends AppWidgetProvider {
updateViews.setTextViewText(R.id.single_note_content, note.getSpannableContent()); updateViews.setTextViewText(R.id.single_note_content, note.getSpannableContent());
Intent intent = new Intent(context, NoteActivity.class); Intent intent = new Intent(context, NoteActivity.class);
intent.putExtra(NotesListViewActivity.SELECTED_NOTE, note); intent.putExtra(NotesListViewActivity.SELECTED_NOTE, note);
// http://stackoverflow.com/questions/4011178/multiple-instances-of-widget-only-updating-last-widget
Uri data = Uri.withAppendedPath(
Uri.parse("notes" + "://widget/id/")
, String.valueOf(appWidgetId));
intent.setData(data);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
updateViews.setOnClickPendingIntent(R.id.single_note, pendingIntent); updateViews.setOnClickPendingIntent(R.id.single_note, pendingIntent);
} }

View file

@ -12,56 +12,56 @@ import java.util.List;
import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.R;
public class ItemAdapter extends ArrayAdapter<Item> { public class ItemAdapter extends ArrayAdapter<Item> {
/** /**
* Sections and Note-Items * Sections and Note-Items
*/ */
private static final int count_types = 2; private static final int count_types = 2;
private static final int section_type = 0; private static final int section_type = 0;
private static final int note_type = 1; private static final int note_type = 1;
private List<Item> itemList = null; private List<Item> itemList = null;
public ItemAdapter(Context context, List<Item> itemList) { public ItemAdapter(Context context, List<Item> itemList) {
super(context, android.R.layout.simple_list_item_1, itemList); super(context, android.R.layout.simple_list_item_1, itemList);
this.itemList = itemList; this.itemList = itemList;
} }
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) { public View getView(int position, View convertView, ViewGroup parent) {
Item item = itemList.get(position); Item item = itemList.get(position);
if (item.isSection()) { if (item.isSection()) {
if (convertView == null) { if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext() LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE); .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.fragment_notes_list_section_item, null); convertView = inflater.inflate(R.layout.fragment_notes_list_section_item, null);
} }
SectionItem section = (SectionItem) item; SectionItem section = (SectionItem) item;
TextView sectionTitle = (TextView) convertView.findViewById(R.id.sectionTitle); TextView sectionTitle = (TextView) convertView.findViewById(R.id.sectionTitle);
if (sectionTitle != null) { if (sectionTitle != null) {
sectionTitle.setText(section.geTitle()); sectionTitle.setText(section.geTitle());
} }
} else { } else {
if (convertView == null) { if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext() LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE); .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.fragment_notes_list_note_item, null); convertView = inflater.inflate(R.layout.fragment_notes_list_note_item, null);
} }
Note note = (Note) item; Note note = (Note) item;
((TextView) convertView.findViewById(R.id.noteTitle)).setText(note.getTitle()); ((TextView) convertView.findViewById(R.id.noteTitle)).setText(note.getTitle());
((TextView) convertView.findViewById(R.id.noteExcerpt)).setText(note.getExcerpt()); ((TextView) convertView.findViewById(R.id.noteExcerpt)).setText(note.getExcerpt());
} }
return convertView; return convertView;
} }
/** /**
* @return count_types * @return count_types
*/ */
@Override @Override
public int getViewTypeCount() { public int getViewTypeCount() {
return count_types; return count_types;
} }
@Override @Override
public int getItemViewType(int position) { public int getItemViewType(int position) {
return getItem(position).isSection() ? section_type : note_type; return getItem(position).isSection() ? section_type : note_type;
} }
} }

View file

@ -11,51 +11,49 @@ import it.niedermann.owncloud.notes.util.NoteUtil;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class Note implements Item, Serializable { public class Note implements Item, Serializable {
private long id = 0; private long id = 0;
private String title = ""; private String title = "";
private Calendar modified = null; private Calendar modified = null;
private String content = ""; private String content = "";
private String excerpt = ""; private String excerpt = "";
private String spannableContent = null;
public Note(long id, Calendar modified, String title, String content) { public Note(long id, Calendar modified, String title, String content) {
this.id = id; this.id = id;
if(title != null) if (title != null)
setTitle(title); setTitle(title);
setTitle(title); setTitle(title);
setContent(content); setContent(content);
this.modified = modified; this.modified = modified;
} }
public long getId() { public long getId() {
return id; return id;
} }
public String getTitle() { public String getTitle() {
return title; return title;
} }
public void setTitle(String title) { public void setTitle(String title) {
this.title = NoteUtil.removeMarkDown(title); this.title = NoteUtil.removeMarkDown(title);
} }
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public Calendar getModified() { public Calendar getModified() {
return modified; return modified;
} }
public String getModified(String format) { public String getModified(String format) {
return new SimpleDateFormat(format, Locale.GERMANY) return new SimpleDateFormat(format, Locale.GERMANY)
.format(this.getModified().getTimeInMillis()); .format(this.getModified().getTimeInMillis());
} }
public String getContent() { public String getContent() {
return content; return content;
} }
public void setContent(String content) { public void setContent(String content) {
setExcerpt(content); setExcerpt(content);
this.content = content; this.content = content;
this.spannableContent = null;
} }
public String getExcerpt() { public String getExcerpt() {
@ -66,17 +64,15 @@ public class Note implements Item, Serializable {
excerpt = NoteUtil.generateNoteExcerpt(content); excerpt = NoteUtil.generateNoteExcerpt(content);
} }
public String getSpannableContent() { public CharSequence getSpannableContent() {
if (spannableContent == null && getContent() != null) { // TODO Cache the generated CharSequence not possible because CharSequence does not implement Serializable
spannableContent = NoteUtil.parseMarkDown(getContent()); return NoteUtil.parseMarkDown(getContent());
}
return spannableContent;
} }
@Override @Override
public String toString() { public String toString() {
return "#" + getId() + " " + getTitle() + " (" + getModified(NoteSQLiteOpenHelper.DATE_FORMAT) + ")"; return "#" + getId() + " " + getTitle() + " (" + getModified(NoteSQLiteOpenHelper.DATE_FORMAT) + ")";
} }
@Override @Override
public boolean isSection() { public boolean isSection() {

View file

@ -29,7 +29,7 @@ import it.niedermann.owncloud.notes.util.NotesClient;
public class NoteServerSyncHelper { public class NoteServerSyncHelper {
private NotesClient client = null; private NotesClient client = null;
private NoteSQLiteOpenHelper db = null; private NoteSQLiteOpenHelper db = null;
private final View.OnClickListener goToSettingsListener = new View.OnClickListener() { private final View.OnClickListener goToSettingsListener = new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -51,6 +51,7 @@ public class NoteServerSyncHelper {
for (ICallback callback : callbacks) { for (ICallback callback : callbacks) {
callback.onFinish(); callback.onFinish();
} }
callbacks.clear();
} }
}; };
SharedPreferences preferences = PreferenceManager SharedPreferences preferences = PreferenceManager
@ -64,10 +65,22 @@ public class NoteServerSyncHelper {
client = new NotesClient(url, username, password); client = new NotesClient(url, username, password);
} }
/**
* Adds a callback method to the NoteServerSyncHelper.
* All callbacks will be executed once all synchronize operations are done.
* After execution the callback will be deleted, so it has to be added again if it shall be
* executed the next time all synchronize operations are finished.
*
* @param callback Implementation of ICallback, contains one method that shall be executed.
*/
public void addCallback(ICallback callback) { public void addCallback(ICallback callback) {
callbacks.add(callback); callbacks.add(callback);
} }
/**
* Synchronizes Edited, New and Deleted Notes. After all changed content has been sent to the
* server, it downloads all changes that happened on the server.
*/
public void synchronize() { public void synchronize() {
uploadEditedNotes(); uploadEditedNotes();
uploadNewNotes(); uploadNewNotes();
@ -75,12 +88,14 @@ public class NoteServerSyncHelper {
downloadNotes(); downloadNotes();
} }
/**
* Helper method to check if all synchronize operations are done yet.
*/
private void asyncTaskFinished() { private void asyncTaskFinished() {
operationsFinished++; operationsFinished++;
if(operationsFinished == operationsCount) { if (operationsFinished == operationsCount) {
handler.obtainMessage(1).sendToTarget(); handler.obtainMessage(1).sendToTarget();
} }
} }
public void uploadEditedNotes() { public void uploadEditedNotes() {
@ -128,7 +143,7 @@ public class NoteServerSyncHelper {
@Override @Override
protected void onPostExecute(Object[] params) { protected void onPostExecute(Object[] params) {
if(params != null) { if (params != null) {
Long id = (Long) params[1]; Long id = (Long) params[1];
if (id != null) { if (id != null) {
db.deleteNote(((Long) params[1])); db.deleteNote(((Long) params[1]));
@ -200,7 +215,7 @@ public class NoteServerSyncHelper {
@Override @Override
protected void onPostExecute(List<Note> result) { protected void onPostExecute(List<Note> result) {
// Clear Database only if there was no Server Error // Clear Database only if there was no Server Error
if(!serverError) { if (!serverError) {
db.clearDatabase(); db.clearDatabase();
} }
for (Note note : result) { for (Note note : result) {

View file

@ -12,11 +12,12 @@ public class NoteUtil {
/** /**
* Parses a MarkDown-String and returns a Spannable * Parses a MarkDown-String and returns a Spannable
*
* @param s String - MarkDown * @param s String - MarkDown
* @return Spannable * @return Spannable
*/ */
public static String parseMarkDown(String s) { public static CharSequence parseMarkDown(String s) {
return bypass.markdownToSpannable(s).toString(); return bypass.markdownToSpannable(s);
} }
/** /**

View file

@ -31,27 +31,27 @@ public class NotesClient {
private static final String key_content = "content"; private static final String key_content = "content";
private static final String key_modified = "modified"; private static final String key_modified = "modified";
private static final String application_json = "application/json"; private static final String application_json = "application/json";
private String url = ""; private String url = "";
private String username = ""; private String username = "";
private String password = ""; private String password = "";
public NotesClient(String url, String username, String password) { public NotesClient(String url, String username, String password) {
this.url = url; this.url = url;
this.username = username; this.username = username;
this.password = password; this.password = password;
} }
public List<Note> getNotes() throws JSONException, public List<Note> getNotes() throws JSONException,
IOException { IOException {
List<Note> notesList = new ArrayList<>(); List<Note> notesList = new ArrayList<>();
JSONArray notes = new JSONArray(requestServer("notes", METHOD_GET, null)); JSONArray notes = new JSONArray(requestServer("notes", METHOD_GET, null));
long noteId = 0; long noteId = 0;
String noteTitle = ""; String noteTitle = "";
String noteContent = ""; String noteContent = "";
Calendar noteModified = null; Calendar noteModified = null;
JSONObject currentItem; JSONObject currentItem;
for (int i = 0; i < notes.length(); i++) { for (int i = 0; i < notes.length(); i++) {
currentItem = notes.getJSONObject(i); currentItem = notes.getJSONObject(i);
if (!currentItem.isNull(key_id)) { if (!currentItem.isNull(key_id)) {
noteId = currentItem.getLong(key_id); noteId = currentItem.getLong(key_id);
@ -64,31 +64,32 @@ public class NotesClient {
} }
if (!currentItem.isNull(key_modified)) { if (!currentItem.isNull(key_modified)) {
noteModified = GregorianCalendar.getInstance(); noteModified = GregorianCalendar.getInstance();
noteModified noteModified
.setTimeInMillis(currentItem.getLong(key_modified) * 1000); .setTimeInMillis(currentItem.getLong(key_modified) * 1000);
} }
notesList notesList
.add(new Note(noteId, noteModified, noteTitle, noteContent)); .add(new Note(noteId, noteModified, noteTitle, noteContent));
} }
return notesList; return notesList;
} }
/** /**
* Fetches a Note by ID from Server * Fetches a Note by ID from Server
* TODO Maybe fetch only id, title and modified from server until a note has been opened? * TODO Maybe fetch only id, title and modified from server until a note has been opened?
*
* @param id long - ID of the wanted note * @param id long - ID of the wanted note
* @return Requested Note * @return Requested Note
* @throws JSONException * @throws JSONException
* @throws IOException * @throws IOException
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public Note getNoteById(long id) throws public Note getNoteById(long id) throws
JSONException, IOException { JSONException, IOException {
long noteId = 0; long noteId = 0;
String noteTitle = ""; String noteTitle = "";
String noteContent = ""; String noteContent = "";
Calendar noteModified = null; Calendar noteModified = null;
JSONObject currentItem = new JSONObject( JSONObject currentItem = new JSONObject(
requestServer("notes/" + id, METHOD_GET, null)); requestServer("notes/" + id, METHOD_GET, null));
if (!currentItem.isNull(key_id)) { if (!currentItem.isNull(key_id)) {
@ -102,27 +103,28 @@ public class NotesClient {
} }
if (!currentItem.isNull(key_modified)) { if (!currentItem.isNull(key_modified)) {
noteModified = GregorianCalendar.getInstance(); noteModified = GregorianCalendar.getInstance();
noteModified noteModified
.setTimeInMillis(currentItem.getLong(key_modified) * 1000); .setTimeInMillis(currentItem.getLong(key_modified) * 1000);
} }
return new Note(noteId, noteModified, noteTitle, noteContent); return new Note(noteId, noteModified, noteTitle, noteContent);
} }
/** /**
* Creates a Note on the Server * Creates a Note on the Server
*
* @param content String - Content of the new Note * @param content String - Content of the new Note
* @return Created Note including generated Title, ID and lastModified-Date * @return Created Note including generated Title, ID and lastModified-Date
* @throws JSONException * @throws JSONException
* @throws IOException * @throws IOException
*/ */
public Note createNote(String content) throws public Note createNote(String content) throws
JSONException, IOException { JSONException, IOException {
long noteId = 0; long noteId = 0;
String noteTitle = ""; String noteTitle = "";
String noteContent = ""; String noteContent = "";
Calendar noteModified = null; Calendar noteModified = null;
JSONObject paramObject = new JSONObject(); JSONObject paramObject = new JSONObject();
paramObject.accumulate(key_content, content); paramObject.accumulate(key_content, content);
JSONObject currentItem = new JSONObject(requestServer("notes", METHOD_POST, JSONObject currentItem = new JSONObject(requestServer("notes", METHOD_POST,
paramObject)); paramObject));
@ -138,18 +140,18 @@ public class NotesClient {
} }
if (!currentItem.isNull(key_modified)) { if (!currentItem.isNull(key_modified)) {
noteModified = GregorianCalendar.getInstance(); noteModified = GregorianCalendar.getInstance();
noteModified noteModified
.setTimeInMillis(currentItem.getLong(key_modified) * 1000); .setTimeInMillis(currentItem.getLong(key_modified) * 1000);
} }
return new Note(noteId, noteModified, noteTitle, noteContent); return new Note(noteId, noteModified, noteTitle, noteContent);
} }
public Note editNote(long noteId, String content) public Note editNote(long noteId, String content)
throws JSONException, IOException { throws JSONException, IOException {
String noteTitle = ""; String noteTitle = "";
Calendar noteModified = null; Calendar noteModified = null;
JSONObject paramObject = new JSONObject(); JSONObject paramObject = new JSONObject();
paramObject.accumulate(key_content, content); paramObject.accumulate(key_content, content);
JSONObject currentItem = new JSONObject(requestServer( JSONObject currentItem = new JSONObject(requestServer(
"notes/" + noteId, METHOD_PUT, paramObject)); "notes/" + noteId, METHOD_PUT, paramObject));
@ -159,41 +161,41 @@ public class NotesClient {
} }
if (!currentItem.isNull(key_modified)) { if (!currentItem.isNull(key_modified)) {
noteModified = GregorianCalendar.getInstance(); noteModified = GregorianCalendar.getInstance();
noteModified noteModified
.setTimeInMillis(currentItem.getLong(key_modified) * 1000); .setTimeInMillis(currentItem.getLong(key_modified) * 1000);
} }
return new Note(noteId, noteModified, noteTitle, content); return new Note(noteId, noteModified, noteTitle, content);
} }
public void deleteNote(long noteId) throws public void deleteNote(long noteId) throws
IOException { IOException {
this.requestServer("notes/" + noteId, METHOD_DELETE, null); this.requestServer("notes/" + noteId, METHOD_DELETE, null);
} }
/** /**
* Request-Method for POST, PUT with or without JSON-Object-Parameter * Request-Method for POST, PUT with or without JSON-Object-Parameter
* *
* @param target Filepath to the wanted function * @param target Filepath to the wanted function
* @param method GET, POST, DELETE or PUT * @param method GET, POST, DELETE or PUT
* @param params JSON Object which shall be transferred to the server. * @param params JSON Object which shall be transferred to the server.
* @return Body of answer * @return Body of answer
* @throws MalformedURLException * @throws MalformedURLException
* @throws IOException * @throws IOException
*/ */
private String requestServer(String target, String method, JSONObject params) private String requestServer(String target, String method, JSONObject params)
throws IOException { throws IOException {
StringBuffer result = new StringBuffer(); StringBuffer result = new StringBuffer();
String targetURL = url + "index.php/apps/notes/api/v0.2/" + target; String targetURL = url + "index.php/apps/notes/api/v0.2/" + target;
HttpURLConnection con = (HttpURLConnection) new URL(targetURL) HttpURLConnection con = (HttpURLConnection) new URL(targetURL)
.openConnection(); .openConnection();
con.setRequestMethod(method); con.setRequestMethod(method);
con.setRequestProperty( con.setRequestProperty(
"Authorization", "Authorization",
"Basic " "Basic "
+ new String(Base64.encode((username + ":" + new String(Base64.encode((username + ":"
+ password).getBytes(), Base64.NO_WRAP))); + password).getBytes(), Base64.NO_WRAP)));
con.setConnectTimeout(10 * 1000); // 10 seconds con.setConnectTimeout(10 * 1000); // 10 seconds
if (params != null) { if (params != null) {
con.setFixedLengthStreamingMode(params.toString().getBytes().length); con.setFixedLengthStreamingMode(params.toString().getBytes().length);
con.setRequestProperty("Content-Type", application_json); con.setRequestProperty("Content-Type", application_json);
con.setDoOutput(true); con.setDoOutput(true);
@ -201,10 +203,10 @@ public class NotesClient {
os.write(params.toString().getBytes()); os.write(params.toString().getBytes());
os.flush(); os.flush();
os.close(); os.close();
} }
BufferedReader rd = new BufferedReader(new InputStreamReader(con.getInputStream())); BufferedReader rd = new BufferedReader(new InputStreamReader(con.getInputStream()));
String line; String line;
while ((line = rd.readLine()) != null) { while ((line = rd.readLine()) != null) {
result.append(line); result.append(line);
} }
return result.toString(); return result.toString();

View file

@ -29,8 +29,7 @@ public class NotesClientUtil {
} }
/** /**
* * @param url String
* @param url String
* @param username String * @param username String
* @param password String * @param password String
* @return Username and Password are a valid Login-Combination for the given URL. * @return Username and Password are a valid Login-Combination for the given URL.

View file

@ -1,22 +1,24 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout
android:id="@+id/editContentContainer"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
tools:context="it.niedermann.owncloud.notes.android.activity.CreateNoteActivity" tools:context="it.niedermann.owncloud.notes.android.activity.CreateNoteActivity">
android:id="@+id/editContentContainer" >
<ScrollView <ScrollView
android:id="@+id/scrollView2"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:id="@+id/scrollView2" >
<EditText <EditText
android:id="@+id/editContent" android:id="@+id/editContent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ems="10" android:ems="10"
android:inputType="textMultiLine" /> android:inputType="textMultiLine"
android:padding="16dp"/>
</ScrollView> </ScrollView>
</LinearLayout> </LinearLayout>

View file

@ -41,7 +41,8 @@
android:id="@+id/settings_username" android:id="@+id/settings_username"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/settings_username" /> android:hint="@string/settings_username"
android:inputType="text"/>
</android.support.design.widget.TextInputLayout> </android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout <android.support.design.widget.TextInputLayout

View file

@ -13,7 +13,7 @@
android:id="@+id/single_note_content" android:id="@+id/single_note_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="10dp" android:padding="16dp"
android:textAppearance="?android:attr/textAppearanceMedium" android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/fg_default" /> android:textColor="@color/fg_default" />
</ScrollView> </ScrollView>

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout
android:id="@+id/sectionItem" android:id="@+id/sectionItem"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight" android:layout_height="?android:attr/listPreferredItemHeight"
android:background="@color/bg_highlighted" android:background="@color/bg_highlighted"
@ -17,6 +18,8 @@
android:layout_alignWithParentIfMissing="true" android:layout_alignWithParentIfMissing="true"
android:ellipsize="end" android:ellipsize="end"
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:textColor="@color/fg_default_selection" android:textColor="@color/fg_default_selection"
android:textSize="12sp" /> android:textSize="12sp" />

View file

@ -32,7 +32,7 @@
<!-- About --> <!-- About -->
<string name="about_version_title">Version</string> <string name="about_version_title">Version</string>
<string name="about_version">Sie benutzen aktuell <strong>v0.3.0</strong></string> <string name="about_version">Sie benutzen aktuell <strong>v0.4.0</strong></string>
<string name="about_author_title">Autor</string> <string name="about_author_title">Autor</string>
<string name="about_author">Diese Android-App wird entwickelt von <a href="http://www.niedermann.it/">Niedermann IT-Dienstleistungen</a></string> <string name="about_author">Diese Android-App wird entwickelt von <a href="http://www.niedermann.it/">Niedermann IT-Dienstleistungen</a></string>
<string name="about_app_icon_disclaimer_title">App-Icon</string> <string name="about_app_icon_disclaimer_title">App-Icon</string>

View file

@ -21,7 +21,7 @@
<!-- About --> <!-- About -->
<string name="about_version_title">Издање</string> <string name="about_version_title">Издање</string>
<string name="about_version">Текуће издање је <strong>0.3.0</strong></string> <string name="about_version">Текуће издање је <strong>0.4.0</strong></string>
<string name="about_author_title">Аутор</string> <string name="about_author_title">Аутор</string>
<string name="about_author">Ову апликацију за Андроид је направио и презентовао <a href="http://www.niedermann.it/">Niedermann IT-Dienstleistungen</a></string> <string name="about_author">Ову апликацију за Андроид је направио и презентовао <a href="http://www.niedermann.it/">Niedermann IT-Dienstleistungen</a></string>
<string name="about_app_icon_disclaimer_title">Икона апликације</string> <string name="about_app_icon_disclaimer_title">Икона апликације</string>

View file

@ -32,7 +32,7 @@
<!-- About --> <!-- About -->
<string name="about_version_title">Version</string> <string name="about_version_title">Version</string>
<string name="about_version">You are currently using <strong>v0.3.0</strong></string> <string name="about_version">You are currently using <strong>v0.4.0</strong></string>
<string name="about_author_title">Author</string> <string name="about_author_title">Author</string>
<string name="about_author">This Android-Application is developed and presented by <a href="http://www.niedermann.it/">Niedermann IT-Dienstleistungen</a></string> <string name="about_author">This Android-Application is developed and presented by <a href="http://www.niedermann.it/">Niedermann IT-Dienstleistungen</a></string>
<string name="about_app_icon_disclaimer_title">App-Icon</string> <string name="about_app_icon_disclaimer_title">App-Icon</string>

View file

@ -9,6 +9,7 @@
<!-- Snackbar Action Link --> <!-- Snackbar Action Link -->
<item name="colorAccent">@color/primary</item> <item name="colorAccent">@color/primary</item>
<item name="android:buttonStyle">@style/ocbutton</item> <item name="android:buttonStyle">@style/ocbutton</item>
<item name="android:windowBackground">@color/bg_normal</item>
</style> </style>
<style name="fab"> <style name="fab">
<item name="android:layout_margin">16dp</item> <item name="android:layout_margin">16dp</item>