-
-
Save mr-archano/c44ee1bf084cc2d742d9 to your computer and use it in GitHub Desktop.
Revisions
-
burgalon revised this gist
Jul 16, 2014 . 1 changed file with 25 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,25 @@ dependencies { .... compile 'com.android.support:appcompat-v7:19.+' compile 'com.squareup.dagger:dagger:1.2.1' provided 'com.squareup.dagger:dagger-compiler:1.2.1' compile 'com.squareup:otto:1.3.+' compile 'com.squareup.okhttp:okhttp:1.5.+' compile 'com.squareup.picasso:picasso:2.2.0' compile 'com.squareup.retrofit:retrofit:1.5.+' debugCompile 'com.squareup.retrofit:retrofit-mock:1.5.+' compile 'com.jakewharton:butterknife:5.1.+' compile 'com.jakewharton.timber:timber:2.2.2' debugCompile 'com.jakewharton.madge:madge:1.1.1' debugCompile 'com.jakewharton.scalpel:scalpel:1.1.1' compile 'com.netflix.rxjava:rxjava-core:0.19.+' compile 'com.netflix.rxjava:rxjava-android:0.19.+' compile 'com.f2prateek.dart:dart:1.1.+' } -
burgalon revised this gist
Jul 16, 2014 . 1 changed file with 3 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,6 @@ // An activity which handles listening to AccountManager changes and invoking AuthenticatorActivity if no account are available // Also hanldes Dagger injections, provides an Otto bus, and allows subscription to observables // while listening to activity lifecycle @SuppressLint("Registered") public class BaseActivity extends FragmentActivity implements OnAccountsUpdateListener { @Inject AppContainer appContainer; -
burgalon revised this gist
Jul 16, 2014 . 1 changed file with 106 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,106 @@ @SuppressLint("Registered") public class BaseActivity extends FragmentActivity implements OnAccountsUpdateListener { @Inject AppContainer appContainer; @Inject ScopedBus bus; @Inject AccountManager accountManager; private ViewGroup container; private ObjectGraph activityGraph; private SubscriptionManager<Activity> subscriptionManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); buildActivityGraphAndInject(); // Inject any extras Dart.inject(this); } private void buildActivityGraphAndInject() { // Create the activity graph by .plus-ing our modules onto the application graph. App app = App.get(this); activityGraph = app.getApplicationGraph().plus(getModules().toArray()); // Inject ourselves so subclasses will have dependencies fulfilled when this method returns. activityGraph.inject(this); container = appContainer.get(this, app); } /** Inject the given object into the activity graph. */ public void inject(Object o) { activityGraph.inject(o); } /** * A list of modules to use for the individual activity graph. Subclasses can override this * method to provide additional modules provided they call and include the modules returned by * calling {@code super.getModules()}. */ protected List<Object> getModules() { return Arrays.<Object>asList(new ActivityModule(this)); } @Override protected void onResume() { super.onResume(); bus.resumed(); bus.register(this); // Watch to make sure the account still exists. if(requireLogin()) accountManager.addOnAccountsUpdatedListener(this, null, true); } @Override protected void onPause() { bus.unregister(this); bus.paused(); if(requireLogin()) accountManager.removeOnAccountsUpdatedListener(this); super.onPause(); } protected boolean requireLogin() { return true; } @Override protected void onDestroy() { // Eagerly clear the reference to the activity graph to allow it to be garbage collected as // soon as possible. activityGraph = null; if(subscriptionManager!=null) subscriptionManager.unsubscribeAll(); super.onDestroy(); } protected void inflateLayout(int layoutResID) { getLayoutInflater().inflate(layoutResID, container); // Inject Views ButterKnife.inject(this); } public static BaseActivity get(Fragment fragment) { return (BaseActivity) fragment.getActivity(); } @Override public void onAccountsUpdated(Account[] accounts) { for (Account account : accounts) { if (AuthConstants.ACCOUNT_TYPE.equals(account.type)) { return; } } // No accounts so start the authenticator activity Intent intent = new Intent(this, AuthenticatorActivity.class); startActivity(intent); finish(); } protected <O> Subscription subscribe(final Observable<O> source, final Observer<O> observer) { if (subscriptionManager == null) { subscriptionManager = new ActivitySubscriptionManager(this); } return subscriptionManager.subscribe(source, observer); } } -
burgalon revised this gist
Jul 16, 2014 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1 @@ This snippet works with Doorkeeper and uses refresh token (with refresh token rotation). -
burgalon created this gist
Jul 16, 2014 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,110 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator { private final Context context; @Inject @ClientId String clientId; @Inject @ClientSecret String clientSecret; @Inject ApiService apiService; public AccountAuthenticator(Context context) { super(context); this.context = context; ((App) context.getApplicationContext()).inject(this); } /* * The user has requested to add a new account to the system. We return an intent that will launch our login screen * if the user has not logged in yet, otherwise our activity will just pass the user's credentials on to the account * manager. */ @Override public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) { Timber.v("addAccount()"); final Intent intent = new Intent(context, AuthenticatorActivity.class); intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, accountType); intent.putExtra(LoginFragment.PARAM_AUTHTOKEN_TYPE, authTokenType); intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); final Bundle bundle = new Bundle(); bundle.putParcelable(AccountManager.KEY_INTENT, intent); return bundle; } @Override public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) { return null; } @Override public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) { return null; } // See /Applications/android-sdk-macosx/samples/android-18/legacy/SampleSyncAdapter/src/com/example/android/samplesync/authenticator/Authenticator.java // Also take a look here https://github.com/github/android/blob/d6ba3f9fe2d88967f56e9939d8df7547127416df/app/src/main/java/com/github/mobile/accounts/AccountAuthenticator.java @Override public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { Timber.d("getAuthToken() account="+account.name+ " type="+account.type); final Bundle bundle = new Bundle(); // If the caller requested an authToken type we don't support, then // return an error if (!authTokenType.equals(AUTHTOKEN_TYPE)) { Timber.d("invalid authTokenType" + authTokenType); bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType"); return bundle; } // Extract the username and password from the Account Manager, and ask // the server for an appropriate AuthToken final AccountManager accountManager = AccountManager.get(context); // Password is storing the refresh token final String password = accountManager.getPassword(account); if (password != null) { Timber.i("Trying to refresh access token"); try { AccessToken accessToken = apiService.refreshAccessToken(password, clientId, clientSecret); if (accessToken!=null && !TextUtils.isEmpty(accessToken.accessToken)) { bundle.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE); bundle.putString(AccountManager.KEY_AUTHTOKEN, accessToken.accessToken); accountManager.setPassword(account, accessToken.refreshToken); return bundle; } } catch (Exception e) { Timber.e(e, "Failed refreshing token."); } } // Otherwise... start the login intent Timber.i("Starting login activity"); final Intent intent = new Intent(context, AuthenticatorActivity.class); intent.putExtra(LoginFragment.PARAM_USERNAME, account.name); intent.putExtra(LoginFragment.PARAM_AUTHTOKEN_TYPE, authTokenType); intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); bundle.putParcelable(AccountManager.KEY_INTENT, intent); return bundle; } @Override public String getAuthTokenLabel(String authTokenType) { return authTokenType.equals(AUTHTOKEN_TYPE) ? authTokenType : null; } @Override public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException { final Bundle result = new Bundle(); result.putBoolean(KEY_BOOLEAN_RESULT, false); return result; } @Override public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) { return null; } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,14 @@ public class AccountAuthenticatorService extends Service { private static AccountAuthenticator AUTHENTICATOR = null; @Override public IBinder onBind(Intent intent) { return intent.getAction().equals(ACTION_AUTHENTICATOR_INTENT) ? getAuthenticator().getIBinder() : null; } private AccountAuthenticator getAuthenticator() { if (AUTHENTICATOR == null) AUTHENTICATOR = new AccountAuthenticator(this); return AUTHENTICATOR; } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,28 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.app"> <service android:name=".authenticator.AccountAuthenticatorService" android:exported="false" android:process=":auth"> <intent-filter> <action android:name="android.accounts.AccountAuthenticator"/> </intent-filter> <meta-data android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator"/> </service> <activity android:name=".authenticator.AuthenticatorActivity" android:screenOrientation="portrait" android:configChanges="orientation|keyboardHidden|screenSize" android:excludeFromRecents="true" android:hardwareAccelerated="true"/> </application> </manifest> This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,79 @@ public class ApiAuthenticator implements OkAuthenticator { AccountManager accountManager; Application application; @Inject public ApiAuthenticator(Application application, AccountManager accountManager) { this.application = application; this.accountManager = accountManager; } @Override public Credential authenticate(Proxy proxy, URL url, List<Challenge> challenges) throws IOException { // Do not try to authenticate oauth related endpoints if (url.getPath().startsWith("/oauth")) return null; for (Challenge challenge : challenges) { if (challenge.getScheme().equals("Bearer")) { Account[] accounts = accountManager.getAccountsByType(AuthConstants.ACCOUNT_TYPE); if (accounts.length != 0) { String oldToken = accountManager.peekAuthToken(accounts[0], AuthConstants.AUTHTOKEN_TYPE); if (oldToken != null) { Timber.d("invalidating auth token"); accountManager.invalidateAuthToken(AuthConstants.ACCOUNT_TYPE, oldToken); } try { Timber.d("calling accountManager.blockingGetAuthToken"); String token = accountManager.blockingGetAuthToken(accounts[0], AuthConstants.AUTHTOKEN_TYPE, false); if(token==null) { accountManager.removeAccount(accounts[0], null, null); } // Do not retry certain URLs //if (url.getPath().startsWith("/donotretry")) { // return null; //} else if (token != null) { if (token != null) { return token(token); } } catch (OperationCanceledException e) { e.printStackTrace(); } catch (AuthenticatorException e) { e.printStackTrace(); } } } } return null; } private Credential token(String token) { try { // TODO: when there is support for different types of Credentials, stop using reflection Constructor<?> constructor = Credential.class.getDeclaredConstructor(String.class); Assert.assertTrue(Modifier .isPrivate(constructor.getModifiers())); constructor.setAccessible(true); return (Credential) constructor.newInstance("Bearer " + token); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; } @Override public Credential authenticateProxy(Proxy proxy, URL url, List<Challenge> challenges) throws IOException { return null; } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,23 @@ public final class ApiHeaders implements RequestInterceptor { private Application application; @Inject public ApiHeaders(Application application) { this.application = application; } @Override public void intercept(RequestFacade request) { AccountManager accountManager = AccountManager.get(application); Account[] accounts = accountManager.getAccountsByType(AuthConstants.ACCOUNT_TYPE); if (accounts.length != 0) { String token = accountManager.peekAuthToken(accounts[0], AuthConstants.AUTHTOKEN_TYPE); if (token != null) { request.addHeader("Authorization", "Bearer " + token); } } request.addHeader("Accept", "application/javascript, application/json"); } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,44 @@ @Module( complete = false, library = true ) public final class ApiModule { public static final String PRODUCTION_API_URL = "http://api.pow-app.192.168.56.1.xip.io/"; private static final String CLIENT_ID = "CLIENT_ID"; private static final String CLIENT_SECRET = "CLIENT_SECRET"; @Provides @Singleton @ClientId String provideClientId() { return CLIENT_ID; } @Provides @Singleton @ClientSecret String provideClientSecret() { return CLIENT_SECRET; } @Provides @Singleton Endpoint provideEndpoint() { return Endpoints.newFixedEndpoint(PRODUCTION_API_URL); } @Provides @Singleton Client provideClient(OkHttpClient client) { return new OkClient(client); } @Provides @Singleton RestAdapter provideRestAdapter(Endpoint endpoint, Client client, ApiHeaders headers, Gson gson) { return new RestAdapter.Builder() .setClient(client) .setEndpoint(endpoint) .setConverter(new GsonConverter(gson)) .setRequestInterceptor(headers) .setErrorHandler(new RestErrorHandler()) .build(); } @Provides @Singleton ApiService provideApiService(RestAdapter restAdapter) { return restAdapter.create(ApiService.class); } @Provides @Singleton ApiDatabase provideApiDatabase(ApiService service) { return new ApiDatabase(service); } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,23 @@ // Retrofit interface public interface ApiService { // Auth @FormUrlEncoded @POST("/oauth/token?grant_type=password") AccessToken getAccessToken( @Field("username") String email, @Field("password") String password, @Field("client_id") String clientId, @Field("client_secret") String clientSecret); @FormUrlEncoded @POST("/oauth/token?grant_type=refresh_token") AccessToken refreshAccessToken( @Field("refresh_token") String refreshToken, @Field("client_id") String clientId, @Field("client_secret") String clientSecret); @FormUrlEncoded @POST("/oauth/token?grant_type=password") Observable<AccessToken> getAccessTokenObservable( @Field("username") String email, @Field("password") String password, @Field("client_id") String clientId, @Field("client_secret") String clientSecret); } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,13 @@ public interface AuthConstants { // Account type id String ACCOUNT_TYPE = "com.example"; // Account name String ACCOUNT_NAME = "Example"; // Provider ID String PROVIDER_AUTHORITY = "com.example.sync"; // Auth token type String AUTHTOKEN_TYPE = ACCOUNT_TYPE; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,61 @@ class LoginFragment extends BaseFragment { @Inject AccountManager accountManager; @InjectView(R.id.et_email) EditText emailText; @InjectView(R.id.et_password) EditText passwordText; @InjectView(R.id.sign_in) Button signInButton; @Inject @ClientId String clientId; @Inject @ClientSecret String clientSecret; ... private void doLogin(final String email, String password) { Observable<AccessToken> accessTokenObservable = apiService.getAccessTokenObservable(email, password, clientId, clientSecret); subscribe(accessTokenObservable, new EndlessObserver<AccessToken>() { @Override public void onNext(AccessToken accessToken) { Account account = addOrFindAccount(email, accessToken.refreshToken); // accountManager.setUserData(account, AccountAuthenticator.USER_ID, accessToken.userId); accountManager.setAuthToken(account, AuthConstants.AUTHTOKEN_TYPE, accessToken.accessToken); finishAccountAdd(email, accessToken.accessToken, accessToken.refreshToken); } @Override public void onError(Throwable throwable) { Timber.e(throwable, "Could not sign in"); Toast.makeText(getActivity(), throwable.getMessage(), Toast.LENGTH_LONG).show(); } }); } private Account addOrFindAccount(String email, String password) { Account[] accounts = accountManager.getAccountsByType(AuthConstants.ACCOUNT_TYPE); Account account = accounts.length != 0 ? accounts[0] : new Account(email, AuthConstants.ACCOUNT_TYPE); if (accounts.length == 0) { accountManager.addAccountExplicitly(account, password, null); } else { accountManager.setPassword(accounts[0], password); } return account; } private void finishAccountAdd(String accountName, String authToken, String password) { final Intent intent = new Intent(); intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, accountName); intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, AuthConstants.ACCOUNT_TYPE); if (authToken != null) intent.putExtra(AccountManager.KEY_AUTHTOKEN, authToken); intent.putExtra(AccountManager.KEY_PASSWORD, password); setAccountAuthenticatorResult(intent.getExtras()); getActivity().setResult(Activity.RESULT_OK, intent); getActivity().finish(); // Go back to the main activity startActivity(new Intent(activityContext, MainActivity.class)); } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,8 @@ // src/main/res/xml/authenticator.xml <?xml version="1.0" encoding="utf-8"?> <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" android:accountType="com.example" android:icon="@drawable/app_icon" android:label="@string/application_name" android:smallIcon="@drawable/app_icon" />