673 lines
27 KiB
Java
673 lines
27 KiB
Java
package com.gh.common.util;
|
||
|
||
import android.app.Dialog;
|
||
import android.content.Context;
|
||
import android.content.SharedPreferences;
|
||
import android.preference.PreferenceManager;
|
||
import android.text.TextUtils;
|
||
|
||
import com.gh.gamecenter.entity.LoginResponseEntity;
|
||
import com.gh.gamecenter.entity.UserInfoEntity;
|
||
import com.gh.gamecenter.retrofit.JSONObjectResponse;
|
||
import com.gh.gamecenter.retrofit.Response;
|
||
import com.gh.gamecenter.retrofit.RetrofitManager;
|
||
import com.google.gson.Gson;
|
||
import com.google.gson.reflect.TypeToken;
|
||
import com.lightgame.utils.Utils;
|
||
|
||
import org.json.JSONException;
|
||
import org.json.JSONObject;
|
||
|
||
import java.lang.reflect.Type;
|
||
import java.util.HashMap;
|
||
import java.util.Map;
|
||
|
||
import okhttp3.MediaType;
|
||
import okhttp3.RequestBody;
|
||
import okhttp3.ResponseBody;
|
||
import retrofit2.HttpException;
|
||
import rx.Observable;
|
||
import rx.android.schedulers.AndroidSchedulers;
|
||
import rx.schedulers.Schedulers;
|
||
|
||
/**
|
||
* Created by khy on 7/07/17.
|
||
*/
|
||
|
||
public class LoginUtils {
|
||
|
||
public enum LoginTag {
|
||
qq, wechat, weibo, phone, refresh, oldUserPhone
|
||
}
|
||
|
||
public static void checkPhoneNum(final Context context, final String phoneName, final onCaptchaCallBackListener listener) { // 老用户登录检查手机是否登录过
|
||
RetrofitManager.getApi()
|
||
.checkPhoneNum(phoneName)
|
||
.subscribeOn(Schedulers.io())
|
||
.observeOn(AndroidSchedulers.mainThread())
|
||
.subscribe(new Response<ResponseBody>() {
|
||
@Override
|
||
public void onResponse(ResponseBody response) {
|
||
super.onResponse(response);
|
||
try {
|
||
JSONObject content = new JSONObject(response.string());
|
||
String status = content.getString("status");
|
||
if ("ok".equals(status)) {
|
||
getPhoneCaptcha(context, phoneName, listener);
|
||
} else {
|
||
DialogUtils.showWarningDialog(context, null, "手机号已存在,请使用未登录过的手机号,以保证数据正常同步到新账号上"
|
||
, null, "我知道了", null, null);
|
||
}
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(HttpException e) {
|
||
super.onFailure(e);
|
||
if (e == null) {
|
||
Utils.toast(context, "请检查网络是否可用");
|
||
return;
|
||
}
|
||
try {
|
||
ResponseBody responseBody = e.response().errorBody();
|
||
String string = responseBody.string();
|
||
JSONObject content = new JSONObject(string);
|
||
int code = content.getInt("code");
|
||
outputErrorHint(context, code);
|
||
} catch (Exception e1) {
|
||
e1.printStackTrace();
|
||
}
|
||
}
|
||
});
|
||
|
||
}
|
||
|
||
// 获取验证码
|
||
public static void getPhoneCaptcha(final Context context, String phoneNum, final onCaptchaCallBackListener listener) {
|
||
Map<String, String> params = new HashMap<>();
|
||
params.put("mobile", phoneNum);
|
||
RequestBody body = RequestBody.create(MediaType.parse("application/json"),
|
||
new JSONObject(params).toString());
|
||
RetrofitManager
|
||
.getUsersea()
|
||
.loginByCaptcha(body)
|
||
.subscribeOn(Schedulers.io())
|
||
.observeOn(AndroidSchedulers.mainThread())
|
||
.subscribe(new JSONObjectResponse() {
|
||
@Override
|
||
public void onResponse(JSONObject response) {
|
||
super.onResponse(response);
|
||
try {
|
||
listener.onCaptcha(response.getString("service_id"));
|
||
} catch (JSONException e) {
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(HttpException e) {
|
||
super.onFailure(e);
|
||
if (e == null) {
|
||
Utils.toast(context, "请检查网络是否可用");
|
||
return;
|
||
}
|
||
try {
|
||
ResponseBody responseBody = e.response().errorBody();
|
||
String string = responseBody.string();
|
||
JSONObject content = new JSONObject(string);
|
||
int code = content.getInt("code");
|
||
outputErrorHint(context, code);
|
||
} catch (Exception e1) {
|
||
e1.printStackTrace();
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
|
||
// 登录前,做好body判断
|
||
public static void login(final Context context, final JSONObject content, final LoginTag loginTag,
|
||
final onLoginCallBackListener listener) {
|
||
try {
|
||
JSONObject device = DeviceUtils.getLoginDevice(context.getApplicationContext());
|
||
content.put("device", device);
|
||
} catch (JSONException e) {
|
||
e.printStackTrace();
|
||
}
|
||
|
||
RequestBody body = RequestBody.create(MediaType.parse("application/json"), content.toString());
|
||
|
||
Observable<LoginResponseEntity> observable;
|
||
|
||
if (loginTag == LoginTag.weibo) {
|
||
observable = RetrofitManager.getUsersea().loginByWeibo(body);
|
||
} else if (loginTag == LoginTag.qq) {
|
||
observable = RetrofitManager.getUsersea().loginByQQ(body);
|
||
} else if (loginTag == LoginTag.wechat) {
|
||
observable = RetrofitManager.getUsersea().loginByWechat(body);
|
||
} else if (loginTag == LoginTag.phone || loginTag == LoginTag.oldUserPhone) {
|
||
observable = RetrofitManager.getUsersea().loginByMobile(body);
|
||
} else if (loginTag == LoginTag.refresh) {
|
||
observable = RetrofitManager.getUsersea().refreshToken(body);
|
||
} else {
|
||
return;
|
||
}
|
||
|
||
observable
|
||
.subscribeOn(Schedulers.io())
|
||
.observeOn(AndroidSchedulers.mainThread())
|
||
.subscribe(new Response<LoginResponseEntity>() {
|
||
@Override
|
||
public void onResponse(LoginResponseEntity response) {
|
||
super.onResponse(response);
|
||
if (loginTag.equals(LoginTag.refresh)) {
|
||
LoginResponseEntity loginToken = getLoginToken(context);
|
||
if (loginToken != null) {
|
||
response.setLoginType(loginToken.getLoginType());
|
||
}
|
||
} else {
|
||
if (loginTag.equals(LoginTag.phone) || loginTag.equals(LoginTag.oldUserPhone)) {
|
||
try {
|
||
response.setLoginType(content.getString("mobile"));
|
||
} catch (JSONException e) {
|
||
e.printStackTrace();
|
||
}
|
||
} else {
|
||
response.setLoginType(loginTag.name());
|
||
}
|
||
|
||
}
|
||
saveLoginToken(context, response);
|
||
|
||
|
||
String token = response.getAccessToken().getValue();
|
||
|
||
if (loginTag == LoginTag.oldUserPhone) {
|
||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||
String syncDeviceID = sp.getString("syncDeviceID", null);
|
||
if (!TextUtils.isEmpty(syncDeviceID)) {
|
||
syncUserData(context, syncDeviceID, token, listener, loginTag);
|
||
return;
|
||
}
|
||
}
|
||
getUserData(context, token, listener, loginTag);
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(HttpException e) {
|
||
super.onFailure(e);
|
||
if (e == null) {
|
||
Utils.toast(context, "请检查网络是否可用");
|
||
return;
|
||
}
|
||
|
||
try {
|
||
ResponseBody responseBody = e.response().errorBody();
|
||
String string = responseBody.string();
|
||
JSONObject content = new JSONObject(string);
|
||
int code = content.getInt("code");
|
||
|
||
if (loginTag == LoginTag.refresh && code == 40802) {
|
||
|
||
Utils.log("=======40802::" + string);
|
||
JSONObject device = content.getJSONObject("device");
|
||
String manufacturer = device.getString("manufacturer");
|
||
|
||
String model = device.getString("model");
|
||
|
||
DialogUtils.showAlertDialog(context, "你的账号已在另外一台设备登录"
|
||
, StringUtils.buildString("(", manufacturer, "-", model, ")")
|
||
, "知道了", null, null, null);
|
||
|
||
LoginUtils.cleanUserData(context);
|
||
// TODO 要不要调用退出登录接口
|
||
} else {
|
||
outputErrorHint(context, code);
|
||
}
|
||
} catch (Exception e1) {
|
||
e1.printStackTrace();
|
||
}
|
||
cleanUserData(context);
|
||
}
|
||
});
|
||
}
|
||
|
||
private static void syncUserData(final Context context, String syncDeviceID, final String token
|
||
, final onLoginCallBackListener listener, final LoginTag loginTag) {
|
||
|
||
HashMap<String, String> map = new HashMap<>();
|
||
map.put("device_id", syncDeviceID);
|
||
RequestBody body = RequestBody.create(MediaType.parse("application/json"), new JSONObject(map).toString());
|
||
RetrofitManager.getApi().syncUserData(token, body)
|
||
.subscribeOn(Schedulers.io())
|
||
.observeOn(AndroidSchedulers.mainThread())
|
||
.subscribe(new Response<ResponseBody>() {
|
||
@Override
|
||
public void onResponse(ResponseBody response) {
|
||
super.onResponse(response);
|
||
getUserData(context, token, listener, loginTag);
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(HttpException e) {
|
||
super.onFailure(e);
|
||
if (e == null) {
|
||
Utils.toast(context, "请检查网络是否可用");
|
||
return;
|
||
}
|
||
try {
|
||
ResponseBody responseBody = e.response().errorBody();
|
||
String string = responseBody.string();
|
||
JSONObject content = new JSONObject(string);
|
||
int code = content.getInt("code");
|
||
outputErrorHint(context, code);
|
||
} catch (Exception e1) {
|
||
e1.printStackTrace();
|
||
}
|
||
getUserData(context, token, listener, loginTag);
|
||
}
|
||
});
|
||
}
|
||
|
||
// 注销登录
|
||
public static void logout(final Context context) {
|
||
LoginResponseEntity loginToken = getLoginToken(context);
|
||
if (loginToken == null || loginToken.getAccessToken() == null) return;
|
||
|
||
RetrofitManager
|
||
.getUsersea()
|
||
.logout(loginToken.getAccessToken().getValue())
|
||
.subscribeOn(Schedulers.io())
|
||
.observeOn(AndroidSchedulers.mainThread())
|
||
.subscribe(new Response<ResponseBody>() {
|
||
@Override
|
||
public void onFailure(HttpException e) {
|
||
super.onFailure(e);
|
||
if (e == null) {
|
||
Utils.toast(context, "请检查网络是否可用");
|
||
return;
|
||
}
|
||
try {
|
||
ResponseBody responseBody = e.response().errorBody();
|
||
String string = responseBody.string();
|
||
JSONObject content = new JSONObject(string);
|
||
int code = content.getInt("code");
|
||
outputErrorHint(context, code);
|
||
} catch (Exception e1) {
|
||
e1.printStackTrace();
|
||
}
|
||
}
|
||
});
|
||
|
||
cleanUserData(context);
|
||
}
|
||
|
||
// 清除本地用户相关信息
|
||
public static void cleanUserData(Context context) {
|
||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||
SharedPreferences.Editor edit = sp.edit();
|
||
edit.putString("user_info", null);
|
||
edit.putString("login_token", null);
|
||
edit.apply();
|
||
|
||
GetLoginDataUtils.getInstance(context).QQLogout();
|
||
}
|
||
|
||
// 检查本地是否有缓存token,有则马上登录
|
||
public static void checkLogin(Context context, onLoginCallBackListener listener) {
|
||
LoginResponseEntity loginToken = getLoginToken(context);
|
||
|
||
if (loginToken != null && loginToken.getAccessToken() != null) {
|
||
LoginResponseEntity.AccessToken accessToken = loginToken.getAccessToken();
|
||
Long accessExpire = accessToken.getExpire();
|
||
if (accessExpire != null && accessExpire > Utils.getTime(context)) {
|
||
getUserData(context, accessToken.getValue(), listener, null);
|
||
} else {
|
||
LoginResponseEntity.RefreshToken refreshToken = loginToken.getRefreshToken();
|
||
Long refreshExpire = refreshToken.getExpire();
|
||
if (refreshExpire != null && refreshExpire > Utils.getTime(context)) {
|
||
Map<String, String> params = new HashMap<>();
|
||
params.put("refresh_token", refreshToken.getValue());
|
||
login(context, new JSONObject(params), LoginTag.refresh, listener); // 刷新accessToken
|
||
} else {
|
||
// 重新登录
|
||
cleanUserData(context);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 获取用户信息
|
||
public static void getUserData(final Context context, String token, final onLoginCallBackListener listener, final LoginTag loginTag) {
|
||
RetrofitManager
|
||
.getApi()
|
||
.getUserInfo(token)
|
||
.subscribeOn(Schedulers.io())
|
||
.observeOn(AndroidSchedulers.mainThread())
|
||
.subscribe(new Response<UserInfoEntity>() {
|
||
@Override
|
||
public void onResponse(UserInfoEntity response) {
|
||
super.onResponse(response);
|
||
saveUserInfo(context, response);
|
||
if (listener != null) {
|
||
listener.onLogin(response, loginTag);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(HttpException e) {
|
||
super.onFailure(e);
|
||
if (e == null) {
|
||
Utils.toast(context, "请检查网络是否可用");
|
||
return;
|
||
}
|
||
try {
|
||
ResponseBody responseBody = e.response().errorBody();
|
||
String string = responseBody.string();
|
||
JSONObject content = new JSONObject(string);
|
||
int code = content.getInt("code");
|
||
outputErrorHint(context, code);
|
||
} catch (Exception e1) {
|
||
e1.printStackTrace();
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// 获取本地缓存用户信息
|
||
public static UserInfoEntity getUserInfo(Context context) {
|
||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||
String loginData = sp.getString("user_info", null);
|
||
if (!TextUtils.isEmpty(loginData)) {
|
||
Type listType = new TypeToken<UserInfoEntity>() {
|
||
}.getType();
|
||
return new Gson().fromJson(loginData, listType);
|
||
}
|
||
return null;
|
||
}
|
||
|
||
|
||
// 在本地缓存用户信息
|
||
public static void saveUserInfo(Context context, UserInfoEntity entity) {
|
||
if (entity != null) {
|
||
String s = new Gson().toJson(entity);
|
||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||
SharedPreferences.Editor edit = sp.edit();
|
||
edit.putString("user_info", s);
|
||
edit.apply();
|
||
}
|
||
}
|
||
|
||
//获取本地缓存用户token
|
||
public static LoginResponseEntity getLoginToken(Context context) {
|
||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||
String loginData = sp.getString("login_token", null);
|
||
if (!TextUtils.isEmpty(loginData)) {
|
||
Type listType = new TypeToken<LoginResponseEntity>() {
|
||
}.getType();
|
||
return new Gson().fromJson(loginData, listType);
|
||
}
|
||
return null;
|
||
}
|
||
|
||
//获取本地缓存用户token
|
||
public static String getToken(Context context) {
|
||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||
String loginData = sp.getString("login_token", null);
|
||
if (!TextUtils.isEmpty(loginData)) {
|
||
Type listType = new TypeToken<LoginResponseEntity>() {
|
||
}.getType();
|
||
LoginResponseEntity entity = new Gson().fromJson(loginData, listType);
|
||
if (entity != null && entity.getAccessToken() != null) {
|
||
return entity.getAccessToken().getValue();
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
// 在本地缓存用户token
|
||
public static void saveLoginToken(Context context, LoginResponseEntity entity) {
|
||
if (entity != null) {
|
||
if (TextUtils.isEmpty(entity.getLoginType())) {
|
||
LoginResponseEntity loginToken = getLoginToken(context);
|
||
if (loginToken != null) {
|
||
entity.setLoginType(loginToken.getLoginType());
|
||
}
|
||
}
|
||
|
||
String s = new Gson().toJson(entity);
|
||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||
SharedPreferences.Editor edit = sp.edit();
|
||
edit.putString("login_token", s);
|
||
edit.apply();
|
||
}
|
||
}
|
||
|
||
//更改用户信息
|
||
public static void changeUserInfo(final onChangeUserInfoListener listener, final Context context, String content, String editType) {
|
||
final UserInfoEntity entity = getUserInfo(context);
|
||
if (entity == null) {
|
||
return;
|
||
}
|
||
|
||
switch (editType) {
|
||
case "nickName":
|
||
entity.setName(content);
|
||
break;
|
||
case "contact":
|
||
entity.setContact(content);
|
||
break;
|
||
case "sex":
|
||
entity.setGender(content);
|
||
break;
|
||
case "area":
|
||
entity.setRegion(content);
|
||
break;
|
||
case "userIcon":
|
||
entity.setIcon(content);
|
||
break;
|
||
default:
|
||
return;
|
||
}
|
||
|
||
final Dialog loadingDialog = DialogUtils.showWaitDialog(context, "正在修改信息...");
|
||
|
||
LoginResponseEntity loginToken = getLoginToken(context);
|
||
if (loginToken == null || loginToken.getAccessToken() == null) return;
|
||
|
||
RequestBody body = RequestBody.create(MediaType.parse("application/json"),
|
||
new Gson().toJson(entity));
|
||
RetrofitManager
|
||
.getUsersea()
|
||
.changeUserInfo(loginToken.getAccessToken().getValue(), body)
|
||
.subscribeOn(Schedulers.io())
|
||
.observeOn(AndroidSchedulers.mainThread())
|
||
.subscribe(new Response<ResponseBody>() {
|
||
@Override
|
||
public void onResponse(ResponseBody response) {
|
||
super.onResponse(response);
|
||
if (loadingDialog != null) {
|
||
loadingDialog.dismiss();
|
||
}
|
||
|
||
saveUserInfo(context, entity);
|
||
listener.onChange();
|
||
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(HttpException e) {
|
||
super.onFailure(e);
|
||
if (loadingDialog != null) {
|
||
loadingDialog.dismiss();
|
||
}
|
||
Utils.toast(context, "修改失败");
|
||
|
||
if (e == null) {
|
||
Utils.toast(context, "请检查网络是否可用");
|
||
return;
|
||
}
|
||
try {
|
||
ResponseBody responseBody = e.response().errorBody();
|
||
String string = responseBody.string();
|
||
JSONObject content = new JSONObject(string);
|
||
int code = content.getInt("code");
|
||
outputErrorHint(context, code);
|
||
} catch (Exception e1) {
|
||
e1.printStackTrace();
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
private static void outputErrorHint(Context context, int code) {
|
||
|
||
switch (code) {
|
||
case 40000:
|
||
Utils.toast(context, "参数不全");
|
||
break;
|
||
case 40001:
|
||
Utils.toast(context, "已经发送过短信");
|
||
break;
|
||
case 40002:
|
||
Utils.toast(context, "请求第三方开放平台时发生错误");
|
||
break;
|
||
case 40003:
|
||
Utils.toast(context, "上传用户头像时发生错误");
|
||
break;
|
||
case 40101:
|
||
Utils.toast(context, "缺少参数 app_id");
|
||
break;
|
||
case 40102:
|
||
Utils.toast(context, "缺少签名验证的头信息");
|
||
break;
|
||
case 40104:
|
||
Utils.toast(context, "缺少token");
|
||
break;
|
||
case 40105:
|
||
Utils.toast(context, "缺少手机号码");
|
||
break;
|
||
case 40106:
|
||
Utils.toast(context, "缺少用户名");
|
||
break;
|
||
case 40107:
|
||
Utils.toast(context, "缺少密码参数");
|
||
break;
|
||
case 40202:
|
||
Utils.toast(context, "无效的手机号码");
|
||
break;
|
||
case 40203:
|
||
Utils.toast(context, "无效的用户名");
|
||
break;
|
||
case 40204:
|
||
Utils.toast(context, "无效的头像地址");
|
||
break;
|
||
case 40205:
|
||
Utils.toast(context, "无效的性别参数");
|
||
break;
|
||
case 40206:
|
||
Utils.toast(context, "无效的地区参数");
|
||
break;
|
||
case 40208:
|
||
Utils.toast(context, "无效的密码");
|
||
break;
|
||
case 40209:
|
||
Utils.toast(context, "无效的URL 地址");
|
||
break;
|
||
case 42000:
|
||
Utils.toast(context, "无效的app_id");
|
||
break;
|
||
case 42001:
|
||
Utils.toast(context, "无效的app_secret");
|
||
break;
|
||
case 42002:
|
||
Utils.toast(context, "无效的Union_id");
|
||
break;
|
||
case 42003:
|
||
Utils.toast(context, "无效的设备信息");
|
||
break;
|
||
case 42004:
|
||
Utils.toast(context, "无效的请求");
|
||
break;
|
||
case 40301:
|
||
Utils.toast(context, "签名验证失败");
|
||
break;
|
||
case 40302:
|
||
Utils.toast(context, "验证码错误");
|
||
break;
|
||
case 40303:
|
||
Utils.toast(context, "密码错误");
|
||
break;
|
||
case 40304:
|
||
Utils.toast(context, "不支持该种方式登录");
|
||
break;
|
||
case 40305:
|
||
Utils.toast(context, "错误的状态值(应用只有两种状态: working / stop)");
|
||
break;
|
||
case 40306:
|
||
Utils.toast(context, "传递了无法识别的参数");
|
||
break;
|
||
case 40401:
|
||
Utils.toast(context, "token过期");
|
||
break;
|
||
case 40402:
|
||
Utils.toast(context, "Service_id过期,主要原因是:收到手机短信验证码后长时间没有进行登录操作");
|
||
break;
|
||
case 40403:
|
||
Utils.toast(context, "验证码已过期");
|
||
break;
|
||
case 40501:
|
||
Utils.toast(context, "同名应用已经存在");
|
||
break;
|
||
case 40502:
|
||
Utils.toast(context, "用户名已存在");
|
||
break;
|
||
case 40503:
|
||
Utils.toast(context, "名称已经存在");
|
||
break;
|
||
case 40601:
|
||
Utils.toast(context, "应用不存在");
|
||
break;
|
||
case 40602:
|
||
Utils.toast(context, "用户不存在");
|
||
break;
|
||
case 40603:
|
||
Utils.toast(context, "用户系统不存在");
|
||
break;
|
||
case 40604:
|
||
Utils.toast(context, "用户已被冻结");
|
||
break;
|
||
case 40605:
|
||
Utils.toast(context, "用户没有冻结");
|
||
break;
|
||
case 40606:
|
||
Utils.toast(context, "该应用被停止运行了");
|
||
break;
|
||
case 40801:
|
||
Utils.toast(context, "访问过于频繁");
|
||
break;
|
||
default:
|
||
Utils.toast(context, "未知错误");
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
// 更改用户信息回调
|
||
public interface onChangeUserInfoListener {
|
||
void onChange();
|
||
}
|
||
|
||
// 获取验证码回调
|
||
public interface onCaptchaCallBackListener {
|
||
void onCaptcha(String serviceId);
|
||
}
|
||
|
||
// 登录回调
|
||
public interface onLoginCallBackListener {
|
||
void onLogin(UserInfoEntity entity, LoginTag loginTag);
|
||
}
|
||
}
|