转载请标明出处:
声明:仿人人项目,所用所有图片资源都来源于官方人人android客户端,编写本应用的目的在于学习交流,如涉及侵权请告知,我会及时换掉用到的相关图片。以前在公司做Android应用(后面简称App)时的疑惑: 我只想实现用户可以用人人账号能登录我们的App,可以将自己在我们App中看到的,某些喜欢的内容分享给自己在人人上的朋友、同事和同学等。但是我又不想使用人人官方提供的SDK。理由:太大,有人可能要说,可以去掉没用到的类文件,我想说的是太费事了(我当时就这件事花了一天多时间);授权界面的Dialog觉得不美观,我自己得改。当时我就在想人人如果能提供一个小点的SDK只有我说的那两个功能就好了,欣赏大多数应用,里面用到人人提供的功能的就是我说的这两个。鉴于此,这次我不想使用人人提供的SDK(当然肯定会参考),所有功能自己编码实现,这对我来说是一个挑战,不过我喜欢这种有挑战的事。好了开始正题,这篇是在前面的基础上进行的,不明白可以查看下。
一、类图如下:
二、编码实现:
1、布局文件:
a. 主布局文件(auth.xml)
b. 顶部工具栏(或者菜单栏)的布局文件(top_navbar.xml)
2、应用授权界面的具体实现:
a. 初始组件:
@Override protected void setupView() { mTopNavbar = (TopNavbar) findViewById(R.id.rl_top_navbar); mWebView = (WebView) findViewById(R.id.wv_auth); mWebView.setVerticalScrollBarEnabled(false); mWebView.setHorizontalScrollBarEnabled(false); mWebView.getSettings().setJavaScriptEnabled(true); }
b. 组拼请求参数
StringBuilder authorizeUrl = new StringBuilder(Constant.AUTHORIZE_URL); authorizeUrl.append("?"); authorizeUrl.append("client_id=").append(Constant.API_KEY); authorizeUrl.append("&redirect_uri=").append(Constant.DEFAULT_REDIRECT_URI); authorizeUrl.append("&response_type=").append("token"); authorizeUrl.append("&display=").append("touch"); String scope = TextUtils.join(" ", Constant.HAVE_PERMISSIONS); authorizeUrl.append("&scope=").append(scope); Log.i(TAG, "authorizeUrl = " + authorizeUrl.toString());
c. 使用WebView组件加载授权界面
mWebView.loadUrl(authorizeUrl.toString()); // 以POST方式请求 // mWebView.postUrl(Constant.AUTHORIZE_URL, EncodingUtils.getBytes(sb.toString(), "BASE64")); mWebView.setWebViewClient(new WebViewClient() { public boolean shouldOverrideUrlLoading(WebView webView, String url) { Log.i(TAG, "shouldOverrideUrlLoading() Redirect URL = " + url); if (url.startsWith(Constant.DEFAULT_REDIRECT_URI + "#error=login_denied")) { AuthActivity.this.onBackPressed(); } else if(url.startsWith("http://graph.renren.com/oauth/login_success.html#access_token")) { String accessToken = url.substring(url.indexOf("="), url.indexOf("&")); Log.i(TAG, "accessToken = " + accessToken); AuthActivity.this.onBackPressed(); return false; } webView.loadUrl(url); return true; } public void onReceivedSslError(WebView view, SslErrorHandler handler, android.net.http.SslError errorCode) { // 在默认情况下,通过loadUrl(String url)方法,可以顺利load。 // 但是,当load有ssl层的https页面时,如果这个网站的安全证书在Android无法得到认证,WebView就会变成一个空白页, // 而并不会像PC浏览器中那样跳出一个风险提示框。因此,我们必须针对这种情况进行处理。(这个证书限于2.1版本以上的Android 系统才可以) // 默认的处理方式,WebView变成空白页 // handler.cancel(); // 接受证书 handler.proceed(); } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { super.onReceivedError(view, errorCode, description, failingUrl); AuthActivity.this.onBackPressed(); } public void onPageStarted(WebView view, String url, Bitmap favicon) { Log.i(TAG, "Webview loading URL: " + url); super.onPageStarted(view, url, favicon); } public void onPageFinished(WebView view, String url) { Log.i(TAG, "onPageFinished() url = " + url); super.onPageFinished(view, url); } });
d. Back键事件处理
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (mWebView != null) { mWebView.stopLoading(); } } return super.onKeyDown(keyCode, event); }
应用授权界面,完整代码:
package com.everyone.android.ui;import android.graphics.Bitmap;import android.os.Bundle;import android.text.TextUtils;import android.util.Log;import android.view.KeyEvent;import android.view.View;import android.webkit.SslErrorHandler;import android.webkit.WebView;import android.webkit.WebViewClient;import com.everyone.android.AppBaseActivity;import com.everyone.android.R;import com.everyone.android.utils.Constant;import com.everyone.android.widget.TopNavbar;/** * 功能描述:应用授权界面 * @author android_ls */public class AuthActivity extends AppBaseActivity { /** * 打印Log的标签 */ private static final String TAG = "AuthActivity"; private WebView mWebView; private TopNavbar mTopNavbar; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 顶部返回按钮事件处理 mTopNavbar.llBack.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onBackPressed(); } }); } @Override protected void setupView() { mTopNavbar = (TopNavbar) findViewById(R.id.rl_top_navbar); mWebView = (WebView) findViewById(R.id.wv_auth); mWebView.setVerticalScrollBarEnabled(false); mWebView.setHorizontalScrollBarEnabled(false); mWebView.getSettings().setJavaScriptEnabled(true); } @Override protected int getLayoutId() { return R.layout.auth; } @Override protected void initializedData() { // 组拼请求参数 StringBuilder authorizeUrl = new StringBuilder(Constant.AUTHORIZE_URL); authorizeUrl.append("?"); authorizeUrl.append("client_id=").append(Constant.API_KEY); authorizeUrl.append("&redirect_uri=").append(Constant.DEFAULT_REDIRECT_URI); authorizeUrl.append("&response_type=").append("token"); authorizeUrl.append("&display=").append("touch"); String scope = TextUtils.join(" ", Constant.HAVE_PERMISSIONS); authorizeUrl.append("&scope=").append(scope); Log.i(TAG, "authorizeUrl = " + authorizeUrl.toString()); mWebView.loadUrl(authorizeUrl.toString()); // 以POST方式请求 // mWebView.postUrl(Constant.AUTHORIZE_URL, EncodingUtils.getBytes(sb.toString(), "BASE64")); mWebView.setWebViewClient(new WebViewClient() { public boolean shouldOverrideUrlLoading(WebView webView, String url) { Log.i(TAG, "shouldOverrideUrlLoading() Redirect URL = " + url); if (url.startsWith(Constant.DEFAULT_REDIRECT_URI + "#error=login_denied")) { AuthActivity.this.onBackPressed(); } else if(url.startsWith("http://graph.renren.com/oauth/login_success.html#access_token")) { String accessToken = url.substring(url.indexOf("="), url.indexOf("&")); Log.i(TAG, "accessToken = " + accessToken); AuthActivity.this.onBackPressed(); return false; } webView.loadUrl(url); return true; } public void onReceivedSslError(WebView view, SslErrorHandler handler, android.net.http.SslError errorCode) { // 在默认情况下,通过loadUrl(String url)方法,可以顺利load。 // 但是,当load有ssl层的https页面时,如果这个网站的安全证书在Android无法得到认证,WebView就会变成一个空白页, // 而并不会像PC浏览器中那样跳出一个风险提示框。因此,我们必须针对这种情况进行处理。(这个证书限于2.1版本以上的Android 系统才可以) // 默认的处理方式,WebView变成空白页 // handler.cancel(); // 接受证书 handler.proceed(); } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { super.onReceivedError(view, errorCode, description, failingUrl); AuthActivity.this.onBackPressed(); } public void onPageStarted(WebView view, String url, Bitmap favicon) { Log.i(TAG, "Webview loading URL: " + url); super.onPageStarted(view, url, favicon); } public void onPageFinished(WebView view, String url) { Log.i(TAG, "onPageFinished() url = " + url); super.onPageFinished(view, url); } }); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (mWebView != null) { mWebView.stopLoading(); } } return super.onKeyDown(keyCode, event); } }
TopNavbar类:
package com.everyone.android.widget;import android.content.Context;import android.util.AttributeSet;import android.view.LayoutInflater;import android.widget.FrameLayout;import android.widget.LinearLayout;import android.widget.RelativeLayout;import android.widget.TextView;import com.everyone.android.R;/** * 功能描述:自定义顶部工具栏 * @author android_ls * 创建日期:2013-03-26 */public class TopNavbar extends FrameLayout { public TextView tvTitle; public LinearLayout llBack; public TopNavbar(Context context) { super(context); setupViews(); } public TopNavbar(Context context, AttributeSet attrs) { super(context, attrs); setupViews(); } private void setupViews() { final LayoutInflater mLayoutInflater = LayoutInflater.from(getContext()); RelativeLayout rlTopNavbar = (RelativeLayout) mLayoutInflater.inflate(R.layout.top_navbar, null); addView(rlTopNavbar); llBack = (LinearLayout) rlTopNavbar.findViewById(R.id.ll_back); tvTitle = (TextView) rlTopNavbar.findViewById(R.id.tv_title); }}
常量类:
package com.everyone.android.utils;/** * 功能描述:常量类 * @author android_ls */public class Constant { /** * 人人登录和授权的地址 */ public static final String AUTHORIZE_URL = "https://graph.renren.com/oauth/authorize"; /** * 默认重定向URL */ public static final String DEFAULT_REDIRECT_URI = "http://graph.renren.com/oauth/login_success.html"; /** * 第三方应用所拥有的权限 */ public static final String[] HAVE_PERMISSIONS = { "publish_feed", "create_album", "photo_upload", "read_user_album", "status_update", "read_user_blog", "read_user_checkin", "read_user_feed", "read_user_guestbook", "read_user_invitation", "read_user_like_history", "read_user_message", "read_user_notification", "read_user_photo", "read_user_status", "read_user_comment", "read_user_share", "read_user_request", "publish_blog", "publish_checkin", "publish_feed", "publish_share", "write_guestbook", "send_invitation", "send_request", "send_message", "send_notification", "photo_upload", "create_album", "publish_comment", "operate_like", "admin_page" }; /** * API_KEY */ public static final String API_KEY = "661ea1ba2d6b49859be197d77fe361f1"; /** * Secret Key */ public static final String SECRET_KEY = "a088d31cd5d341819bfc75ac0208b5e1"; /** * 应用ID */ public static final String APP_ID = "195789"; }三、运行效果图: