/* * Copyright (C) 2010-2013 The SINA WEIBO Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sina.weibo.sdk.widget; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.graphics.Typeface; import android.os.Bundle; import android.text.TextUtils; import android.util.AttributeSet; import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import com.sina.weibo.sdksupport.R; import com.sina.weibo.sdk.auth.AuthInfo; import com.sina.weibo.sdk.auth.Oauth2AccessToken; import com.sina.weibo.sdk.auth.WeiboAuthListener; import com.sina.weibo.sdk.auth.sso.SsoHandler; import com.sina.weibo.sdk.exception.WeiboException; import com.sina.weibo.sdk.net.RequestListener; import com.sina.weibo.sdk.openapi.LogoutAPI; import com.sina.weibo.sdk.utils.LogUtil; /** * 该类提供了一个简单的登录/注销控件。 * 该控件提供内置的登录(SSO 登陆授权)和注销功能,它有两种样式(蓝色和银色)。 * 注意:使用者可以自行修改 /res/values/styles.xml 文件中的样式。 * * @author SINA * @since 2013-11-04 */ public class LoginoutButton extends Button implements OnClickListener { private static final String TAG = "LoginButton"; /** 微博授权时,启动 SSO 界面的 Activity */ private Context mContext; /** 授权认证所需要的信息 */ private AuthInfo mAuthInfo; /** SSO 授权认证实例 */ private SsoHandler mSsoHandler; /** 微博授权认证回调 */ private WeiboAuthListener mAuthListener; /** Access Token 实例 */ private Oauth2AccessToken mAccessToken; /** 注销回调 */ private RequestListener mLogoutListener; /** 点击 Button 时,额外的 Listener */ private OnClickListener mExternalOnClickListener; /** * 创建一个登录/注销按钮。 * * @see View#View(Context) */ public LoginoutButton(Context context) { this(context, null); } /** * 从 XML 配置文件中创建一个登录/注销按钮。 * * @see View#View(Context, AttributeSet) */ public LoginoutButton(Context context, AttributeSet attrs) { this(context, attrs, /*R.style.com_sina_weibo_sdk_loginview_default_style*/0); } /** * 从 XML 配置文件以及样式中创建一个登录/注销按钮。 * * @see View#View(Context, AttributeSet, int) */ public LoginoutButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initialize(context, attrs); } /** * 设置微博授权所需信息以及回调函数。 * * @param authInfo 用于保存授权认证所需要的信息 * @param authListener 微博授权认证回调接口 */ public void setWeiboAuthInfo(AuthInfo authInfo, WeiboAuthListener authListener) { mAuthInfo = authInfo; mAuthListener = authListener; } /** * 设置微博授权所需信息。 * * @param appKey 第三方应用的 APP_KEY * @param redirectUrl 第三方应用的回调页 * @param scope 第三方应用申请的权限 * @param authListener 微博授权认证回调接口 */ public void setWeiboAuthInfo(String appKey, String redirectUrl, String scope, WeiboAuthListener authListener) { mAuthInfo = new AuthInfo(mContext, appKey, redirectUrl, scope); mAuthListener = authListener; } /** * 设置注销时,需要设置的 Token 信息以及注销后的回调接口。 * * @param accessToken AccessToken 信息 * @param logoutListener 注销回调 */ public void setLogoutInfo(Oauth2AccessToken accessToken, RequestListener logoutListener) { mAccessToken = accessToken; mLogoutListener = logoutListener; if (mAccessToken != null && mAccessToken.isSessionValid()) { setText(R.string.com_sina_weibo_sdk_logout); } } /** * 设置注销回调。 * * @param logoutListener 注销回调 */ public void setLogoutListener(RequestListener logoutListener) { mLogoutListener = logoutListener; } /** * 设置一个额外的 Button 点击时的 Listener。 * 当触发 Button 点击事件时,会先调用该 Listener,给使用者一个可访问的机会, * 然后再调用内部默认的处理。 * <p><b>注意:一般情况下,使用者不需要调用该方法,除非有其它必要性。<b></p> * * @param listener Button 点击时的 Listener */ public void setExternalOnClickListener(OnClickListener l) { mExternalOnClickListener = l; } /** * 使用该控件进行授权登陆时,需要手动调用该函数。 * <p> * 重要:使用该控件的 Activity 必须重写 {@link Activity#onActivityResult(int, int, Intent)}, * 并在内部调用该函数,否则无法授权成功。</p> * <p>Sample Code:</p> * <pre class="prettyprint"> * protected void onActivityResult(int requestCode, int resultCode, Intent data) { * super.onActivityResult(requestCode, resultCode, data); * * // 在此处调用 * mLoginoutButton.onActivityResult(requestCode, resultCode, data); * } * </pre> * @param requestCode 请查看 {@link Activity#onActivityResult(int, int, Intent)} * @param resultCode 请查看 {@link Activity#onActivityResult(int, int, Intent)} * @param data 请查看 {@link Activity#onActivityResult(int, int, Intent)} */ public void onActivityResult(int requestCode, int resultCode, Intent data) { if (mSsoHandler != null) { mSsoHandler.authorizeCallBack(requestCode, resultCode, data); } } /** * 按钮被点击时,调用该函数。 */ @Override public void onClick(View v) { // Give a chance to external listener if (mExternalOnClickListener != null) { mExternalOnClickListener.onClick(v); } if (mAccessToken != null && mAccessToken.isSessionValid()) { logout(); } else { login(); } } /** * 初始化函数。 * * @param context 上下文环境,一般为放置该 Button 的 Activity * @param attrs XML 属性集合对象 */ private void initialize(Context context, AttributeSet attrs) { mContext = context; this.setOnClickListener(this); // 如果布局文件中未设置 style,加载默认的 style loadDefaultStyle(attrs); } /** * 加载默认的样式(蓝色)。 * * @param attrs XML 属性集合对象 */ private void loadDefaultStyle(AttributeSet attrs) { if (attrs != null && 0 == attrs.getStyleAttribute()) { Resources res = getResources(); this.setBackgroundResource(R.drawable.com_sina_weibo_sdk_button_blue); this.setPadding(res.getDimensionPixelSize(R.dimen.com_sina_weibo_sdk_loginview_padding_left), res.getDimensionPixelSize(R.dimen.com_sina_weibo_sdk_loginview_padding_top), res.getDimensionPixelSize(R.dimen.com_sina_weibo_sdk_loginview_padding_right), res.getDimensionPixelSize(R.dimen.com_sina_weibo_sdk_loginview_padding_bottom)); this.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_com_sina_weibo_sdk_logo, 0, 0, 0); this.setCompoundDrawablePadding( res.getDimensionPixelSize(R.dimen.com_sina_weibo_sdk_loginview_compound_drawable_padding)); this.setTextColor(res.getColor(R.color.com_sina_weibo_sdk_loginview_text_color)); this.setTextSize(TypedValue.COMPLEX_UNIT_PX, res.getDimension(R.dimen.com_sina_weibo_sdk_loginview_text_size)); this.setTypeface(Typeface.DEFAULT_BOLD); this.setGravity(Gravity.CENTER); this.setText(R.string.com_sina_weibo_sdk_login_with_weibo_account); } } /** * 进行 SSO 登陆。如果未安装微博客户端,或 SSO 验证失败,则会跳转到正常的 Web 授权。 */ private void login() { LogUtil.i(TAG, "Click to login"); if (null == mSsoHandler && mAuthInfo != null) { mSsoHandler = new SsoHandler((Activity)mContext, mAuthInfo); } if (mSsoHandler != null) { mSsoHandler.authorize(new WeiboAuthListener() { @Override public void onComplete(Bundle values) { // 从 Bundle 中解析 Token mAccessToken = Oauth2AccessToken.parseAccessToken(values); if (mAccessToken.isSessionValid()) { setText(R.string.com_sina_weibo_sdk_logout); } if (mAuthListener != null) { mAuthListener.onComplete(values); } } @Override public void onCancel() { if (mAuthListener != null) { mAuthListener.onCancel(); } } @Override public void onWeiboException(WeiboException e) { if (mAuthListener != null) { mAuthListener.onWeiboException(e); } } }); } else { LogUtil.e(TAG, "Please setWeiboAuthInfo(...) for first"); } } /** * 调用 {@link LogoutAPI#logout(RequestListener)} 来注销。 */ private void logout() { if (mAccessToken != null && mAccessToken.isSessionValid()) { LogUtil.i(TAG, "Click to logout"); new LogoutAPI(mContext, mAuthInfo.getAppKey(), mAccessToken).logout(new RequestListener() { @Override public void onComplete(String response) { if (!TextUtils.isEmpty(response)) { try { JSONObject obj = new JSONObject(response); if(obj.isNull("error")){ String value = obj.getString("result"); // 注销成功 if ("true".equalsIgnoreCase(value)) { // XXX: 考虑是否需要将 AccessTokenKeeper 放到 SDK 中?? //AccessTokenKeeper.clear(getContext()); // 清空当前 Token mAccessToken = null; setText(R.string.com_sina_weibo_sdk_login_with_weibo_account); } } else { String error_code = obj.getString("error_code"); if(error_code.equals("21317")){ mAccessToken = null; setText(R.string.com_sina_weibo_sdk_login_with_weibo_account); } } } catch (JSONException e) { e.printStackTrace(); } } if (mLogoutListener != null) { mLogoutListener.onComplete(response); } } @Override public void onWeiboException(WeiboException e) { LogUtil.e(TAG, "WeiboException: " + e.getMessage()); // 注销失败 setText(R.string.com_sina_weibo_sdk_logout); if (mLogoutListener != null) { mLogoutListener.onWeiboException(e); } } }); } } }