================================================
FILE: api/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 22
buildToolsVersion "22.0.0"
defaultConfig {
minSdkVersion 11
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.0.0'
compile 'com.google.code.gson:gson:2.3'
compile project(':model')
}
================================================
FILE: api/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/keegan/adt-bundle-mac-x86_64-20140702/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: api/src/androidTest/java/me/keeganlee/kandroid/api/ApplicationTest.java
================================================
package me.keeganlee.kandroid.api;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* Testing Fundamentals
*/
public class ApplicationTest extends ApplicationTestCase {
public ApplicationTest() {
super(Application.class);
}
}
================================================
FILE: api/src/main/AndroidManifest.xml
================================================
================================================
FILE: api/src/main/java/me/keeganlee/kandroid/api/Api.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.api;
import me.keeganlee.kandroid.model.CouponBO;
import java.util.List;
/**
* Api接口
*
* @author Keegan小钢
* @date 15/6/21
* @version 1.0
*/
public interface Api {
// 发送验证码
public final static String SEND_SMS_CODE = "service.sendSmsCode4Register";
// 注册
public final static String REGISTER = "customer.registerByPhone";
// 登录
public final static String LOGIN = "customer.loginByApp";
// 券列表
public final static String LIST_COUPON = "issue.listNewCoupon";
/**
* 发送验证码
*
* @param phoneNum 手机号码
* @return 成功时返回:{ "code": 0, "msg":"success" }
*/
public ApiResponse sendSmsCode4Register(String phoneNum);
/**
* 注册
*
* @param phoneNum 手机号码
* @param code 验证码
* @param password MD5加密的密码
* @return 成功时返回:{ "code": 0, "msg":"success" }
*/
public ApiResponse registerByPhone(String phoneNum, String code, String password);
/**
* 登录
*
* @param loginName 登录名(手机号)
* @param password MD5加密的密码
* @param imei 手机IMEI串号
* @param loginOS Android为1
* @return 成功时返回:{ "code": 0, "msg":"success" }
*/
public ApiResponse loginByApp(String loginName, String password, String imei, int loginOS);
/**
* 券列表
*
* @param currentPage 当前页数
* @param pageSize 每页显示数量
* @return 成功时返回:{ "code": 0, "msg":"success", "objList":[...] }
*/
public ApiResponse> listNewCoupon(int currentPage, int pageSize);
}
================================================
FILE: api/src/main/java/me/keeganlee/kandroid/api/ApiImpl.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.api;
import com.google.gson.reflect.TypeToken;
import me.keeganlee.kandroid.api.net.HttpEngine;
import me.keeganlee.kandroid.api.utils.EncryptUtil;
import me.keeganlee.kandroid.model.CouponBO;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Api实现类
*
* @author Keegan小钢
* @date 15/6/21
* @version 1.0
*/
public class ApiImpl implements Api {
private final static String APP_KEY = "ANDROID_KCOUPON";
private final static String TIME_OUT_EVENT = "CONNECT_TIME_OUT";
private final static String TIME_OUT_EVENT_MSG = "连接服务器失败";
private HttpEngine httpEngine;
public ApiImpl() {
httpEngine = HttpEngine.getInstance();
}
@Override
public ApiResponse sendSmsCode4Register(String phoneNum) {
Map paramMap = new HashMap();
paramMap.put("appKey", APP_KEY);
paramMap.put("method", SEND_SMS_CODE);
paramMap.put("phoneNum", phoneNum);
Type type = new TypeToken>(){}.getType();
try {
return httpEngine.postHandle(paramMap, type);
} catch (IOException e) {
return new ApiResponse(TIME_OUT_EVENT, TIME_OUT_EVENT_MSG);
}
}
@Override
public ApiResponse registerByPhone(String phoneNum, String code, String password) {
Map paramMap = new HashMap();
paramMap.put("appKey", APP_KEY);
paramMap.put("method", REGISTER);
paramMap.put("phoneNum", phoneNum);
paramMap.put("code", code);
paramMap.put("password", EncryptUtil.makeMD5(password));
Type type = new TypeToken>>(){}.getType();
try {
return httpEngine.postHandle(paramMap, type);
} catch (IOException e) {
return new ApiResponse(TIME_OUT_EVENT, TIME_OUT_EVENT_MSG);
}
}
@Override
public ApiResponse loginByApp(String loginName, String password, String imei, int loginOS) {
Map paramMap = new HashMap();
paramMap.put("appKey", APP_KEY);
paramMap.put("method", LOGIN);
paramMap.put("loginName", loginName);
paramMap.put("password", EncryptUtil.makeMD5(password));
paramMap.put("imei", imei);
paramMap.put("loginOS", String.valueOf(loginOS));
Type type = new TypeToken>>(){}.getType();
try {
return httpEngine.postHandle(paramMap, type);
} catch (IOException e) {
return new ApiResponse(TIME_OUT_EVENT, TIME_OUT_EVENT_MSG);
}
}
@Override
public ApiResponse> listNewCoupon(int currentPage, int pageSize) {
Map paramMap = new HashMap();
paramMap.put("appKey", APP_KEY);
paramMap.put("method", LIST_COUPON);
paramMap.put("currentPage", String.valueOf(currentPage));
paramMap.put("pageSize", String.valueOf(pageSize));
Type type = new TypeToken>>(){}.getType();
try {
return httpEngine.postHandle(paramMap, type);
} catch (IOException e) {
return new ApiResponse(TIME_OUT_EVENT, TIME_OUT_EVENT_MSG);
}
}
}
================================================
FILE: api/src/main/java/me/keeganlee/kandroid/api/ApiResponse.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.api;
/**
* Api响应结果的封装类.
*
* @author Keegan小钢
* @date 15/6/21
* @version 1.0
*/
public class ApiResponse {
private String event; // 返回码,0为成功
private String msg; // 返回信息
private T obj; // 单个对象
private T objList; // 数组对象
private int currentPage; // 当前页数
private int pageSize; // 每页显示数量
private int maxCount; // 总条数
private int maxPage; // 总页数
public ApiResponse(String event, String msg) {
this.event = event;
this.msg = msg;
}
public boolean isSuccess() {
return event.equals("0");
}
public String getEvent() {
return event;
}
public void setEvent(String event) {
this.event = event;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
public T getObjList() {
return objList;
}
public void setObjList(T objList) {
this.objList = objList;
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getMaxCount() {
return maxCount;
}
public void setMaxCount(int maxCount) {
this.maxCount = maxCount;
}
public int getMaxPage() {
return maxPage;
}
public void setMaxPage(int maxPage) {
this.maxPage = maxPage;
}
}
================================================
FILE: api/src/main/java/me/keeganlee/kandroid/api/net/HttpEngine.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.api.net;
import android.util.Log;
import com.google.gson.Gson;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;
/**
* Http引擎处理类
*
* @author Keegan小钢
* @date 15/6/21
* @version 1.0
*/
public class HttpEngine {
private final static String TAG = "HttpEngine";
private final static String SERVER_URL = "http://domain.com/platform/api";
private final static String REQUEST_MOTHOD = "POST";
private final static String ENCODE_TYPE = "UTF-8";
private final static int TIME_OUT = 20000;
private static HttpEngine instance = null;
private HttpEngine() {
}
public static HttpEngine getInstance() {
if (instance == null) {
instance = new HttpEngine();
}
return instance;
}
public T postHandle(Map paramsMap, Type typeOfT) throws IOException {
String data = joinParams(paramsMap);
// 打印出请求
Log.i(TAG, "request: " + data);
HttpURLConnection connection = getConnection();
connection.setRequestProperty("Content-Length", String.valueOf(data.getBytes().length));
connection.connect();
OutputStream os = connection.getOutputStream();
os.write(data.getBytes());
os.flush();
if (connection.getResponseCode() == 200) {
// 获取响应的输入流对象
InputStream is = connection.getInputStream();
// 创建字节输出流对象
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 定义读取的长度
int len = 0;
// 定义缓冲区
byte buffer[] = new byte[1024];
// 按照缓冲区的大小,循环读取
while ((len = is.read(buffer)) != -1) {
// 根据读取的长度写入到os对象中
baos.write(buffer, 0, len);
}
// 释放资源
is.close();
baos.close();
connection.disconnect();
// 返回字符串
final String result = new String(baos.toByteArray());
// 打印出结果
Log.i(TAG, "response: " + result);
Gson gson = new Gson();
return gson.fromJson(result, typeOfT);
} else {
connection.disconnect();
return null;
}
}
// 获取connection
private HttpURLConnection getConnection() {
HttpURLConnection connection = null;
// 初始化connection
try {
// 根据地址创建URL对象
URL url = new URL(SERVER_URL);
// 根据URL对象打开链接
connection = (HttpURLConnection) url.openConnection();
// 设置请求的方式
connection.setRequestMethod(REQUEST_MOTHOD);
// 发送POST请求必须设置允许输入,默认为true
connection.setDoInput(true);
// 发送POST请求必须设置允许输出
connection.setDoOutput(true);
// 设置不使用缓存
connection.setUseCaches(false);
// 设置请求的超时时间
connection.setReadTimeout(TIME_OUT);
connection.setConnectTimeout(TIME_OUT);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Connection", "keep-alive");
connection.setRequestProperty("Response-Type", "json");
connection.setChunkedStreamingMode(0);
} catch (IOException e) {
e.printStackTrace();
}
return connection;
}
// 拼接参数列表
private String joinParams(Map paramsMap) {
StringBuilder stringBuilder = new StringBuilder();
for (String key : paramsMap.keySet()) {
stringBuilder.append(key);
stringBuilder.append("=");
try {
stringBuilder.append(URLEncoder.encode(paramsMap.get(key), ENCODE_TYPE));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
stringBuilder.append("&");
}
return stringBuilder.substring(0, stringBuilder.length() - 1);
}
}
================================================
FILE: api/src/main/java/me/keeganlee/kandroid/api/utils/EncryptUtil.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.api.utils;
import java.math.BigInteger;
import java.security.MessageDigest;
/**
* 加密工具类
*
* @author Keegan小钢
* @date 15/6/21
* @version 1.0
*/
public class EncryptUtil {
// MD5加密
public static String makeMD5(String password) {
try {
// 生成一个MD5加密计算摘要
MessageDigest md = MessageDigest.getInstance("MD5");
// 计算md5函数
md.update(password.getBytes());
// digest()最后确定返回md5 hash值,返回值为8为字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
// BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值
return new BigInteger(1, md.digest()).toString(16);
} catch (Exception e) {
e.printStackTrace();
}
return password;
}
}
================================================
FILE: api/src/main/res/values/strings.xml
================================================
api
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/app.iml
================================================
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.0"
defaultConfig {
applicationId "me.keeganlee.kandroid"
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':model')
compile project(':core')
compile 'com.android.support:appcompat-v7:22.0.0'
compile 'com.google.android.gms:play-services:6.1.71'
}
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/keegan/adt-bundle-mac-x86_64-20140702/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: app/src/androidTest/java/me/keeganlee/kandroid/ApplicationTest.java
================================================
package me.keeganlee.kandroid;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* Testing Fundamentals
*/
public class ApplicationTest extends ApplicationTestCase {
public ApplicationTest() {
super(Application.class);
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/me/keeganlee/kandroid/KApplication.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid;
import android.app.Application;
import me.keeganlee.kandroid.core.AppAction;
import me.keeganlee.kandroid.core.AppActionImpl;
/**
* Application类,应用级别的操作都放这里
*
* @version 1.0 创建时间:15/6/25
*/
public class KApplication extends Application {
private AppAction appAction;
@Override
public void onCreate() {
super.onCreate();
appAction = new AppActionImpl(this);
}
public AppAction getAppAction() {
return appAction;
}
}
================================================
FILE: app/src/main/java/me/keeganlee/kandroid/activity/CouponListActivity.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.activity;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.widget.ListView;
import android.widget.Toast;
import me.keeganlee.kandroid.R;
import me.keeganlee.kandroid.adapter.CouponListAdapter;
import me.keeganlee.kandroid.core.ActionCallbackListener;
import me.keeganlee.kandroid.model.CouponBO;
import java.util.List;
/**
* 券列表
*
* @version 1.0 创建时间:15/6/28
*/
public class CouponListActivity extends KBaseActivity implements SwipeRefreshLayout.OnRefreshListener {
private SwipeRefreshLayout swipeRefreshLayout;
private ListView listView;
private CouponListAdapter listAdapter;
private int currentPage = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_coupon_list);
initViews();
getData();
// TODO 添加上拉加载更多的功能
}
private void initViews() {
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
swipeRefreshLayout.setOnRefreshListener(this);
listView = (ListView) findViewById(R.id.list_view);
listAdapter = new CouponListAdapter(this);
listView.setAdapter(listAdapter);
}
private void getData() {
this.appAction.listCoupon(currentPage, new ActionCallbackListener>() {
@Override
public void onSuccess(List data) {
if (!data.isEmpty()) {
if (currentPage == 1) { // 第一页
listAdapter.setItems(data);
} else { // 分页数据
listAdapter.addItems(data);
}
}
swipeRefreshLayout.setRefreshing(false);
}
@Override
public void onFailure(String errorEvent, String message) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
swipeRefreshLayout.setRefreshing(false);
}
});
}
@Override
public void onRefresh() {
// 需要重置当前页为第一页,并且清掉数据
currentPage = 1;
listAdapter.clearItems();
getData();
}
}
================================================
FILE: app/src/main/java/me/keeganlee/kandroid/activity/KBaseActivity.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import me.keeganlee.kandroid.KApplication;
import me.keeganlee.kandroid.core.AppAction;
/**
* Activity抽象基类
*
* @version 1.0 创建时间:15/6/26
*/
public abstract class KBaseActivity extends FragmentActivity {
// 上下文实例
public Context context;
// 应用全局的实例
public KApplication application;
// 核心层的Action实例
public AppAction appAction;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = getApplicationContext();
application = (KApplication) this.getApplication();
appAction = application.getAppAction();
}
}
================================================
FILE: app/src/main/java/me/keeganlee/kandroid/activity/LoginActivity.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import me.keeganlee.kandroid.R;
import me.keeganlee.kandroid.core.ActionCallbackListener;
/**
* 登录
*
* @version 1.0 创建时间:15/6/26
*/
public class LoginActivity extends KBaseActivity {
private EditText phoneEdit;
private EditText passwordEdit;
private Button loginBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
// 初始化View
initViews();
}
// 初始化View
private void initViews() {
phoneEdit = (EditText) findViewById(R.id.edit_phone);
passwordEdit = (EditText) findViewById(R.id.edit_password);
loginBtn = (Button) findViewById(R.id.btn_login);
}
// 准备登录
public void toLogin(View view) {
String loginName = phoneEdit.getText().toString();
String password = passwordEdit.getText().toString();
loginBtn.setEnabled(false);
this.appAction.login(loginName, password, new ActionCallbackListener() {
@Override
public void onSuccess(Void data) {
Toast.makeText(context, R.string.toast_login_success, Toast.LENGTH_SHORT).show();
Intent intent = new Intent(context, CouponListActivity.class);
startActivity(intent);
finish();
}
@Override
public void onFailure(String errorEvent, String message) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
loginBtn.setEnabled(true);
}
});
}
// 进入注册页
public void toRegister(View view) {
Intent intent = new Intent(this, RegisterActivity.class);
startActivity(intent);
}
}
================================================
FILE: app/src/main/java/me/keeganlee/kandroid/activity/RegisterActivity.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import me.keeganlee.kandroid.R;
import me.keeganlee.kandroid.core.ActionCallbackListener;
/**
* 注册
*
* @version 1.0 创建时间:15/6/26
*/
public class RegisterActivity extends KBaseActivity {
private EditText phoneEdit;
private EditText codeEdit;
private EditText passwordEdit;
private Button sendCodeBtn;
private Button registerBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
getActionBar().setDisplayHomeAsUpEnabled(true);
initViews();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == android.R.id.home) {
finish();
}
return super.onOptionsItemSelected(item);
}
private void initViews() {
phoneEdit = (EditText) findViewById(R.id.edit_phone);
codeEdit = (EditText) findViewById(R.id.edit_code);
passwordEdit = (EditText) findViewById(R.id.edit_password);
sendCodeBtn = (Button) findViewById(R.id.btn_send_code);
registerBtn = (Button) findViewById(R.id.btn_register);
}
// 准备发送验证码
public void toSendCode(View view) {
String phoneNum = phoneEdit.getText().toString();
sendCodeBtn.setEnabled(false);
this.appAction.sendSmsCode(phoneNum, new ActionCallbackListener() {
@Override
public void onSuccess(Void data) {
Toast.makeText(context, R.string.toast_code_has_sent, Toast.LENGTH_SHORT).show();
sendCodeBtn.setEnabled(true);
}
@Override
public void onFailure(String errorEvent, String message) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
sendCodeBtn.setEnabled(true);
}
});
}
// 准备注册
public void toRegister(View view) {
String phoneNum = phoneEdit.getText().toString();
String code = codeEdit.getText().toString();
String password = passwordEdit.getText().toString();
registerBtn.setEnabled(false);
this.appAction.register(phoneNum, code, password, new ActionCallbackListener() {
@Override
public void onSuccess(Void data) {
Toast.makeText(context, R.string.toast_register_success, Toast.LENGTH_SHORT).show();
Intent intent = new Intent(context, CouponListActivity.class);
startActivity(intent);
finish();
}
@Override
public void onFailure(String errorEvent, String message) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
registerBtn.setEnabled(true);
}
});
}
}
================================================
FILE: app/src/main/java/me/keeganlee/kandroid/adapter/CouponListAdapter.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.adapter;
import android.content.Context;
import android.text.SpannableString;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import me.keeganlee.kandroid.R;
import me.keeganlee.kandroid.model.CouponBO;
import me.keeganlee.kandroid.util.CouponPriceUtil;
/**
* 券列表的Adapter
*
* @version 1.0 创建时间:15/6/28
*/
public class CouponListAdapter extends KBaseAdapter {
public CouponListAdapter(Context context) {
super(context);
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder holder;
if (view == null) {
view = inflater.inflate(R.layout.item_list_coupon, viewGroup, false);
holder = new ViewHolder();
holder.titleText = (TextView) view.findViewById(R.id.text_item_title);
holder.infoText = (TextView) view.findViewById(R.id.text_item_info);
holder.priceText = (TextView) view.findViewById(R.id.text_item_price);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
CouponBO coupon = itemList.get(i);
holder.titleText.setText(coupon.getName());
holder.infoText.setText(coupon.getIntroduce());
SpannableString priceString;
// 根据不同的券类型展示不同的价格显示方式
switch (coupon.getModelType()) {
default:
case CouponBO.TYPE_CASH:
priceString = CouponPriceUtil.getCashPrice(context, coupon.getFaceValue(), coupon.getEstimateAmount());
break;
case CouponBO.TYPE_DEBIT:
priceString = CouponPriceUtil.getVoucherPrice(context, coupon.getDebitAmount(), coupon.getMiniAmount());
break;
case CouponBO.TYPE_DISCOUNT:
priceString = CouponPriceUtil.getDiscountPrice(context, coupon.getDiscount(), coupon.getMiniAmount());
break;
}
holder.priceText.setText(priceString);
return view;
}
static class ViewHolder {
TextView titleText;
TextView infoText;
TextView priceText;
}
}
================================================
FILE: app/src/main/java/me/keeganlee/kandroid/adapter/KBaseAdapter.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.ArrayList;
import java.util.List;
/**
* Adapter抽象基类
*
* @version 1.0 创建时间:15/6/28
*/
public abstract class KBaseAdapter extends BaseAdapter {
protected Context context;
protected LayoutInflater inflater;
protected List itemList = new ArrayList();
public KBaseAdapter(Context context) {
this.context = context;
inflater = LayoutInflater.from(context);
}
/**
* 判断数据是否为空
*
* @return 为空返回true,不为空返回false
*/
public boolean isEmpty() {
return itemList.isEmpty();
}
/**
* 在原有的数据上添加新数据
*
* @param itemList
*/
public void addItems(List itemList) {
this.itemList.addAll(itemList);
notifyDataSetChanged();
}
/**
* 设置为新的数据,旧数据会被清空
*
* @param itemList
*/
public void setItems(List itemList) {
this.itemList.clear();
this.itemList = itemList;
notifyDataSetChanged();
}
/**
* 清空数据
*/
public void clearItems() {
itemList.clear();
notifyDataSetChanged();
}
@Override
public int getCount() {
return itemList.size();
}
@Override
public Object getItem(int i) {
return itemList.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
abstract public View getView(int i, View view, ViewGroup viewGroup);
}
================================================
FILE: app/src/main/java/me/keeganlee/kandroid/util/CouponPriceUtil.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.util;
import android.content.Context;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.StrikethroughSpan;
import java.text.DecimalFormat;
/**
* 处理券价格的拼接
*
* @version 1.0 创建时间:15/6/28
*/
public class CouponPriceUtil {
/**
* 自动处理double数据,保留非0的2位小数
*/
public static String handleDouble(double price) {
DecimalFormat decimalFormat = new DecimalFormat("##.##");
return decimalFormat.format(price);
}
/**
* sp 转 px
*/
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
/**
* 现金券显示价格样式
*/
public static SpannableString getCashPrice(Context context, double oldPrice, double newPrice) {
StringBuilder builder = new StringBuilder();
builder.append(handleDouble(newPrice)).append("元").append(" ").append(handleDouble(oldPrice)).append("元");
int start = 0;
int middle = builder.indexOf(" ") + 1;
int end = builder.length();
SpannableString string = new SpannableString(builder);
/*改变文字的大小*/
string.setSpan(new AbsoluteSizeSpan(sp2px(context, 20)), start, middle, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
string.setSpan(new AbsoluteSizeSpan(sp2px(context, 14)), middle, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
/*给文字设置删除线*/
string.setSpan(new StrikethroughSpan(), middle, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
/*改变文字的颜色*/
int textOrange = context.getResources().getColor(android.R.color.holo_red_light);
int textGray = context.getResources().getColor(android.R.color.darker_gray);
string.setSpan(new ForegroundColorSpan(textOrange), start, middle, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
string.setSpan(new ForegroundColorSpan(textGray), middle, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return string;
}
/**
* 抵用券显示样式
*/
public static SpannableString getVoucherPrice(Context context, double voucher, double miniAmount) {
StringBuilder builder = new StringBuilder();
int textOrange = context.getResources().getColor(android.R.color.holo_red_light);
int textGray = context.getResources().getColor(android.R.color.darker_gray);
SpannableString string;
if (miniAmount > 0) {
builder.append("满").append(handleDouble(miniAmount)).append("元减").append(handleDouble(voucher)).append("元");
int index = builder.indexOf("元") + 1;
string = new SpannableString(builder);
/*改变文字的颜色*/
int size = string.length();
string.setSpan(new ForegroundColorSpan(textGray), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
string.setSpan(new ForegroundColorSpan(textOrange), 1, index, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
string.setSpan(new ForegroundColorSpan(textGray), index, index + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
string.setSpan(new ForegroundColorSpan(textOrange), index + 1, size, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
builder.append("立减").append(handleDouble(voucher)).append("元");
string = new SpannableString(builder);
/*改变文字的颜色*/
int size = string.length();
string.setSpan(new ForegroundColorSpan(textGray), 0, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
string.setSpan(new ForegroundColorSpan(textOrange), 2, size, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return string;
}
/**
* 折扣券显示样式
*/
public static SpannableString getDiscountPrice(Context context, double discount, double miniAmount) {
discount = discount * 0.1;
StringBuilder builder = new StringBuilder();
int textOrange = context.getResources().getColor(android.R.color.holo_red_light);
int textGray = context.getResources().getColor(android.R.color.darker_gray);
SpannableString string;
if (miniAmount > 0) {
builder.append("满").append(handleDouble(miniAmount)).append("元享").append(handleDouble(discount)).append("折");
int index = builder.indexOf("元") + 1;
string = new SpannableString(builder);
/*改变文字的颜色*/
int size = string.length();
string.setSpan(new ForegroundColorSpan(textGray), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
string.setSpan(new ForegroundColorSpan(textOrange), 1, index, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
string.setSpan(new ForegroundColorSpan(textGray), index, index + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
string.setSpan(new ForegroundColorSpan(textOrange), index + 1, size - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
string.setSpan(new ForegroundColorSpan(textGray), size - 1, size, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
builder.append(handleDouble(discount)).append("折");
string = new SpannableString(builder);
/*改变文字的颜色*/
int size = string.length();
string.setSpan(new ForegroundColorSpan(textOrange), 0, size - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
string.setSpan(new ForegroundColorSpan(textGray), size - 1, size, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return string;
}
}
================================================
FILE: app/src/main/res/layout/activity_coupon_list.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_login.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_register.xml
================================================
================================================
FILE: app/src/main/res/layout/item_list_coupon.xml
================================================
================================================
FILE: app/src/main/res/values/dimens.xml
================================================
16dp16dp16dp16dp18sp16sp5dp20dp
================================================
FILE: app/src/main/res/values/strings.xml
================================================
KAndroid登录注册最新券注册登录注册注册发送验证码手机号码密码验证码登录成功验证码已发送到手机注册成功
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: app/src/main/res/values-w820dp/dimens.xml
================================================
64dp
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
================================================
FILE: core/.gitignore
================================================
/build
================================================
FILE: core/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 22
buildToolsVersion "22.0.0"
defaultConfig {
minSdkVersion 11
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.0.0'
compile project(':model')
compile project(':api')
}
================================================
FILE: core/core.iml
================================================
================================================
FILE: core/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/keegan/adt-bundle-mac-x86_64-20140702/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: core/src/androidTest/java/me/keeganlee/kandroid/core/ApplicationTest.java
================================================
package me.keeganlee.kandroid.core;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* Testing Fundamentals
*/
public class ApplicationTest extends ApplicationTestCase {
public ApplicationTest() {
super(Application.class);
}
}
================================================
FILE: core/src/main/AndroidManifest.xml
================================================
================================================
FILE: core/src/main/java/me/keeganlee/kandroid/core/ActionCallbackListener.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.core;
/**
* Action的处理结果回调监听器
*
* @author Keegan小钢
* @date 15/6/25
* @version 1.0
*/
public interface ActionCallbackListener {
/**
* 成功时调用
*
* @param data 返回的数据
*/
public void onSuccess(T data);
/**
* 失败时调用
*
* @param errorEvent 错误码
* @param message 错误信息
*/
public void onFailure(String errorEvent, String message);
}
================================================
FILE: core/src/main/java/me/keeganlee/kandroid/core/AppAction.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.core;
import me.keeganlee.kandroid.model.CouponBO;
import java.util.List;
/**
* 接收app层的各种Action
*
* @author Keegan小钢
* @date 15/6/25
* @version 1.0
*/
public interface AppAction {
/**
* 发送验证码
*
* @param phoneNum 手机号
* @param listener 回调监听器
*/
public void sendSmsCode(String phoneNum, ActionCallbackListener listener);
/**
* 注册
*
* @param phoneNum 手机号
* @param code 验证码
* @param password 密码
* @param listener 回调监听器
*/
public void register(String phoneNum, String code, String password, ActionCallbackListener listener);
/**
* 登录
*
* @param loginName 登录名
* @param password 密码
* @param listener 回调监听器
*/
public void login(String loginName, String password, ActionCallbackListener listener);
/**
* 券列表
*
* @param currentPage 当前页数
* @param listener 回调监听器
*/
public void listCoupon(int currentPage, ActionCallbackListener> listener);
}
================================================
FILE: core/src/main/java/me/keeganlee/kandroid/core/AppActionImpl.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.core;
import android.content.Context;
import android.os.AsyncTask;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import me.keeganlee.kandroid.api.Api;
import me.keeganlee.kandroid.api.ApiImpl;
import me.keeganlee.kandroid.api.ApiResponse;
import me.keeganlee.kandroid.model.CouponBO;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* AppAction接口的实现类
*
* @author Keegan小钢
* @date 15/6/25
* @version 1.0
*/
public class AppActionImpl implements AppAction {
private final static int LOGIN_OS = 1; // 表示Android
private final static int PAGE_SIZE = 20; // 默认每页20条
private Context context;
private Api api;
public AppActionImpl(Context context) {
this.context = context;
this.api = new ApiImpl();
}
@Override
public void sendSmsCode(final String phoneNum, final ActionCallbackListener listener) {
// 参数检查
if (TextUtils.isEmpty(phoneNum)) {
if (listener != null) {
listener.onFailure(ErrorEvent.PARAM_NULL, "手机号为空");
}
return;
}
Pattern pattern = Pattern.compile("1\\d{10}");
Matcher matcher = pattern.matcher(phoneNum);
if (!matcher.matches()) {
if (listener != null) {
listener.onFailure(ErrorEvent.PARAM_ILLEGAL, "手机号不正确");
}
return;
}
// 请求Api
new AsyncTask>() {
@Override
protected ApiResponse doInBackground(Void... voids) {
return api.sendSmsCode4Register(phoneNum);
}
@Override
protected void onPostExecute(ApiResponse response) {
if (listener != null && response != null) {
if (response.isSuccess()) {
listener.onSuccess(null);
} else {
listener.onFailure(response.getEvent(), response.getMsg());
}
}
}
}.execute();
}
@Override
public void register(final String phoneNum, final String code, final String password, final ActionCallbackListener listener) {
// 参数检查
if (TextUtils.isEmpty(phoneNum)) {
if (listener != null) {
listener.onFailure(ErrorEvent.PARAM_NULL, "手机号为空");
}
return;
}
if (TextUtils.isEmpty(code)) {
if (listener != null) {
listener.onFailure(ErrorEvent.PARAM_NULL, "验证码为空");
}
return;
}
if (TextUtils.isEmpty(password)) {
if (listener != null) {
listener.onFailure(ErrorEvent.PARAM_NULL, "密码为空");
}
return;
}
Pattern pattern = Pattern.compile("1\\d{10}");
Matcher matcher = pattern.matcher(phoneNum);
if (!matcher.matches()) {
if (listener != null) {
listener.onFailure(ErrorEvent.PARAM_ILLEGAL, "手机号不正确");
}
return;
}
// 请求Api
new AsyncTask>() {
@Override
protected ApiResponse doInBackground(Void... voids) {
return api.registerByPhone(phoneNum, code, password);
}
@Override
protected void onPostExecute(ApiResponse response) {
if (listener != null && response != null) {
if (response.isSuccess()) {
listener.onSuccess(null);
} else {
listener.onFailure(response.getEvent(), response.getMsg());
}
}
}
}.execute();
}
@Override
public void login(final String loginName, final String password, final ActionCallbackListener listener) {
// 参数检查
if (TextUtils.isEmpty(loginName)) {
if (listener != null) {
listener.onFailure(ErrorEvent.PARAM_NULL, "登录名为空");
}
return;
}
if (TextUtils.isEmpty(password)) {
if (listener != null) {
listener.onFailure(ErrorEvent.PARAM_NULL, "密码为空");
}
return;
}
// 请求Api
new AsyncTask>() {
@Override
protected ApiResponse doInBackground(Void... voids) {
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
String imei = telephonyManager.getDeviceId();
return api.loginByApp(loginName, password, imei, LOGIN_OS);
}
@Override
protected void onPostExecute(ApiResponse response) {
if (listener != null && response != null) {
if (response.isSuccess()) {
listener.onSuccess(null);
} else {
listener.onFailure(response.getEvent(), response.getMsg());
}
}
}
}.execute();
}
@Override
public void listCoupon(final int currentPage, final ActionCallbackListener> listener) {
// 参数检查
if (currentPage < 0) {
if (listener != null) {
listener.onFailure(ErrorEvent.PARAM_ILLEGAL, "当前页数小于零");
}
}
// 请求Api
new AsyncTask>>() {
@Override
protected ApiResponse> doInBackground(Void... voids) {
return api.listNewCoupon(currentPage, PAGE_SIZE);
}
@Override
protected void onPostExecute(ApiResponse> response) {
if (listener != null && response != null) {
if (response.isSuccess()) {
listener.onSuccess(response.getObjList());
} else {
listener.onFailure(response.getEvent(), response.getMsg());
}
}
}
}.execute();
}
}
================================================
FILE: core/src/main/java/me/keeganlee/kandroid/core/ErrorEvent.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.core;
/**
* 错误码
*
* @author Keegan小钢
* @date 15/6/25
* @version 1.0
*/
public class ErrorEvent {
static final public String PARAM_NULL = "PARAM_NULL"; // 参数为空
static final public String PARAM_ILLEGAL = "PARAM_ILLEGAL"; // 参数不合法
}
================================================
FILE: core/src/main/res/values/strings.xml
================================================
core
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Wed Apr 10 15:27:10 PDT 2013
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: model/.gitignore
================================================
/build
================================================
FILE: model/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 22
buildToolsVersion "22.0.0"
defaultConfig {
minSdkVersion 11
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.0.0'
}
================================================
FILE: model/model.iml
================================================
================================================
FILE: model/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/keegan/adt-bundle-mac-x86_64-20140702/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: model/src/androidTest/java/me/keeganlee/kandroid/model/ApplicationTest.java
================================================
package me.keeganlee.kandroid.model;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* Testing Fundamentals
*/
public class ApplicationTest extends ApplicationTestCase {
public ApplicationTest() {
super(Application.class);
}
}
================================================
FILE: model/src/main/AndroidManifest.xml
================================================
================================================
FILE: model/src/main/java/me/keeganlee/kandroid/model/CouponBO.java
================================================
/**
* Copyright (C) 2015. Keegan小钢(http://keeganlee.me)
*
* 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 me.keeganlee.kandroid.model;
import java.io.Serializable;
/**
* 券的业务模型类,封装了券的基本信息。
* 券分为了三种类型:现金券、抵扣券、折扣券。
* 现金券是拥有固定面值的券,有固定的售价;
* 抵扣券是满足一定金额后可以抵扣的券,比如满100减10元;
* 折扣券是可以打折的券。
*
* @author Keegan小钢
* @date 15/6/21
* @version 1.0
*/
public class CouponBO implements Serializable {
private static final long serialVersionUID = -8022957276104379230L;
public static final int TYPE_CASH = 1; // 现金券
public static final int TYPE_DEBIT = 2; // 抵扣券
public static final int TYPE_DISCOUNT = 3; // 折扣券
private int id; // 券id
private String name; // 券名称
private String introduce; // 券简介
private int modelType; // 券类型,1为现金券,2为抵扣券,3为折扣券
private double faceValue; // 现金券的面值
private double estimateAmount; // 现金券的售价
private double debitAmount; // 抵扣券的抵扣金额
private double discount; // 折扣券的折扣率(0-100)
private double miniAmount; // 抵扣券和折扣券的最小使用金额
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIntroduce() {
return introduce;
}
public void setIntroduce(String introduce) {
this.introduce = introduce;
}
public int getModelType() {
return modelType;
}
public void setModelType(int modelType) {
this.modelType = modelType;
}
public double getFaceValue() {
return faceValue;
}
public void setFaceValue(double faceValue) {
this.faceValue = faceValue;
}
public double getEstimateAmount() {
return estimateAmount;
}
public void setEstimateAmount(double estimateAmount) {
this.estimateAmount = estimateAmount;
}
public double getDebitAmount() {
return debitAmount;
}
public void setDebitAmount(double debitAmount) {
this.debitAmount = debitAmount;
}
public double getDiscount() {
return discount;
}
public void setDiscount(double discount) {
this.discount = discount;
}
public double getMiniAmount() {
return miniAmount;
}
public void setMiniAmount(double miniAmount) {
this.miniAmount = miniAmount;
}
}
================================================
FILE: model/src/main/res/values/strings.xml
================================================
model
================================================
FILE: settings.gradle
================================================
include ':app', ':model', ':api', ':core'