Initial changes for new GUI

pull/2/head
Ricki Hirner 8 years ago
parent 9a8d29e774
commit 515969c4b8

@ -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;

@ -0,0 +1,81 @@
/*
* 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;
}
}
}

@ -0,0 +1,82 @@
/*
* 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;
}
}

@ -0,0 +1,25 @@
/*
* 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);
}
}

@ -0,0 +1,45 @@
/*
* 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);
}
});
}
}

@ -1,134 +0,0 @@
/*
* 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;
}
}
}

@ -1,193 +0,0 @@
/*
* 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;
}
// actions
@SuppressWarnings("Recycle")
void addAccount() {
String accountName = editAccountName.getText().toString();
AccountManager accountManager = AccountManager.get(getActivity());
Account account = new Account(accountName, Constants.ACCOUNT_TYPE);
Bundle userData = AccountSettings.createBundle(serverInfo);
if (accountManager.addAccountExplicitly(account, serverInfo.getPassword(), userData)) {
addSync(account, ContactsContract.AUTHORITY, serverInfo.getAddressBooks(), new AddSyncCallback() {
@Override
public void createLocalCollection(Account account, ServerInfo.ResourceInfo resource) throws ContactsStorageException {
@Cleanup("release") ContentProviderClient provider = getActivity().getContentResolver().acquireContentProviderClient(ContactsContract.AUTHORITY);
if (provider != null) {
LocalAddressBook addressBook = new LocalAddressBook(account, provider);
// set URL
addressBook.setURL(resource.getUrl());
// set Settings
ContentValues settings = new ContentValues(2);
settings.put(ContactsContract.Settings.SHOULD_SYNC, 1);
settings.put(ContactsContract.Settings.UNGROUPED_VISIBLE, 1);
addressBook.updateSettings(settings);
} else
Constants.log.error("Couldn't access Contacts Provider");
}
});
addSync(account, CalendarContract.AUTHORITY, serverInfo.getCalendars(), new AddSyncCallback() {
@Override
public void createLocalCollection(Account account, ServerInfo.ResourceInfo calendar) {
try {
LocalCalendar.create(account, getActivity().getContentResolver(), calendar);
} catch(CalendarStorageException e) {
Constants.log.error("Couldn't create local calendar", e);
}
}
});
addSync(account, TaskProvider.ProviderName.OpenTasks.authority, serverInfo.getTaskLists(), new AddSyncCallback() {
@Override
public void createLocalCollection(Account account, ServerInfo.ResourceInfo todoList) {
try {
LocalTaskList.create(account, getActivity().getContentResolver(), todoList);
} catch (CalendarStorageException e) {
Constants.log.error("Couldn't create local task list", e);
}
}
});
getActivity().finish();
} else
Toast.makeText(getActivity(), "Couldn't create account (account with this name already existing?)", Toast.LENGTH_LONG).show();
}
protected interface AddSyncCallback {
void createLocalCollection(Account account, ServerInfo.ResourceInfo resource) throws ContactsStorageException;
}
protected void addSync(Account account, String authority, ServerInfo.ResourceInfo[] resourceList, AddSyncCallback callback) {
boolean sync = false;
for (ServerInfo.ResourceInfo resource : resourceList)
if (resource.isEnabled()) {
sync = true;
if (callback != null)
try {
callback.createLocalCollection(account, resource);
} catch(ContactsStorageException e) {
Log.e(TAG, "Couldn't add sync collection", e);
Toast.makeText(getActivity(), "Couldn't set up synchronization for " + authority, Toast.LENGTH_LONG).show();
}
}
if (sync) {
ContentResolver.setIsSyncable(account, authority, 1);
ContentResolver.setSyncAutomatically(account, authority, true);
} else
ContentResolver.setIsSyncable(account, authority, 0);
}
// input validation
@Override
public void onPrepareOptionsMenu(Menu menu) {
boolean ok = editAccountName.getText().length() > 0;
MenuItem item = menu.findItem(R.id.add_account);
item.setEnabled(ok);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
getActivity().invalidateOptionsMenu();
}
@Override
public void afterTextChanged(Editable s) {
}
}

@ -1,60 +0,0 @@
/*
* 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.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.resource.ServerInfo;
public class AddAccountActivity extends Activity {
final private static String KEY_SERVER_INFO = "serverInfo";
protected ServerInfo serverInfo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null)
serverInfo = (ServerInfo)savedInstanceState.getSerializable(KEY_SERVER_INFO);
setContentView(R.layout.setup_add_account);
if (savedInstanceState == null) { // first call
getFragmentManager().beginTransaction()
.add(R.id.right_pane, new LoginTypeFragment())
.commit();
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(KEY_SERVER_INFO, serverInfo);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.setup_add_account, menu);
return true;
}
public void showHelp(MenuItem item) {
startActivityForResult(new Intent(Intent.ACTION_VIEW, Constants.webUri.buildUpon().appendEncodedPath("configuration/").build()), 0);
}
}

@ -1,63 +0,0 @@
/*
* 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.app.Fragment;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
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.TextView;
import at.bitfire.davdroid.R;
public class InstallAppsFragment extends Fragment {
// https://code.google.com/p/android/issues/detail?id=25906
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.setup_install_apps, container, false);
setHasOptionsMenu(true);
TextView tvInfo = (TextView)v.findViewById(R.id.setup_install_tasks_app);
tvInfo.setText(Html.fromHtml(getString(R.string.setup_install_tasks_app_html)));
tvInfo.setMovementMethod(LinkMovementMethod.getInstance());
return v;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.only_skip, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.skip:
skip();
break;
default:
return false;
}
return true;
}
protected void skip() {
getFragmentManager().beginTransaction()
.replace(R.id.right_pane, new SelectCollectionsFragment())
.addToBackStack(null)
.commit();
}
}

@ -1,130 +0,0 @@
/*
* 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.app.DialogFragment;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
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.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import java.net.URI;
import java.net.URISyntaxException;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.resource.ServerInfo;
public class LoginEmailFragment extends Fragment implements TextWatcher {
protected EditText editEmail, editPassword;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.setup_login_email, container, false);
editEmail = (EditText)v.findViewById(R.id.email_address);
editEmail.addTextChangedListener(this);
editPassword = (EditText)v.findViewById(R.id.password);
editPassword.addTextChangedListener(this);
setHasOptionsMenu(true);
return v;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.only_next, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.next:
try {
String email = editEmail.getText().toString();
Bundle args = new Bundle();
args.putSerializable(QueryServerDialogFragment.KEY_SERVER_INFO, new ServerInfo(
new URI("mailto:" + email),
email,
editPassword.getText().toString(),
true
));
DialogFragment dialog = new QueryServerDialogFragment();
dialog.setArguments(args);
dialog.show(getFragmentManager(), QueryServerDialogFragment.class.getName());
} catch (URISyntaxException e) {
Constants.log.debug("Invalid email address", e);
}
break;
default:
return false;
}
return true;
}
@Override
public void onResume() {
super.onResume();
// set focus and show soft keyboard
if (editEmail.requestFocus()) {
InputMethodManager imm = (InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(editEmail, InputMethodManager.SHOW_IMPLICIT);
}
}
// input validation
@Override
public void onPrepareOptionsMenu(Menu menu) {
boolean emailOk = false,
passwordOk = editPassword.getText().length() > 0;
String email = editEmail.getText().toString();
try {
URI uri = new URI("mailto:" + email);
if (uri.isOpaque()) {
int pos = email.lastIndexOf("@");
if (pos != -1)
emailOk = !email.substring(pos + 1).isEmpty();
}
} catch (URISyntaxException e) {
// invalid mailto: URI
}
MenuItem item = menu.findItem(R.id.next);
item.setEnabled(emailOk && passwordOk);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
getActivity().invalidateOptionsMenu();
}
@Override
public void afterTextChanged(Editable s) {
}
}

@ -1,57 +0,0 @@
/*
* 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.app.Fragment;
import android.os.Bundle;
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.RadioButton;
import at.bitfire.davdroid.R;
public class LoginTypeFragment extends Fragment {
protected RadioButton btnTypeEmail;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.setup_login_type, container, false);
btnTypeEmail = (RadioButton)v.findViewById(R.id.login_type_email);
setHasOptionsMenu(true);
return v;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.only_next, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.next:
Fragment loginFragment = btnTypeEmail.isChecked() ? new LoginEmailFragment() : new LoginURLFragment();
getFragmentManager().beginTransaction()
.replace(R.id.right_pane, loginFragment)
.addToBackStack(null)
.commitAllowingStateLoss();
return true;
default:
return false;
}
}
}

@ -1,168 +0,0 @@
/*
* 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.app.Activity;
import android.app.DialogFragment;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
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.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import java.net.URI;
import java.net.URISyntaxException;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.resource.ServerInfo;
public class LoginURLFragment extends Fragment implements TextWatcher {
protected Spinner spnrScheme;
protected TextView textHttpWarning;
protected EditText editBaseURI, editUserName, editPassword;
protected CheckBox checkboxPreemptive;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.setup_login_url, container, false);
// protocol selection spinner
textHttpWarning = (TextView)v.findViewById(R.id.http_warning);
spnrScheme = (Spinner)v.findViewById(R.id.login_scheme);
spnrScheme.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String scheme = parent.getAdapter().getItem(position).toString();
textHttpWarning.setVisibility(scheme.equals("https://") ? View.GONE : View.VISIBLE);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
spnrScheme.setSelection(1); // HTTPS
// other input fields
editBaseURI = (EditText)v.findViewById(R.id.login_host_path);
editBaseURI.addTextChangedListener(this);
editUserName = (EditText)v.findViewById(R.id.userName);
editUserName.addTextChangedListener(this);
editPassword = (EditText)v.findViewById(R.id.password);
editPassword.addTextChangedListener(this);
checkboxPreemptive = (CheckBox)v.findViewById(R.id.auth_preemptive);
// hook into action bar
setHasOptionsMenu(true);
return v;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.only_next, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.next:
try {
ServerInfo serverInfo = new ServerInfo(
getBaseURI(),
editUserName.getText().toString(),
editPassword.getText().toString(),
checkboxPreemptive.isChecked()
);
Bundle args = new Bundle();
args.putSerializable(QueryServerDialogFragment.KEY_SERVER_INFO, serverInfo);
DialogFragment dialog = new QueryServerDialogFragment();
dialog.setArguments(args);
dialog.show(getFragmentManager(), null);
} catch (URISyntaxException e) {
Constants.log.debug("Invalid URI", e);
}
break;
default:
return false;
}
return true;
}
@Override
public void onResume() {
super.onResume();
// set focus and show soft keyboard
if (editBaseURI.requestFocus()) {
InputMethodManager imm = (InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(editBaseURI, InputMethodManager.SHOW_IMPLICIT);
}
}
private URI getBaseURI() throws URISyntaxException {
String scheme = spnrScheme.getSelectedItem().toString(),
host_path = editBaseURI.getText().toString();
return new URI(scheme + host_path);
}
// input validation
@Override
public void onPrepareOptionsMenu(Menu menu) {
boolean usernameOk = editUserName.getText().length() > 0,
passwordOk = editPassword.getText().length() > 0,
urlOk = false;
// check host name
try {
if (!TextUtils.isEmpty(getBaseURI().getHost()))
urlOk = true;
} catch (Exception e) {
}
MenuItem item = menu.findItem(R.id.next);
item.setEnabled(usernameOk && passwordOk && urlOk);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Activity activity = getActivity();
if (activity != null)
activity.invalidateOptionsMenu();
}
@Override
public void afterTextChanged(Editable s) {
}
}

@ -1,184 +0,0 @@
/*
* 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.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.LoaderManager.LoaderCallbacks;
import android.app.ProgressDialog;
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.os.Bundle;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.log.StringLogger;
import at.bitfire.davdroid.resource.DavResourceFinder;
import at.bitfire.davdroid.resource.LocalTaskList;
import at.bitfire.davdroid.resource.ServerInfo;
import at.bitfire.davdroid.ui.DebugInfoActivity;
import lombok.Cleanup;
public class QueryServerDialogFragment extends DialogFragment implements LoaderCallbacks<ServerInfo> {
public static final String KEY_SERVER_INFO = "server_info";
public static QueryServerDialogFragment newInstance(ServerInfo serverInfo) {
Bundle args = new Bundle();
args.putSerializable(KEY_SERVER_INFO, serverInfo);
QueryServerDialogFragment fragment = new QueryServerDialogFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
ProgressDialog dialog = new ProgressDialog(getActivity());
dialog.setCanceledOnTouchOutside(false);
setCancelable(false);
dialog.setTitle(R.string.setup_resource_detection);
dialog.setIndeterminate(true);
dialog.setMessage(getString(R.string.setup_querying_server));
return dialog;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Loader<ServerInfo> loader = getLoaderManager().initLoader(0, getArguments(), this);
}
@Override
public Loader<ServerInfo> onCreateLoader(int id, Bundle args) {
return new ServerInfoLoader(getActivity(), args);
}
@Override
@SuppressLint("CommitTransaction")
public void onLoadFinished(Loader<ServerInfo> loader, ServerInfo serverInfo) {
if (serverInfo.isEmpty()) {
// resource detection didn't find anything
getFragmentManager().beginTransaction()
.add(NothingDetectedFragment.newInstance(serverInfo.getLogs()), null)
.commitAllowingStateLoss();
} else {
((AddAccountActivity)getActivity()).serverInfo = serverInfo;
// resource detection brought some results
Fragment nextFragment;
if (serverInfo.getTaskLists().length > 0 && !LocalTaskList.tasksProviderAvailable(getActivity().getContentResolver()))
nextFragment = new InstallAppsFragment();
else
nextFragment = new SelectCollectionsFragment();
getFragmentManager().beginTransaction()
.replace(R.id.right_pane, nextFragment)
.addToBackStack(null)
.commitAllowingStateLoss();
}
getDialog().dismiss();
}
@Override
public void onLoaderReset(Loader<ServerInfo> arg0) {
}
public static class NothingDetectedFragment extends DialogFragment {
private static String KEY_LOGS = "logs";
public static NothingDetectedFragment newInstance(String logs) {
Bundle args = new Bundle();
args.putString(KEY_LOGS, logs);
NothingDetectedFragment fragment = new NothingDetectedFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.setup_resource_detection)
.setIcon(android.R.drawable.ic_dialog_info)
.setMessage(R.string.setup_no_collections_found)
.setNeutralButton(R.string.setup_view_logs, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(getActivity(), DebugInfoActivity.class);
intent.putExtra(DebugInfoActivity.KEY_LOGS, getArguments().getString(KEY_LOGS));
startActivity(intent);
}
})
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// dismiss
}
})
.create();
}
}
static class ServerInfoLoader extends AsyncTaskLoader<ServerInfo> {
private static final String TAG = "davdroid.ServerInfoLoader";
final Bundle args;
final Context context;
public ServerInfoLoader(Context context, Bundle args) {
super(context);
this.context = context;
this.args = args;
}
@Override
protected void onStartLoading() {
forceLoad();
}
@Override
public ServerInfo loadInBackground() {
ServerInfo serverInfo = (ServerInfo)args.getSerializable(KEY_SERVER_INFO);
StringLogger logger = new StringLogger("DavResourceFinder", true);
DavResourceFinder finder = new DavResourceFinder(logger, context, serverInfo);
finder.findResources();
// duplicate logs to ADB
String logs = logger.toString();
try {
@Cleanup BufferedReader logStream = new BufferedReader(new StringReader(logs));
Constants.log.info("Successful resource detection:");
String line;
while ((line = logStream.readLine()) != null)
Constants.log.debug(line);
} catch (IOException e) {
Constants.log.error("Couldn't read resource detection logs", e);
}
serverInfo.setLogs(logger.toString());
return serverInfo;
}
}
}

@ -1,217 +0,0 @@
/*
* 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.annotation.SuppressLint;
import android.content.Context;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckedTextView;
import android.widget.ListAdapter;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.resource.LocalTaskList;
import at.bitfire.davdroid.resource.ServerInfo;
import lombok.Getter;
/**
* Order of display:
*
* number of rows type
* nAddressBookHeadings (0 or 1) heading: "address books"
* nAddressBooks address book info
* nCalendarHeadings (0 or 1) heading: "calendars"
* nCalendars calendar info
* nNotebookHeadings (0 or 1) heading: "notebooks"
* nNotebooks notebook info
* nTaskListHeadings (0 or 1) heading: "task lists"
* nTaskLists task list info
*/
public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter {
final static int
TYPE_ADDRESS_BOOKS_HEADING = 0,
TYPE_ADDRESS_BOOKS_ROW = 1,
TYPE_CALENDARS_HEADING = 2,
TYPE_CALENDARS_ROW = 3,
TYPE_TASK_LISTS_HEADING = 4,
TYPE_TASK_LISTS_ROW = 5;
final protected Context context;
final protected ServerInfo serverInfo;
@Getter protected int
nAddressBooks, nAddressBookHeadings,
nCalendars, nCalendarHeadings,
nTaskLists, nTaskListHeadings;
public SelectCollectionsAdapter(Context context, ServerInfo serverInfo) {
this.context = context;
this.serverInfo = serverInfo;
nAddressBooks = serverInfo.getAddressBooks().length;
nAddressBookHeadings = nAddressBooks == 0 ? 0 : 1;
nCalendars = serverInfo.getCalendars().length;
nCalendarHeadings = nCalendars == 0 ? 0 : 1;
nTaskLists = serverInfo.getTaskLists().length;
nTaskListHeadings = nTaskLists == 0 ? 0 : 1;
}
// item data
@Override
public int getCount() {
return nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings + nTaskLists;
}
@Override
public Object getItem(int position) {
if (position >= nAddressBookHeadings &&
position < (nAddressBookHeadings + nAddressBooks))
return serverInfo.getAddressBooks()[position - nAddressBookHeadings];
else if (position >= (nAddressBookHeadings + nAddressBooks + nCalendarHeadings) &&
(position < (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars)))
return serverInfo.getCalendars()[position - (nAddressBookHeadings + nAddressBooks + nCalendarHeadings)];
else if (position >= (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings) &&
(position < (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings + nTaskLists)))
return serverInfo.getTaskLists()[position - (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings)];
return null;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public long getItemId(int position) {
return position;
}
// item views
@Override
public int getViewTypeCount() {
return TYPE_TASK_LISTS_ROW + 1;
}
@Override
public int getItemViewType(int position) {
if ((nAddressBookHeadings != 0) && (position == 0))
return TYPE_ADDRESS_BOOKS_HEADING;
else if ((nAddressBooks != 0) && (position > 0) && (position < nAddressBookHeadings + nAddressBooks))
return TYPE_ADDRESS_BOOKS_ROW;
else if ((nCalendarHeadings != 0) && (position == nAddressBookHeadings + nAddressBooks))
return TYPE_CALENDARS_HEADING;
else if ((nCalendars != 0) && (position > nAddressBookHeadings + nAddressBooks) && (position < nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars))
return TYPE_CALENDARS_ROW;
else if ((nTaskListHeadings != 0) && (position == nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars))
return TYPE_TASK_LISTS_HEADING;
else if ((nTaskLists != 0) && (position > nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars) && (position < nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings + nTaskLists))
return TYPE_TASK_LISTS_ROW;
else
return IGNORE_ITEM_VIEW_TYPE;
}
@Override
@SuppressLint("InflateParams")
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
int viewType = getItemViewType(position);
// step 1: get view (either by creating or recycling)
if (v == null) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
switch (viewType) {
case TYPE_ADDRESS_BOOKS_HEADING:
v = inflater.inflate(R.layout.setup_address_books_heading, parent, false);
break;
case TYPE_ADDRESS_BOOKS_ROW:
v = inflater.inflate(android.R.layout.simple_list_item_single_choice, null);
v.setPadding(0, 8, 0, 8);
break;
case TYPE_CALENDARS_HEADING:
v = inflater.inflate(R.layout.setup_calendars_heading, parent, false);
break;
case TYPE_TASK_LISTS_HEADING:
v = inflater.inflate(R.layout.setup_task_lists_heading, parent, false);
break;
case TYPE_CALENDARS_ROW:
case TYPE_TASK_LISTS_ROW:
v = inflater.inflate(android.R.layout.simple_list_item_multiple_choice, null);
v.setPadding(0, 8, 0, 8);
}
}
// step 2: fill view with content
switch (viewType) {
case TYPE_ADDRESS_BOOKS_ROW:
setContent((CheckedTextView)v, R.drawable.addressbook, (ServerInfo.ResourceInfo)getItem(position));
break;
case TYPE_CALENDARS_ROW:
setContent((CheckedTextView)v, R.drawable.calendar, (ServerInfo.ResourceInfo)getItem(position));
break;
case TYPE_TASK_LISTS_ROW:
setContent((CheckedTextView)v, R.drawable.tasks, (ServerInfo.ResourceInfo)getItem(position));
}
// disable task list selection if there's no local task provider
if (viewType == TYPE_TASK_LISTS_ROW && !LocalTaskList.tasksProviderAvailable(context.getContentResolver())) {
final CheckedTextView check = (CheckedTextView)v;
check.setEnabled(false);
check.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
check.setChecked(false);
}
});
}
return v;
}
protected void setContent(CheckedTextView view, int collectionIcon, ServerInfo.ResourceInfo info) {
// set layout and icons
view.setCompoundDrawablesWithIntrinsicBounds(collectionIcon, 0, info.isReadOnly() ? R.drawable.ic_read_only : 0, 0);
view.setCompoundDrawablePadding(10);
// set text
String title = "<b>" + info.getTitle() + "</b>";
if (info.isReadOnly())
title = title + " (" + context.getString(R.string.setup_read_only) + ")";
String description = info.getDescription();
if (description == null)
description = info.getUrl();
// FIXME escape HTML
view.setText(Html.fromHtml(title + "<br/>" + description));
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
@Override
public boolean isEnabled(int position) {
int type = getItemViewType(position);
return (type == TYPE_ADDRESS_BOOKS_ROW || type == TYPE_CALENDARS_ROW || type == TYPE_TASK_LISTS_ROW);
}
}

@ -1,125 +0,0 @@
/*
* 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.app.ListFragment;
import android.os.Bundle;
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.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListAdapter;
import android.widget.ListView;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.resource.ServerInfo;
public class SelectCollectionsFragment extends ListFragment {
protected ServerInfo serverInfo;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
serverInfo = ((AddAccountActivity)getActivity()).serverInfo;
View v = super.onCreateView(inflater, container, savedInstanceState);
setHasOptionsMenu(true);
return v;
}
@Override
public void onDestroyView() {
super.onDestroyView();
setListAdapter(null);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final ListView listView = getListView();
listView.setPadding(20, 30, 20, 30);
View header = getActivity().getLayoutInflater().inflate(R.layout.setup_select_collections_header, getListView(), false);
listView.addHeaderView(header, getListView(), false);
final SelectCollectionsAdapter adapter = new SelectCollectionsAdapter(view.getContext(), serverInfo);
setListAdapter(adapter);
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
int itemPosition = position - 1; // one list header view at pos. 0
if (adapter.getItemViewType(itemPosition) == SelectCollectionsAdapter.TYPE_ADDRESS_BOOKS_ROW) {
// unselect all other address books
for (int pos = 1; pos <= adapter.getNAddressBooks(); pos++)
if (pos != itemPosition)
listView.setItemChecked(pos + 1, false);
}
getActivity().invalidateOptionsMenu();
}
});
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.only_next, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.next:
// synchronize only selected collections
for (ServerInfo.ResourceInfo addressBook : serverInfo.getAddressBooks())
addressBook.setEnabled(false);
for (ServerInfo.ResourceInfo calendar : serverInfo.getCalendars())
calendar.setEnabled(false);
for (ServerInfo.ResourceInfo todoList : serverInfo.getTaskLists())
todoList.setEnabled(false);
ListAdapter adapter = getListView().getAdapter();
for (long id : getListView().getCheckedItemIds()) {
int position = (int)id + 1; // +1 because header view is inserted at pos. 0
ServerInfo.ResourceInfo info = (ServerInfo.ResourceInfo)adapter.getItem(position);
info.setEnabled(true);
}
getFragmentManager().beginTransaction()
.replace(R.id.right_pane, new AccountDetailsFragment())
.addToBackStack(null)
.commitAllowingStateLoss();
break;
default:
return false;
}
return true;
}
// input validation
@Override
public void onPrepareOptionsMenu(Menu menu) {
boolean ok = false;
try {
ok = getListView().getCheckedItemCount() > 0;
} catch (IllegalStateException e) {
}
MenuItem item = menu.findItem(R.id.next);
item.setEnabled(ok);
}
}

@ -0,0 +1,13 @@
<?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
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="@android:color/darker_gray" />
<item android:color="@android:color/black" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

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

@ -0,0 +1,13 @@
<!--
~ Copyright © 2013 2016 Ricki Hirner (bitfire web engineering).
~ All rights reserved. This program and the accompanying materials
~ are made available under the terms of the GNU Public License v3.0
~ which accompanies this distribution, and is available at
~ http://www.gnu.org/licenses/gpl.html
-->
<vector android:alpha="0.54" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M11.8,10.9c-2.27,-0.59 -3,-1.2 -3,-2.15 0,-1.09 1.01,-1.85 2.7,-1.85 1.78,0 2.44,0.85 2.5,2.1h2.21c-0.07,-1.72 -1.12,-3.3 -3.21,-3.81V3h-3v2.16c-1.94,0.42 -3.5,1.68 -3.5,3.61 0,2.31 1.91,3.46 4.7,4.13 2.5,0.6 3,1.48 3,2.41 0,0.69 -0.49,1.79 -2.7,1.79 -2.06,0 -2.87,-0.92 -2.98,-2.1h-2.2c0.12,2.19 1.76,3.42 3.68,3.83V21h3v-2.15c1.95,-0.37 3.5,-1.5 3.5,-3.55 0,-2.84 -2.43,-3.81 -4.7,-4.4z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:alpha="0.54" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:alpha="0.54" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,15h-2v-6h2v6zm0,-8h-2V7h2v2z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:alpha="0.54" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:alpha="0.54" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zm0,-8c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:alpha="0.54" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M12,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zm4.31,-0.78l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z"/>
</vector>

@ -0,0 +1,15 @@
<?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
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true"
android:drawable="@drawable/ic_visibility_dark" />
<item android:state_checked="false"
android:drawable="@drawable/ic_visibility_off_dark" />
</selector>

@ -0,0 +1,17 @@
<!--
~ Copyright © 2013 2016 Ricki Hirner (bitfire web engineering).
~ All rights reserved. This program and the accompanying materials
~ are made available under the terms of the GNU Public License v3.0
~ which accompanies this distribution, and is available at
~ http://www.gnu.org/licenses/gpl.html
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="135"
android:centerColor="#4CAF50"
android:endColor="#2E7D32"
android:startColor="#81C784"
android:type="linear"/>
</shape>

@ -0,0 +1,23 @@
<?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
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<fragment
class="at.bitfire.davdroid.ui.AccountListFragment"
android:id="@+id/account_list"
android:layout_width="@dimen/leftcol_width"
android:layout_height="match_parent" />
</LinearLayout>

@ -14,7 +14,7 @@
android:layout_height="match_parent">
<fragment
android:layout_width="@dimen/titles_width"
android:layout_width="@dimen/leftcol_width"
android:layout_height="match_parent"
android:id="@+id/settings_scope"
android:name="at.bitfire.davdroid.ui.settings.SettingsScopeFragment" />

@ -0,0 +1,45 @@
<?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
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@id/android:list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@android:color/transparent" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:src="@drawable/gletschersee"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true" />
<TextView
android:id="@id/android:empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="72dp"
android:layout_marginRight="64dp"
android:gravity="right|bottom"
android:textColor="@color/white"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:text="@string/account_list_empty" />
</RelativeLayout>
</LinearLayout>

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
card_view:contentPadding="16dp"
card_view:cardBackgroundColor="#ececec"
card_view:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/account_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Title"
tools:text="Account Name"/>
</LinearLayout>
</android.support.v7.widget.CardView>
</FrameLayout>

@ -0,0 +1,34 @@
<?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
-->
<android.support.v4.widget.DrawerLayout
android:id="@+id/drawer_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
layout="@layout/app_bar_accounts"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_accounts"
app:menu="@menu/activity_accounts_drawer"/>
</android.support.v4.widget.DrawerLayout>

@ -0,0 +1,125 @@
<?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
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.AppCompatRadioButton
android:id="@+id/login_type_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/login_type_email"
android:checked="true" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login_email_address"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login_password"/>
<at.bitfire.davdroid.ui.EditPassword
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
<android.support.v7.widget.AppCompatRadioButton
android:id="@+id/login_type_url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/login_type_url" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login_base_url"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login_user_name"/>
<EditText
android:layout_width="match_parent"
android:maxWidth="100dp"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login_password"/>
<at.bitfire.davdroid.ui.EditPassword
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.v7.widget.AppCompatCheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login_auth_preemptive"/>
</LinearLayout>
</RadioGroup>
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/stepper_nav_bar">
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:text="Back"
android:visibility="invisible"
style="@style/stepper_nav_button"/>
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:text="Login"
style="@style/stepper_nav_button"/>
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:text="Create account"
android:visibility="gone"
style="@style/stepper_nav_button"/>
</LinearLayout>
</LinearLayout>

@ -0,0 +1,42 @@
<?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
-->
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="at.bitfire.davdroid.ui.AccountsActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_accounts"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_add_light"/>
</android.support.design.widget.CoordinatorLayout>

@ -0,0 +1,23 @@
<?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
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<fragment
class="at.bitfire.davdroid.ui.AccountListFragment"
android:id="@+id/account_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

@ -0,0 +1,29 @@
<?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
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/password"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="textPassword"/>
<android.support.v7.widget.AppCompatCheckBox
android:id="@+id/show_password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:button="@drawable/password_eye_button"
android:checked="false"/>
</LinearLayout>

@ -0,0 +1,42 @@
<?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
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/nav_header_height"
android:background="@drawable/side_nav_bar"
android:gravity="bottom"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:theme="@style/ThemeOverlay.AppCompat.Dark">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:src="@drawable/ic_launcher"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:text="@string/app_name"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CalDAV/CardDAV Sync Adapter"/>
</LinearLayout>

@ -0,0 +1,29 @@
<?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">
<item
android:id="@+id/nav_about"
android:icon="@drawable/ic_info_dark"
android:title="@string/navigation_drawer_about"/>
<item
android:id="@+id/nav_website"
android:icon="@drawable/ic_home_dark"
android:title="@string/navigation_drawer_website"/>
<item
android:id="@+id/nav_donate"
android:icon="@drawable/ic_attach_money_dark"
android:title="@string/navigation_drawer_donate"/>
<item
android:id="@+id/nav_preferences"
android:icon="@drawable/ic_settings_dark"
android:title="@string/navigation_drawer_preferences"/>
</menu>

@ -0,0 +1,18 @@
<!--
~ 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
-->
<resources>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2013 2015 Ricki Hirner (bitfire web engineering).
~ Copyright (c) 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
@ -8,5 +8,14 @@
-->
<resources>
<dimen name="titles_width">300dp</dimen>
<dimen name="leftcol_width">320dp</dimen>
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="fab_margin">16dp</dimen>
<dimen name="nav_header_vertical_spacing">16dp</dimen>
<dimen name="nav_header_height">160dp</dimen>
</resources>

@ -0,0 +1,16 @@
<!--
~ 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
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<item name="ic_menu_camera" type="drawable">@android:drawable/ic_menu_camera</item>
<item name="ic_menu_gallery" type="drawable">@android:drawable/ic_menu_gallery</item>
<item name="ic_menu_slideshow" type="drawable">@android:drawable/ic_menu_slideshow</item>
<item name="ic_menu_manage" type="drawable">@android:drawable/ic_menu_manage</item>
<item name="ic_menu_share" type="drawable">@android:drawable/ic_menu_share</item>
<item name="ic_menu_send" type="drawable">@android:drawable/ic_menu_send</item>
</resources>

@ -17,6 +17,17 @@
<string name="send">Send</string>
<string name="skip">Skip</string>
<!-- AccountsActivity -->
<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string>
<string name="navigation_drawer_subtitle">CalDAV/CardDAV Sync Adapter</string>
<string name="navigation_drawer_about">About / License</string>
<string name="navigation_drawer_donate">Donate</string>
<string name="navigation_drawer_preferences">Preferences</string>
<string name="navigation_drawer_website">Web site</string>
<string name="account_list_empty">Welcome to DAVdroid!\n\nYou can add a CalDAV/CardDAV account now.</string>
<!-- MainActivity -->
<string name="main_manage_accounts">Manage sync accounts</string>
<string name="main_show_debug_info">Show debug info</string>
@ -80,14 +91,15 @@
<string name="donate_later">Maybe later</string>
<!-- AddAccountActivity -->
<string name="login_title">Add account</string>
<string name="login_type_email">Login with email address</string>
<string name="login_type_email_description">Service details will be auto-detected by domain name. Example: myaccount@icloud.com</string>
<string name="login_type_url">Login with URL and user name</string>
<string name="login_type_url_description">Service details will be auto-detected by initial URL and user name. Mostly used for self-hosted services.</string>
<string name="login_email_description">Please enter your email address. Its domain name will be used to auto-detect service settings.</string>
<string name="login_email_address">Email:</string>
<string-array name="login_url_scheme">
<item>http://</item>
<item>https://</item>
@ -177,7 +189,7 @@
<string name="setup_select_address_book">Select up to one address book (tap again to unselect) for synchronization:</string>
<string name="setup_select_calendars">Select calendars for synchronization:</string>
<string name="setup_select_task_lists">Select task lists for synchronization:</string>
<string name="setup_account_details">Account details</string>
<string name="setup_account_name">Account name:</string>
<string name="setup_account_name_hint">My CalDAV/CardDAV Account</string>

@ -1,5 +1,5 @@
<!--
~ Copyright (c) 2013 2015 Ricki Hirner (bitfire web engineering).
~ Copyright (c) 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
@ -8,23 +8,55 @@
<resources>
<!--
Base application theme, dependent on API level. This theme is replaced
by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
-->
<style name="AppBaseTheme" parent="android:Theme.Holo.Light">
<!--
Theme customizations available in newer API levels can go in
res/values-vXX/styles.xml, while customizations related to
backward-compatibility can go here.
-->
<!-- colors -->
<color name="davdroid_green">#58a434</color>
<color name="davdroid_green_dark">#407826</color>
<color name="davdroid_lightblue">#00a8e6</color>
<color name="very_light_grey">#ebebeb</color>
<color name="white">#ffffff</color>
<!-- app theme -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/davdroid_green</item>
<item name="colorPrimaryDark">@color/davdroid_green_dark</item>
<item name="colorAccent">@color/davdroid_lightblue</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
<!-- stepper (wizard) -->
<style name="stepper_nav_bar">
<item name="android:background">@color/very_light_grey</item>
</style>
<style name="stepper_nav_button">
<item name="android:layout_margin">8dp</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:background">?android:attr/selectableItemBackground</item>
<item name="android:paddingTop">8dp</item>
<item name="android:paddingLeft">4dp</item>
<item name="android:paddingRight">4dp</item>
<item name="android:paddingBottom">8dp</item>
<item name="android:textAllCaps">true</item>
<item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
<item name="android:textColor">@color/buttontext</item>
</style>
<!-- text content -->
<style name="TextView.Heading" parent="AppTheme">
<item name="android:padding">5dp</item>
<item name="android:background">#7ca42b</item>

@ -12,7 +12,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.1'
classpath 'com.android.tools.build:gradle:1.5.0+'
}
}

@ -1 +1 @@
Subproject commit 652eac6f3f9fe5adb94e6142d1bad5761b468feb
Subproject commit 781cd6254258a243deea542ca71bd1a86c996ea6
Loading…
Cancel
Save