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(); + } }