diff --git a/build.gradle b/build.gradle
index f2315f84..f9bc4af7 100755
--- a/build.gradle
+++ b/build.gradle
@@ -35,6 +35,7 @@ dependencies {
compile 'com.google.code.gson:gson:2.1'
compile 'net.sf.opencsv:opencsv:2.3'
compile 'com.google.code.findbugs:jsr305:1.3.9'
+ compile 'com.android.support:recyclerview-v7:23.0.1'
}
android {
diff --git a/res/layout/login.xml b/res/layout/login.xml
index 11976e09..8f8619ed 100644
--- a/res/layout/login.xml
+++ b/res/layout/login.xml
@@ -3,62 +3,52 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/andBackground"
- android:orientation="vertical" >
+ android:orientation="vertical">
-
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingBottom="80dp">
-
-
-
-
-
+ android:layout_marginLeft="5dp"
+ android:layout_marginRight="5dp"
+ android:layout_marginTop="10dp"
+ android:paddingLeft="5dp"
+ android:paddingRight="5dp"
+ android:text="@string/account_required"
+ android:textStyle="bold" />
-
+
-
+
-
-
-
-
+
+
+ android:padding="5dp">
diff --git a/src/com/github/andlyticsproject/AccountListAdapter.java b/src/com/github/andlyticsproject/AccountListAdapter.java
new file mode 100644
index 00000000..fa81a136
--- /dev/null
+++ b/src/com/github/andlyticsproject/AccountListAdapter.java
@@ -0,0 +1,63 @@
+package com.github.andlyticsproject;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import com.github.andlyticsproject.model.DeveloperAccount;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AccountListAdapter extends RecyclerView.Adapter {
+ private final AccountSelectedListener listener;
+ private List developerAccounts = new ArrayList<>();
+
+ public class ViewHolder extends RecyclerView.ViewHolder {
+ public TextView emailDeveloper;
+ public CheckBox checkBox;
+
+ public ViewHolder(View itemView) {
+ super(itemView);
+
+ emailDeveloper = (TextView) itemView.findViewById(R.id.login_list_item_text);
+ checkBox = (CheckBox) itemView.findViewById(R.id.login_list_item_enabled);
+ }
+ }
+
+ public AccountListAdapter(List developerAccounts, AccountSelectedListener listener) {
+ this.listener = listener;
+ this.developerAccounts.addAll(developerAccounts);
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.login_list_item, parent, false);
+
+ ViewHolder viewHolder = new ViewHolder(v);
+
+ return viewHolder;
+ }
+
+ @Override
+ public void onBindViewHolder(final ViewHolder holder, final int position) {
+ DeveloperAccount developerAccount = developerAccounts.get(position);
+ holder.emailDeveloper.setText(developerAccount.getName());
+ holder.checkBox.setChecked(!developerAccount.isHidden());
+ holder.checkBox.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ listener.accountSelected(holder.checkBox.isChecked(), developerAccounts.get(position));
+ }
+ });
+
+ }
+
+ @Override
+ public int getItemCount() {
+ return developerAccounts.size();
+ }
+}
diff --git a/src/com/github/andlyticsproject/AccountSelectedListener.java b/src/com/github/andlyticsproject/AccountSelectedListener.java
new file mode 100644
index 00000000..b6868563
--- /dev/null
+++ b/src/com/github/andlyticsproject/AccountSelectedListener.java
@@ -0,0 +1,8 @@
+package com.github.andlyticsproject;
+
+import com.github.andlyticsproject.model.DeveloperAccount;
+
+
+public interface AccountSelectedListener {
+ void accountSelected(boolean checked, DeveloperAccount account);
+}
diff --git a/src/com/github/andlyticsproject/LoginActivity.java b/src/com/github/andlyticsproject/LoginActivity.java
index a3af68ba..4871e475 100644
--- a/src/com/github/andlyticsproject/LoginActivity.java
+++ b/src/com/github/andlyticsproject/LoginActivity.java
@@ -10,17 +10,14 @@
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.LinearLayout;
-import android.widget.TextView;
import com.github.andlyticsproject.model.DeveloperAccount;
import com.github.andlyticsproject.sync.AutosyncHandler;
@@ -38,276 +35,268 @@
* or
* Main -> LoginActivity -> Main
*/
-public class LoginActivity extends Activity {
-
- private static final String TAG = LoginActivity.class.getSimpleName();
-
- public static final String EXTRA_MANAGE_ACCOUNTS_MODE = "com.github.andlyticsproject.manageAccounts";
-
- public static final String AUTH_TOKEN_TYPE_ANDROID_DEVELOPER = "androiddeveloper";
-
- protected static final int CREATE_ACCOUNT_REQUEST = 1;
-
- private List developerAccounts;
-
- private boolean manageAccountsMode = false;
- private boolean blockGoingBack = false;
- private DeveloperAccount selectedAccount = null;
- private View okButton;
- private LinearLayout accountList;
-
- private AccountManager accountManager;
- private DeveloperAccountManager developerAccountManager;
- private AutosyncHandler syncHandler;
-
- // TODO Clean this code and res/layout/login.xml up e.g. using a ListView
- // instead of a LinearLayout
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-
- accountManager = AccountManager.get(this);
- developerAccountManager = DeveloperAccountManager.getInstance(getApplicationContext());
- syncHandler = new AutosyncHandler();
-
- // When called from accounts action item in Main, this flag is passed to
- // indicate
- // that LoginActivity should not auto login as we are managing the
- // accounts,
- // rather than performing the initial login
- Bundle extras = getIntent().getExtras();
- if (extras != null) {
- manageAccountsMode = extras.getBoolean(LoginActivity.EXTRA_MANAGE_ACCOUNTS_MODE);
- }
-
- if (manageAccountsMode) {
- getActionBar().setTitle(R.string.manage_accounts);
- }
-
- selectedAccount = developerAccountManager.getSelectedDeveloperAccount();
-
- setContentView(R.layout.login);
- setProgressBarIndeterminateVisibility(false);
- accountList = (LinearLayout) findViewById(R.id.login_input);
-
- okButton = findViewById(R.id.login_ok_button);
- okButton.setClickable(true);
- okButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- new AsyncTask() {
-
- @Override
- protected void onPreExecute() {
- setProgressBarIndeterminateVisibility(true);
- okButton.setEnabled(false);
- }
-
- @Override
- protected Void doInBackground(Void... args) {
- saveDeveloperAccounts();
-
- return null;
- }
-
- @Override
- protected void onPostExecute(Void arg) {
- setProgressBarIndeterminateVisibility(false);
- okButton.setEnabled(true);
-
- if (selectedAccount != null) {
- redirectToMain(selectedAccount.getName(),
- selectedAccount.getDeveloperId());
- } else {
- // Go to the first non hidden account
- for (DeveloperAccount account : developerAccounts) {
- if (account.isVisible()) {
- redirectToMain(account.getName(), account.getDeveloperId());
- break;
- }
- }
- }
- }
- }.execute();
- }
- });
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- boolean skipAutologin = Preferences.getSkipAutologin(this);
-
- if (!manageAccountsMode & !skipAutologin & selectedAccount != null) {
- redirectToMain(selectedAccount.getName(), selectedAccount.getDeveloperId());
- } else {
- showAccountList();
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.login_menu, menu);
- return true;
- }
-
- /**
- * Called if item in option menu is selected.
- *
- * @param item
- * The chosen menu item
- * @return boolean true/false
- */
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.itemLoginmenuAdd:
- addNewGoogleAccount();
- break;
- case android.R.id.home:
- if (!blockGoingBack) {
- setResult(RESULT_OK);
- finish();
- }
- break;
- default:
- return false;
- }
- return true;
- }
-
- @Override
- public void onBackPressed() {
- setResult(blockGoingBack ? RESULT_CANCELED : RESULT_OK);
- super.onBackPressed();
- }
-
- protected void showAccountList() {
- Account[] googleAccounts = accountManager.getAccountsByType(AutosyncHandler.ACCOUNT_TYPE_GOOGLE);
- List dbAccounts = developerAccountManager.getAllDeveloperAccounts();
- developerAccounts = new ArrayList();
-
- accountList.removeAllViews();
- for (int i = 0; i < googleAccounts.length; i++) {
- DeveloperAccount developerAccount = DeveloperAccount
- .createHidden(googleAccounts[i].name);
- int idx = dbAccounts.indexOf(developerAccount);
- // use persistent object if exists
- if (idx != -1) {
- developerAccount = dbAccounts.get(idx);
- }
- developerAccounts.add(developerAccount);
-
- // Setup auto sync
- // only do this when managing accounts, otherwise sync may start
- // in the background before accounts are actually configured
- if (manageAccountsMode) {
- // Ensure it matches the sync period (excluding disabled state)
- syncHandler.setAutosyncPeriod(googleAccounts[i].name,
- Preferences.getLastNonZeroAutosyncPeriod(this));
- // Now make it match the master sync (including disabled state)
- syncHandler.setAutosyncPeriod(googleAccounts[i].name,
- Preferences.getAutosyncPeriod(this));
- }
-
- View accountItem = getLayoutInflater().inflate(R.layout.login_list_item, null);
- TextView accountName = (TextView) accountItem.findViewById(R.id.login_list_item_text);
- accountName.setText(googleAccounts[i].name);
- accountItem.setTag(developerAccount);
- CheckBox enabled = (CheckBox) accountItem.findViewById(R.id.login_list_item_enabled);
- enabled.setChecked(!developerAccount.isHidden());
- enabled.setOnCheckedChangeListener(new OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- DeveloperAccount account = (DeveloperAccount) ((View) buttonView.getParent())
- .getTag();
- if (isChecked) {
- account.activate();
- } else {
- account.hide();
- }
-
- if (manageAccountsMode && account.equals(selectedAccount)) {
- // If they remove the current account, then stop them
- // going back
- blockGoingBack = account.isHidden();
- }
-
- okButton.setEnabled(isAtLeastOneAccountEnabled());
- }
- });
- accountList.addView(accountItem);
- }
-
- // Update ok button
- okButton.setEnabled(isAtLeastOneAccountEnabled());
- }
-
- private void saveDeveloperAccounts() {
- for (DeveloperAccount account : developerAccounts) {
- if (account.isHidden()) {
- // They are removing the account from Andlytics, disable
- // syncing
- syncHandler.setAutosyncEnabled(account.getName(), false);
- } else {
- // Make it match the master sync period (including
- // disabled state)
- syncHandler.setAutosyncPeriod(account.getName(),
- Preferences.getAutosyncPeriod(LoginActivity.this));
- }
- developerAccountManager.addOrUpdateDeveloperAccount(account);
- }
- }
-
- private boolean isAtLeastOneAccountEnabled() {
- for (DeveloperAccount acc : developerAccounts) {
- if (acc.isVisible()) {
- return true;
- }
- }
-
- return false;
- }
-
- private void addNewGoogleAccount() {
- AccountManagerCallback callback = new AccountManagerCallback() {
- public void run(AccountManagerFuture future) {
- try {
- Bundle bundle = future.getResult();
- bundle.keySet();
- Log.d(TAG, "account added: " + bundle);
-
- showAccountList();
-
- } catch (OperationCanceledException e) {
- Log.d(TAG, "addAccount was canceled");
- } catch (IOException e) {
- Log.d(TAG, "addAccount failed: " + e);
- } catch (AuthenticatorException e) {
- Log.d(TAG, "addAccount failed: " + e);
- }
- // gotAccount(false);
- }
- };
-
- // TODO request a weblogin: token here, so we have it cached?
- accountManager.addAccount(AutosyncHandler.ACCOUNT_TYPE_GOOGLE,
- LoginActivity.AUTH_TOKEN_TYPE_ANDROID_DEVELOPER, null, null /* options */,
- LoginActivity.this, callback, null /* handler */);
- }
-
- private void redirectToMain(String selectedAccount, String developerId) {
- Preferences.saveSkipAutoLogin(this, false);
- Intent intent = new Intent(LoginActivity.this, Main.class);
- intent.putExtra(BaseActivity.EXTRA_AUTH_ACCOUNT_NAME, selectedAccount);
- intent.putExtra(BaseActivity.EXTRA_DEVELOPER_ID, developerId);
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- overridePendingTransition(R.anim.activity_fade_in, R.anim.activity_fade_out);
- finish();
- }
-
+public class LoginActivity extends Activity implements AccountSelectedListener {
+
+ private static final String TAG = LoginActivity.class.getSimpleName();
+
+ public static final String EXTRA_MANAGE_ACCOUNTS_MODE = "com.github.andlyticsproject.manageAccounts";
+
+ public static final String AUTH_TOKEN_TYPE_ANDROID_DEVELOPER = "androiddeveloper";
+
+ protected static final int CREATE_ACCOUNT_REQUEST = 1;
+
+ private List developerAccounts;
+
+ private boolean manageAccountsMode = false;
+ private boolean blockGoingBack = false;
+ private DeveloperAccount selectedAccount = null;
+ private View okButton;
+ private RecyclerView accountList;
+
+ private AccountManager accountManager;
+ private DeveloperAccountManager developerAccountManager;
+ private AutosyncHandler syncHandler;
+
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+
+ accountManager = AccountManager.get(this);
+ developerAccountManager = DeveloperAccountManager.getInstance(getApplicationContext());
+ syncHandler = new AutosyncHandler();
+
+ // When called from accounts action item in Main, this flag is passed to
+ // indicate
+ // that LoginActivity should not auto login as we are managing the
+ // accounts,
+ // rather than performing the initial login
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ manageAccountsMode = extras.getBoolean(LoginActivity.EXTRA_MANAGE_ACCOUNTS_MODE);
+ }
+
+ if (manageAccountsMode) {
+ getActionBar().setTitle(R.string.manage_accounts);
+ }
+
+ selectedAccount = developerAccountManager.getSelectedDeveloperAccount();
+
+ setContentView(R.layout.login);
+ setProgressBarIndeterminateVisibility(false);
+ accountList = (RecyclerView) findViewById(R.id.login_input);
+
+ okButton = findViewById(R.id.login_ok_button);
+ okButton.setClickable(true);
+ okButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ new AsyncTask() {
+
+ @Override
+ protected void onPreExecute() {
+ setProgressBarIndeterminateVisibility(true);
+ okButton.setEnabled(false);
+ }
+
+ @Override
+ protected Void doInBackground(Void... args) {
+ saveDeveloperAccounts();
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void arg) {
+ setProgressBarIndeterminateVisibility(false);
+ okButton.setEnabled(true);
+
+ if (selectedAccount != null) {
+ redirectToMain(selectedAccount.getName(),
+ selectedAccount.getDeveloperId());
+ } else {
+ // Go to the first non hidden account
+ for (DeveloperAccount account : developerAccounts) {
+ if (account.isVisible()) {
+ redirectToMain(account.getName(), account.getDeveloperId());
+ break;
+ }
+ }
+ }
+ }
+ }.execute();
+ }
+ });
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ boolean skipAutologin = Preferences.getSkipAutologin(this);
+
+ if (!manageAccountsMode & !skipAutologin & selectedAccount != null) {
+ redirectToMain(selectedAccount.getName(), selectedAccount.getDeveloperId());
+ } else {
+ showAccountList();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getMenuInflater().inflate(R.menu.login_menu, menu);
+ return true;
+ }
+
+ /**
+ * Called if item in option menu is selected.
+ *
+ * @param item The chosen menu item
+ * @return boolean true/false
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.itemLoginmenuAdd:
+ addNewGoogleAccount();
+ break;
+ case android.R.id.home:
+ if (!blockGoingBack) {
+ setResult(RESULT_OK);
+ finish();
+ }
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void onBackPressed() {
+ setResult(blockGoingBack ? RESULT_CANCELED : RESULT_OK);
+ super.onBackPressed();
+ }
+
+ protected void showAccountList() {
+ accountList.setLayoutManager(new LinearLayoutManager(this));
+ this.developerAccounts = getDeveloperAccounts();
+ AccountListAdapter adapter = new AccountListAdapter(developerAccounts, this);
+ accountList.setAdapter(adapter);
+
+ okButton.setEnabled(isAtLeastOneAccountEnabled());
+ }
+
+ @Override
+ public void accountSelected(boolean checked, DeveloperAccount account) {
+ if (checked) {
+ account.activate();
+ } else {
+ account.hide();
+ }
+
+ if (manageAccountsMode && account.equals(selectedAccount)) {
+ // If they remove the current account, then stop them
+ // going back
+ blockGoingBack = account.isHidden();
+ }
+
+ okButton.setEnabled(isAtLeastOneAccountEnabled());
+ }
+
+ private List getDeveloperAccounts() {
+ Account[] googleAccounts = accountManager.getAccountsByType(AutosyncHandler.ACCOUNT_TYPE_GOOGLE);
+ List dbAccounts = developerAccountManager.getAllDeveloperAccounts();
+ List result = new ArrayList<>();
+
+ for (int i = 0; i < googleAccounts.length; i++) {
+ DeveloperAccount developerAccount = DeveloperAccount
+ .createHidden(googleAccounts[i].name);
+ int idx = dbAccounts.indexOf(developerAccount);
+ // use persistent object if exists
+ if (idx != -1) {
+ developerAccount = dbAccounts.get(idx);
+ }
+ result.add(developerAccount);
+
+ // Setup auto sync
+ // only do this when managing accounts, otherwise sync may start
+ // in the background before accounts are actually configured
+ if (manageAccountsMode) {
+ // Ensure it matches the sync period (excluding disabled state)
+ syncHandler.setAutosyncPeriod(googleAccounts[i].name,
+ Preferences.getLastNonZeroAutosyncPeriod(this));
+ // Now make it match the master sync (including disabled state)
+ syncHandler.setAutosyncPeriod(googleAccounts[i].name,
+ Preferences.getAutosyncPeriod(this));
+ }
+ }
+
+ return result;
+ }
+
+ private void saveDeveloperAccounts() {
+ for (DeveloperAccount account : developerAccounts) {
+ if (account.isHidden()) {
+ // They are removing the account from Andlytics, disable
+ // syncing
+ syncHandler.setAutosyncEnabled(account.getName(), false);
+ } else {
+ // Make it match the master sync period (including
+ // disabled state)
+ syncHandler.setAutosyncPeriod(account.getName(),
+ Preferences.getAutosyncPeriod(LoginActivity.this));
+ }
+ developerAccountManager.addOrUpdateDeveloperAccount(account);
+ }
+ }
+
+ private boolean isAtLeastOneAccountEnabled() {
+ for (DeveloperAccount acc : developerAccounts) {
+ if (acc.isVisible()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void addNewGoogleAccount() {
+ AccountManagerCallback callback = new AccountManagerCallback() {
+ public void run(AccountManagerFuture future) {
+ try {
+ Bundle bundle = future.getResult();
+ bundle.keySet();
+ Log.d(TAG, "account added: " + bundle);
+
+ showAccountList();
+
+ } catch (OperationCanceledException e) {
+ Log.d(TAG, "addAccount was canceled");
+ } catch (IOException e) {
+ Log.d(TAG, "addAccount failed: " + e);
+ } catch (AuthenticatorException e) {
+ Log.d(TAG, "addAccount failed: " + e);
+ }
+ // gotAccount(false);
+ }
+ };
+
+ // TODO request a weblogin: token here, so we have it cached?
+ accountManager.addAccount(AutosyncHandler.ACCOUNT_TYPE_GOOGLE,
+ LoginActivity.AUTH_TOKEN_TYPE_ANDROID_DEVELOPER, null, null /* options */,
+ LoginActivity.this, callback, null /* handler */);
+ }
+
+ private void redirectToMain(String selectedAccount, String developerId) {
+ Preferences.saveSkipAutoLogin(this, false);
+ Intent intent = new Intent(LoginActivity.this, Main.class);
+ intent.putExtra(BaseActivity.EXTRA_AUTH_ACCOUNT_NAME, selectedAccount);
+ intent.putExtra(BaseActivity.EXTRA_DEVELOPER_ID, developerId);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ overridePendingTransition(R.anim.activity_fade_in, R.anim.activity_fade_out);
+ finish();
+ }
}