Tuesday, January 26, 2016

Understanding Architecture of Nick Butcher's Plaid App - Part-5

Today we are going to look at how Plaid app communicates with Dribble API and that its the same mechanism with which it communicates with Designer News and Producthunt APIs. but for the purpose of this blog we are going to look at only the Dribble APIs.

This is the tree that we will discuss today.

Data->api
  • Designernews
  • Dribble
  • Producthunt     







If you open the navigation drawer and click on
  • Dribble following
  • My Dribble Shots
  • My Dribble likes

You will be asked for a login into Dribble.


Plaid app uses Retrofit to for the communication with Dribble API , why ? Just because its awesome , you will see how in this post.  Retrofit is popular library among all android developers for communicating with Rest APIs and its from SQUARE.

Model Classes : 

Shot.java - modeled from http://developer.dribbble.com/v1/shots/
User.java  - modeled from http://developer.dribbble.com/v1/users/

Response
The response will be returned as JSON and takes the following form:
{
 
"access_token" : "29ed478ab86c07f1c069b1af76088f7431396b7c4a2523d06911345da82224a0",
 
"token_type" : "bearer",
 
"scope" : "public write"
}

Images.java - modeled from field images from  http://developer.dribbble.com/v1/shots/


Retrofit endpoint specific implementation

DribbbleAuthService.java
      This service helps authenticating with Dribble API

 DribbbleService.java
     This service helps fetch the data from Dribble API once the authentication is successful



DribbleLogin.java 

Flow :   Once you press on login button

DribbleLogin.java
  public void doLogin(View view) {
        showLoading();
        dribbblePrefs.login(DribbbleLogin.this);
    }


DribblePrefs.java
 public static final String LOGIN_URL = "https://dribbble.com/oauth/authorize?client_id="
            + BuildConfig.DRIBBBLE_CLIENT_ID
            + "&redirect_uri=plaid%3A%2F%2F" + LOGIN_CALLBACK
            + "&scope=public+write+comment+upload";


 public void login(Context context) {
        Log.d("#### " , Uri.parse(LOGIN_URL).toString());
        context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(LOGIN_URL)));
    }


for Dribblelogin activity , in AndroidManifest.xml, we have  android:launchmode = "SingleTop" ,
Once we are redirected back to the activity after authentication , we use the same activity instance and redeliver the intent to same activity.

This time onNewIntent() is invoked and since we will receive the access token as part of this intent we fetch the accesstoken.  Then we authenticate with Dribble auth endpoint using the client ID / SECRET and the access token that we received. Once the authentication is successful , the logged in user is displayed.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
   @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        checkAuthCallback(intent);
    }

  
private void checkAuthCallback(Intent intent) {
        if (intent != null
                && intent.getData() != null
                && !TextUtils.isEmpty(intent.getData().getAuthority())
                && DribbblePrefs.LOGIN_CALLBACK.equals(intent.getData().getAuthority())) {
            showLoading();
            getAccessToken(intent.getData().getQueryParameter("code"));
        }

    }



  private void getAccessToken(String code) {
        RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(DribbbleAuthService.ENDPOINT)
                .build();

        DribbbleAuthService dribbbleAuthApi = restAdapter.create((DribbbleAuthService.class));

        dribbbleAuthApi.getAccessToken(BuildConfig.DRIBBBLE_CLIENT_ID,
                BuildConfig.DRIBBBLE_CLIENT_SECRET,
                code, "", new Callback<AccessToken>() {
                    @Override
                    public void success(AccessToken accessToken, Response response) {
                        dribbblePrefs.setAccessToken(accessToken.access_token);
                        showLoggedInUser();
                        setResult(Activity.RESULT_OK);
                        finishAfterTransition();
                    }

                    @Override
                    public void failure(RetrofitError error) {
                        Log.e(getClass().getCanonicalName(), error.getMessage(), error);
                        // TODO snackbar?
                        Toast.makeText(getApplicationContext(), "Log in failed: " + error
                                .getResponse()
                                .getStatus(), Toast.LENGTH_LONG).show();
                        showLogin();
                    }
                });
    }


private void showLoggedInUser() {
        Gson gson = new GsonBuilder()
                .setDateFormat(DribbbleService.DATE_FORMAT)
                .create();

        RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(DribbbleService.ENDPOINT)
                .setConverter(new GsonConverter(gson))
                .setRequestInterceptor(new AuthInterceptor(dribbblePrefs.getAccessToken()))
                .build();

        DribbbleService dribbbleApi = restAdapter.create((DribbbleService.class));
        dribbbleApi.getAuthenticatedUser(new Callback<User>() {
            @Override
            public void success(User user, Response response) {
                dribbblePrefs.setLoggedInUser(user);
                Toast confirmLogin = new Toast(getApplicationContext());
                View v = LayoutInflater.from(DribbbleLogin.this).inflate(R.layout
                        .toast_logged_in_confirmation, null, false);
                ((TextView) v.findViewById(R.id.name)).setText(user.name);
                // need to use app context here as the activity will be destroyed shortly
                Glide.with(getApplicationContext())
                        .load(user.avatar_url)
                        .placeholder(R.drawable.ic_player)
                        .transform(new CircleTransform(getApplicationContext()))
                        .into((ImageView) v.findViewById(R.id.avatar));
                v.findViewById(R.id.scrim).setBackground(ScrimUtil.makeCubicGradientScrimDrawable
                        (ContextCompat.getColor(DribbbleLogin.this, R.color.scrim),
                                5, Gravity.BOTTOM));
                confirmLogin.setView(v);
                confirmLogin.setGravity(Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 0, 0);
                confirmLogin.setDuration(Toast.LENGTH_LONG);
                confirmLogin.show();
            }

            @Override
            public void failure(RetrofitError error) {
            }
        });
    }

AuthInterceptor.java 

A {@see RequestInterceptor} that adds an auth token to requests

 @Override
    public void intercept(RequestFacade request) {
        request.addHeader("Authorization", "Bearer " + accessToken);
    }


Use the access token to access the API.
The access token allows you to make requests to the API on a behalf of a user.
You can pass the token in the query parameters like shown above, but a cleaner approach is to include it in the Authorization header:
Authorization: Bearer ACCESS_TOKEN
For example, in curl you can set the Authorization header like this:
curl -H "Authorization: Bearer ACCESS_TOKEN" https://api.dribbble.com/v1/user


No comments:

Post a Comment