Commit 35c6ccdf authored by Ricki Hirner's avatar Ricki Hirner

Initial changes for new GUI

parent 06a046f5
......@@ -55,16 +55,20 @@ configurations.all {
}
dependencies {
compile 'dnsjava:dnsjava:2.1.7'
compile 'org.apache.commons:commons-lang3:3.4'
provided 'org.projectlombok:lombok:1.16.6'
compile 'org.slf4j:slf4j-android:1.7.13'
compile project(':dav4android')
compile project(':ical4android')
compile project(':vcard4android')
compile 'com.android.support:appcompat-v7:23.+'
compile 'com.android.support:cardview-v7:23.+'
compile 'com.android.support:design:23.+'
compile project(':MemorizingTrustManager')
androidTestCompile 'com.squareup.okhttp:mockwebserver:2.7.2'
compile 'dnsjava:dnsjava:2.1.7'
compile 'org.apache.commons:commons-lang3:3.4'
compile 'org.slf4j:slf4j-android:1.7.13'
}
......@@ -5,64 +5,75 @@
~ 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
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
package="at.bitfire.davdroid"
android:installLocation="internalOnly">
-->
<manifest package="at.bitfire.davdroid"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="internalOnly">
<!-- normal permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
<!-- legacy permissions -->
<!-- for writing external log files; permission only required for SDK <= 18 because since then,
writing to app-private directory doesn't require extra permissions -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="18" tools:ignore="UnusedAttribute"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18" tools:ignore="UnusedAttribute"/>
<!--
for writing external log files; permission only required for SDK <= 18 because since then,
writing to app-private directory doesn't require extra permissions
-->
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="18"
tools:ignore="UnusedAttribute"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18"
tools:ignore="UnusedAttribute"/>
<!-- other permissions -->
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_CONTACTS" /> <!-- android.permission-group.CONTACTS -->
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALENDAR" /> <!-- android.permission-group.CALENDAR -->
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<!-- android.permission-group.CONTACTS -->
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.READ_CALENDAR"/>
<!-- android.permission-group.CALENDAR -->
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>
<!-- ical4android declares task access permissions -->
<application
android:allowBackup="true" android:fullBackupContent="false" tools:ignore="UnusedAttribute"
android:allowBackup="true"
android:fullBackupContent="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
android:theme="@style/AppTheme"
tools:ignore="UnusedAttribute">
<service
android:name=".syncadapter.AccountAuthenticatorService"
android:exported="false">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
<action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/account_authenticator" />
android:resource="@xml/account_authenticator"/>
</service>
<service
android:name=".syncadapter.ContactsSyncAdapterService"
android:exported="true"
android:process=":sync"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/sync_contacts" />
android:resource="@xml/sync_contacts"/>
<meta-data
android:name="android.provider.CONTACTS_STRUCTURE"
android:resource="@xml/contacts" />
android:resource="@xml/contacts"/>
</service>
<service
android:name=".syncadapter.CalendarsSyncAdapterService"
......@@ -70,11 +81,12 @@
android:process=":sync"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/sync_calendars" />
android:resource="@xml/sync_calendars"/>
</service>
<service
android:name=".syncadapter.TasksSyncAdapterService"
......@@ -82,47 +94,57 @@
android:process=":sync"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/sync_tasks" />
android:resource="@xml/sync_tasks"/>
</service>
<activity
android:name=".ui.MainActivity"
android:label="@string/app_name" >
android:name=".ui.AccountsActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".ui.DebugInfoActivity"
android:label="@string/debug_info_title"
android:exported="true">
android:name=".ui.AddAccountActivity"
android:label="@string/login_title"
android:parentActivityName=".ui.AccountsActivity"
android:noHistory="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
<activity
android:name=".ui.setup.AddAccountActivity"
android:excludeFromRecents="true">
android:name=".ui.DebugInfoActivity"
android:exported="true"
android:label="@string/debug_info_title">
</activity>
<activity
android:name=".ui.settings.SettingsActivity"
android:label="@string/settings_title" >
android:label="@string/settings_title">
<intent-filter>
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:name=".ui.settings.AccountActivity"
android:label="@string/settings_title"
android:parentActivityName=".ui.settings.SettingsActivity" tools:ignore="UnusedAttribute">
android:parentActivityName=".ui.settings.SettingsActivity"
tools:ignore="UnusedAttribute">
</activity>
<!-- MemorizingTrustManager -->
<activity android:name="de.duenndns.ssl.MemorizingActivity"
android:theme="@android:style/Theme.Holo.Light.Dialog.NoActionBar"/>
<activity
android:name="de.duenndns.ssl.MemorizingActivity"
android:theme="@android:style/Theme.Holo.Light.Dialog.NoActionBar"/>
</application>
</manifest>
......@@ -18,7 +18,7 @@ import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import at.bitfire.davdroid.ui.setup.AddAccountActivity;
import at.bitfire.davdroid.ui.AddAccountActivity;
public class AccountAuthenticatorService extends Service {
private static AccountAuthenticator accountAuthenticator;
......
/*
* 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 at.bitfire.davdroid.ui;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.OnAccountsUpdateListener;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
public class AccountListFragment extends ListFragment implements OnAccountsUpdateListener {
protected AccountManager accountManager;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
setListAdapter(new AccountListAdapter(getContext()));
accountManager = AccountManager.get(getContext());
accountManager.addOnAccountsUpdatedListener(this, null, true);
return inflater.inflate(R.layout.account_list, container, false);
}
@Override
public void onDestroyView() {
accountManager.removeOnAccountsUpdatedListener(this);
super.onDestroyView();
}
@Override
public void onAccountsUpdated(Account[] accounts) {
AccountListAdapter adapter = (AccountListAdapter)getListAdapter();
if (adapter != null) {
adapter.clear();
for (Account account : accounts)
if (Constants.ACCOUNT_TYPE.equals(account.type))
adapter.add(account);
}
}
class AccountListAdapter extends ArrayAdapter<Account> {
public AccountListAdapter(Context context) {
super(context, R.layout.account_list_item);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater inflater = LayoutInflater.from(getContext());
v = inflater.inflate(R.layout.account_list_item, parent, false);
}
Account account = getItem(position);
TextView tvName = (TextView)v.findViewById(R.id.account_name);
tvName.setText(account.name);
return v;
}
}
}
/*
* 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 at.bitfire.davdroid.ui;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.view.View;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
public class AccountsActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_accounts);
Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton)findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(AccountsActivity.this, AddAccountActivity.class));
}
});
DrawerLayout drawer = (DrawerLayout)findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.setDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = (NavigationView)findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
}
@Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout)findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START))
drawer.closeDrawer(GravityCompat.START);
else
super.onBackPressed();
}
@Override
public boolean onNavigationItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.nav_about:
break;
case R.id.nav_website:
startActivity(new Intent(Intent.ACTION_VIEW, Constants.webUri));
break;
case R.id.nav_donate:
startActivity(new Intent(Intent.ACTION_VIEW, Constants.webUri.buildUpon().appendEncodedPath("donate/").build()));
break;
case R.id.nav_preferences:
break;
}
DrawerLayout drawer = (DrawerLayout)findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
}
/*
* 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 at.bitfire.davdroid.ui;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import at.bitfire.davdroid.R;
public class AddAccountActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_account);
}
}
/*
* 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 at.bitfire.davdroid.ui;
import android.content.Context;
import android.support.v7.widget.AppCompatCheckBox;
import android.util.AttributeSet;
import android.view.inputmethod.EditorInfo;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.LinearLayout;
import at.bitfire.davdroid.R;
public class EditPassword extends LinearLayout {
public EditPassword(Context context) {
super(context, null);
}
public EditPassword(Context context, AttributeSet attrs) {
super(context, attrs);
inflate(context, R.layout.edit_password, this);
final EditText editPassword = (EditText)findViewById(R.id.password);
AppCompatCheckBox checkShowPassword = (AppCompatCheckBox)findViewById(R.id.show_password);
checkShowPassword.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
int inputType = editPassword.getInputType() & ~EditorInfo.TYPE_MASK_VARIATION;
inputType |= isChecked ? EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD : EditorInfo.TYPE_TEXT_VARIATION_PASSWORD;
editPassword.setInputType(inputType);
}
});
}
}
/*
* Copyright © 2013 – 2015 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 at.bitfire.davdroid.ui;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import at.bitfire.davdroid.BuildConfig;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.ui.settings.SettingsActivity;
import at.bitfire.davdroid.ui.setup.AddAccountActivity;
import lombok.Getter;
public class MainActivity extends Activity {
private static final String libraries =
"· <a href=\"https://commons.apache.org/\">Apache Commons</a> – <a href=\"https://www.apache.org/licenses/\">Apache License, Version 2.0</a><br/>" +
"· <a href=\"http://www.xbill.org/dnsjava/\">dnsjava</a> – <a href=\"http://www.xbill.org/dnsjava/dnsjava-current/LICENSE\">BSD License</a><br/>" +
"· <a href=\"https://github.com/mangstadt/ez-vcard\">ez-vcard</a> – <a href=\"http://opensource.org/licenses/BSD-3-Clause\">New BSD License</a><br/>" +
"· <a href=\"https://github.com/ical4j/ical4j\">iCal4j</a> – <a href=\"https://github.com/ical4j/ical4j/blob/master/LICENSE\">New BSD License</a><br/>" +
"· <a href=\"https://github.com/ge0rg/MemorizingTrustManager\">MemorizingTrustManager</a> – <a href=\"https://raw.githubusercontent.com/ge0rg/MemorizingTrustManager/master/LICENSE.txt\">MIT License</a><br/>" +
"· <a href=\"https://square.github.io/okhttp/\">okhttp</a> – <a href=\"https://square.github.io/okhttp/#license\">Apache License, Version 2.0</a><br/>" +
"· <a href=\"https://projectlombok.org/\">Project Lombok</a> – <a href=\"http://opensource.org/licenses/mit-license.php\">MIT License</a><br/>" +
"· <a href=\"https://commons.apache.org/\">SLF4j</a> (Simple Logging Facade for Java) – <a href=\"http://www.slf4j.org/license.html\">MIT License</a>";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
TextView tv = (TextView)findViewById(R.id.text_store_specific);
final String installedFrom = installedFrom();
if (installedFrom == null || installedFrom.startsWith("org.fdroid")) {
if (savedInstanceState == null)
new DonateDialogFragment().show(getFragmentManager(), "donate");
} else if ("com.android.vending".equals(installedFrom))
setHtmlText(R.id.text_store_specific, R.string.main_play_workaround_html);
setPlainText(R.id.text_welcome, R.string.main_welcome, BuildConfig.VERSION_NAME);
setHtmlText(R.id.text_what_is_davdroid, R.string.main_what_is_davdroid_html);
setHtmlText(R.id.text_how_to_setup, R.string.main_how_to_setup_html);
setHtmlText(R.id.text_support, R.string.main_support_html);
setHtmlText(R.id.text_open_source_disclaimer, R.string.main_open_source_disclaimer_html);
setHtmlText(R.id.text_license, R.string.main_license_html);
setPlainText(R.id.text_libraries_heading, R.string.main_used_libraries_heading);
setHtmlText(R.id.text_libraries_list, libraries);
}
private void setPlainText(int viewId, int stringId, Object... args) {
TextView tv = (TextView)findViewById(viewId);
tv.setVisibility(View.VISIBLE);
tv.setText(getString(stringId, args));
}
private void setHtmlText(int viewId, int stringId, Object... args) {
TextView tv = (TextView)findViewById(viewId);
tv.setVisibility(View.VISIBLE);
tv.setText(trim(Html.fromHtml(getString(stringId, args))));
tv.setMovementMethod(LinkMovementMethod.getInstance());
}
private void setHtmlText(int viewId, String html) {
TextView tv = (TextView)findViewById(viewId);
tv.setVisibility(View.VISIBLE);
tv.setText(trim(Html.fromHtml(html)));
tv.setMovementMethod(LinkMovementMethod.getInstance());
}
private CharSequence trim(CharSequence text) {
while (text.charAt(text.length() - 1) == '\n') {
text = text.subSequence(0, text.length() - 1);
}
return text;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_activity, menu);
return true;
}
public void addAccount(MenuItem item) {
startActivity(new Intent(this, AddAccountActivity.class));
}
public void showDebugInfo(MenuItem item) {
startActivity(new Intent(this, DebugInfoActivity.class));
}
public void showSettings(MenuItem item) {
startActivity(new Intent(this, SettingsActivity.class));
}
public void showSyncSettings(MenuItem item) {
Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
startActivity(intent);
}
public void showWebsite(MenuItem item) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Constants.webUri);
startActivity(intent);
}
private String installedFrom() {
try {
return getPackageManager().getInstallerPackageName("at.bitfire.davdroid");
} catch(IllegalArgumentException e) {
return null;
}
}
}
/*
* Copyright © 2013 – 2015 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 at.bitfire.davdroid.ui.setup;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Fragment;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.os.Bundle;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.resource.LocalAddressBook;
import at.bitfire.davdroid.resource.LocalCalendar;
import at.bitfire.davdroid.resource.LocalTaskList;
import at.bitfire.davdroid.resource.ServerInfo;
import at.bitfire.davdroid.syncadapter.AccountSettings;
import at.bitfire.ical4android.CalendarStorageException;
import at.bitfire.ical4android.TaskProvider;
import at.bitfire.vcard4android.ContactsStorageException;
import lombok.Cleanup;
public class AccountDetailsFragment extends Fragment implements TextWatcher {
public static final String TAG = "davdroid.AccountDetails";
ServerInfo serverInfo;
EditText editAccountName;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.setup_account_details, container, false);
serverInfo = ((AddAccountActivity)getActivity()).serverInfo;
editAccountName = (EditText)v.findViewById(R.id.account_name);
editAccountName.addTextChangedListener(this);
editAccountName.setText(serverInfo.getUserName());
TextView textAccountNameInfo = (TextView)v.findViewById(R.id.account_name_info);
if (!serverInfo.hasEnabledCalendars())
textAccountNameInfo.setVisibility(View.GONE);
setHasOptionsMenu(true);
return v;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.setup_account_details, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.add_account:
addAccount();
break;
default:
return false;
}
return true;