Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions example/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,23 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.cattaka.android.snippets.example">

<uses-permission
android:name="android.permission.AUTHENTICATE_ACCOUNTS"
android:maxSdkVersion="22" />
<uses-permission
android:name="android.permission.GET_ACCOUNTS"
android:maxSdkVersion="22" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />

<uses-permission
android:name="android.permission.MANAGE_ACCOUNTS"
android:maxSdkVersion="22" />

<uses-permission
android:name="android.permission.USE_CREDENTIALS"
android:maxSdkVersion="22" />

<application
android:name=".core.MyApplication"
android:allowBackup="false"
Expand Down Expand Up @@ -45,6 +59,17 @@
<activity android:name=".MotionLayoutStretchableSurfacesActivity" />
<activity android:name=".MotionLayoutMorphActivity" />
<activity android:name=".MotionLayoutProgressActivity" />
<activity android:name=".AccountsListActivity" />
<activity android:name=".AccountEditActivity" />

<service android:name=".account_manager.AuthenticatorService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package net.cattaka.android.snippets.example;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.Context;
import android.content.Intent;
import android.databinding.DataBindingUtil;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

import net.cattaka.android.snippets.example.data.AccountContainer;
import net.cattaka.android.snippets.example.databinding.ActivityAccountEditBinding;

public class AccountEditActivity extends AppCompatActivity implements View.OnClickListener {
public static final String KEY_ORIG_ACCOUNT = "et.cattaka.android.snippets.example.AccountEditActivity.ORIG_ACCOUNT";

public static Intent createIntent(@NonNull Context context, @NonNull AccountContainer origAccountContainer) {
Intent intent = new Intent(context, AccountEditActivity.class);
intent.putExtra(KEY_ORIG_ACCOUNT, origAccountContainer);
return intent;
}

ActivityAccountEditBinding mBinding;
AccountContainer mOrigAccountContainer;

AccountManager mAccountManager;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_account_edit);

mOrigAccountContainer = getIntent().getParcelableExtra(KEY_ORIG_ACCOUNT);

mBinding.buttonCancel.setOnClickListener(this);
mBinding.buttonRemove.setOnClickListener(this);
mBinding.buttonOk.setOnClickListener(this);

if (mOrigAccountContainer != null) {
mBinding.editAccountName.setText(mOrigAccountContainer.getAccount().name);
mBinding.editAuthToken.setText(mOrigAccountContainer.getAuthToken());
mBinding.buttonRemove.setVisibility(View.VISIBLE);
} else {
mOrigAccountContainer = new AccountContainer();
mBinding.buttonRemove.setVisibility(View.INVISIBLE);
}

mAccountManager = AccountManager.get(this);
}

@Override
public void onClick(View v) {
if (v.getId() == R.id.button_cancel) {
finish();
} else if (v.getId() == R.id.button_remove) {
onClickRemove();
} else if (v.getId() == R.id.button_ok) {
onClickOk();
}
}

private void onClickOk() {
String accountType = getString(R.string.account_manager_account_type);
String name = String.valueOf(mBinding.editAccountName.getText());
String authToken = String.valueOf(mBinding.editAuthToken.getText());
if (mOrigAccountContainer.getAccount() != null) {
// TODO: Show block
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mAccountManager.renameAccount(mOrigAccountContainer.getAccount(), name, null, null);
mAccountManager.setAuthToken(mOrigAccountContainer.getAccount(), Constants.AUTH_TOKEN_TYPE, authToken);
finish();
} else {
// FIXME: Can not rename correctly with older devices
mAccountManager.removeAccount(mOrigAccountContainer.getAccount(), future -> {
Account account = new Account(name, accountType);
mAccountManager.addAccountExplicitly(account, null, null);
mAccountManager.setAuthToken(account, Constants.AUTH_TOKEN_TYPE, authToken);
finish();
}, null);
}
} else {
Account account = new Account(name, accountType);
mAccountManager.addAccountExplicitly(account, null, null);
mAccountManager.setAuthToken(account, Constants.AUTH_TOKEN_TYPE, authToken);
finish();
}
}

private void onClickRemove() {
if (mOrigAccountContainer.getAccount() != null) {
// TODO: Show block
mAccountManager.removeAccount(mOrigAccountContainer.getAccount(), future -> {
finish();
}, null);
} else {
// error case
finish();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package net.cattaka.android.snippets.example;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Toast;

import net.cattaka.android.adaptertoolbox.adapter.ScrambleAdapter;
import net.cattaka.android.adaptertoolbox.adapter.listener.ListenerRelay;
import net.cattaka.android.snippets.example.adapter.factory.AccountViewHolderFactory;
import net.cattaka.android.snippets.example.data.AccountContainer;
import net.cattaka.android.snippets.example.databinding.ActivityAccountsListBinding;
import net.cattaka.android.snippets.example.tracker.IScreen;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;

public class AccountsListActivity extends AppCompatActivity implements
IScreen,
View.OnClickListener {
ListenerRelay<ScrambleAdapter<?>, RecyclerView.ViewHolder> mListenerRelay = new ListenerRelay<ScrambleAdapter<?>, RecyclerView.ViewHolder>() {
@Override
public void onClick(@NonNull RecyclerView recyclerView, @NonNull ScrambleAdapter<?> adapter, @NonNull RecyclerView.ViewHolder holder, @NonNull View view) {
super.onClick(recyclerView, adapter, holder, view);
if (holder instanceof AccountViewHolderFactory.ViewHolder) {
Account account = mAdapter.getItemAt(holder.getAdapterPosition());
if (view.getId() == R.id.button_info) {
onClickButtonInfo(account);
} else {
obtainAccountContainer(account, arg -> {
startActivity(AccountEditActivity.createIntent(me, arg));
});
}
}
}
};

AccountsListActivity me = this;
ActivityAccountsListBinding mBinding;
AccountManager mAccountManager;

ScrambleAdapter<Account> mAdapter;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_accounts_list);

mBinding.buttonAdd.setOnClickListener(this);

mAdapter = new ScrambleAdapter<>(this, new ArrayList<>(), mListenerRelay, new AccountViewHolderFactory());
mBinding.recycler.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false));
mBinding.recycler.setAdapter(mAdapter);

mAccountManager = AccountManager.get(this);
}

@Override
protected void onStart() {
super.onStart();
reloadAccounts();
}

@Override
public void onClick(View v) {
if (v.getId() == R.id.button_add) {
startActivity(AccountEditActivity.createIntent(this, null));
}
}

private void reloadAccounts() {
String accountType = getString(R.string.account_manager_account_type);
Account[] accounts = mAccountManager.getAccountsByType(accountType);

mAdapter.getItems().clear();
mAdapter.getItems().addAll(Arrays.asList(accounts));
mAdapter.notifyDataSetChanged();
}

private void onClickButtonInfo(@NonNull Account account) {
obtainAccountContainer(account, arg -> {
String name = arg.getAccount().name;
String accountType = arg.getAccount().type;
String authToken = arg.getAuthToken();
String text = String.format(Locale.ROOT, "name = %s\naccountType = %s\nauthToken = %s\n", name, accountType, authToken);
Toast.makeText(me, text, Toast.LENGTH_SHORT).show();
}
);
}

private void obtainAccountContainer(@NonNull Account account, @NonNull Consumer<AccountContainer> consumer) {
mAccountManager.getAuthToken(
account,
Constants.AUTH_TOKEN_TYPE,
true,
future -> {
if (future.isDone()) {
try {
Bundle bundle = future.getResult();
String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);

AccountContainer container = new AccountContainer();
container.setAccount(account);
container.setAuthToken(authToken);
consumer.accept(container);
} catch (IOException e) {
// ignore
} catch (OperationCanceledException e) {
// ignore
} catch (AuthenticatorException e) {
// ignore
}
}
}, null);
}

private interface Consumer<T> {
void accept(T arg);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ public class Constants {
public static final String DB_NAME = "database";
public static final int DB_VERSION = 1;
public static final String PREF_NAME = "pref";

public static final String AUTH_TOKEN_TYPE = "my_auth_token_type";
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public class MainActivity extends AppCompatActivity implements IScreen {
new ActivityEntry("Progress", MotionLayoutProgressActivity.class, Build.VERSION_CODES.JELLY_BEAN_MR2),
new ActivityEntry("Morph", MotionLayoutMorphActivity.class, Build.VERSION_CODES.JELLY_BEAN_MR2)
),
new ActivityEntry("AccountManager", null,
new ActivityEntry("Accounts List", AccountsListActivity.class)
),
new ActivityEntry("With Google Applications", null,
new ActivityEntry("Pick from Google Photos", PickFromGooglePhotosActivity.class)
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package net.cattaka.android.snippets.example.account_manager;

import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.accounts.NetworkErrorException;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;

import net.cattaka.android.snippets.example.AccountEditActivity;
import net.cattaka.android.snippets.example.Constants;

public class AccountAuthenticator extends AbstractAccountAuthenticator {
Context mContext;

public AccountAuthenticator(Context context) {
super(context);
mContext = context;
}

@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
return null;
}

@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
Intent intent = new Intent(mContext, AccountEditActivity.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}

@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
return null;
}

@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
AccountManager manager = AccountManager.get(mContext);
String authToken = manager.peekAuthToken(account, Constants.AUTH_TOKEN_TYPE);

Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
if (Constants.AUTH_TOKEN_TYPE.equals(authTokenType)) {
result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
}
return result;
}

@Override
public String getAuthTokenLabel(String authTokenType) {
return null;
}

@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
return null;
}

@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package net.cattaka.android.snippets.example.account_manager;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class AuthenticatorService extends Service {
private AccountAuthenticator mAccountAuthenticator;

public AuthenticatorService() {
}

@Override
public void onCreate() {
super.onCreate();
mAccountAuthenticator = new AccountAuthenticator(this);
}

@Override
public IBinder onBind(Intent intent) {
return this.mAccountAuthenticator.getIBinder();
}
}
Loading