Full Code of keeganlee/kandroid for AI

master b5d7a52353af cached
75 files
123.4 KB
33.4k tokens
121 symbols
1 requests
Download .txt
Repository: keeganlee/kandroid
Branch: master
Commit: b5d7a52353af
Files: 75
Total size: 123.4 KB

Directory structure:
gitextract_wub9ezo5/

├── .gitignore
├── .idea/
│   ├── .name
│   ├── compiler.xml
│   ├── copyright/
│   │   ├── Copyright_Header.xml
│   │   └── profiles_settings.xml
│   ├── dictionaries/
│   │   └── keegan.xml
│   ├── gradle.xml
│   ├── inspectionProfiles/
│   │   ├── Project_Default.xml
│   │   └── profiles_settings.xml
│   ├── misc.xml
│   ├── modules.xml
│   ├── runConfigurations.xml
│   └── vcs.xml
├── KAndroid.iml
├── README.md
├── api/
│   ├── .gitignore
│   ├── api.iml
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── me/
│       │           └── keeganlee/
│       │               └── kandroid/
│       │                   └── api/
│       │                       └── ApplicationTest.java
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── me/
│           │       └── keeganlee/
│           │           └── kandroid/
│           │               └── api/
│           │                   ├── Api.java
│           │                   ├── ApiImpl.java
│           │                   ├── ApiResponse.java
│           │                   ├── net/
│           │                   │   └── HttpEngine.java
│           │                   └── utils/
│           │                       └── EncryptUtil.java
│           └── res/
│               └── values/
│                   └── strings.xml
├── app/
│   ├── .gitignore
│   ├── app.iml
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── me/
│       │           └── keeganlee/
│       │               └── kandroid/
│       │                   └── ApplicationTest.java
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── me/
│           │       └── keeganlee/
│           │           └── kandroid/
│           │               ├── KApplication.java
│           │               ├── activity/
│           │               │   ├── CouponListActivity.java
│           │               │   ├── KBaseActivity.java
│           │               │   ├── LoginActivity.java
│           │               │   └── RegisterActivity.java
│           │               ├── adapter/
│           │               │   ├── CouponListAdapter.java
│           │               │   └── KBaseAdapter.java
│           │               └── util/
│           │                   └── CouponPriceUtil.java
│           └── res/
│               ├── layout/
│               │   ├── activity_coupon_list.xml
│               │   ├── activity_login.xml
│               │   ├── activity_register.xml
│               │   └── item_list_coupon.xml
│               ├── values/
│               │   ├── dimens.xml
│               │   ├── strings.xml
│               │   └── styles.xml
│               └── values-w820dp/
│                   └── dimens.xml
├── build.gradle
├── core/
│   ├── .gitignore
│   ├── build.gradle
│   ├── core.iml
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── me/
│       │           └── keeganlee/
│       │               └── kandroid/
│       │                   └── core/
│       │                       └── ApplicationTest.java
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── me/
│           │       └── keeganlee/
│           │           └── kandroid/
│           │               └── core/
│           │                   ├── ActionCallbackListener.java
│           │                   ├── AppAction.java
│           │                   ├── AppActionImpl.java
│           │                   └── ErrorEvent.java
│           └── res/
│               └── values/
│                   └── strings.xml
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── model/
│   ├── .gitignore
│   ├── build.gradle
│   ├── model.iml
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── me/
│       │           └── keeganlee/
│       │               └── kandroid/
│       │                   └── model/
│       │                       └── ApplicationTest.java
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── me/
│           │       └── keeganlee/
│           │           └── kandroid/
│           │               └── model/
│           │                   └── CouponBO.java
│           └── res/
│               └── values/
│                   └── strings.xml
└── settings.gradle

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures


================================================
FILE: .idea/.name
================================================
KAndroid

================================================
FILE: .idea/compiler.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="CompilerConfiguration">
    <resourceExtensions />
    <wildcardResourcePatterns>
      <entry name="!?*.java" />
      <entry name="!?*.form" />
      <entry name="!?*.class" />
      <entry name="!?*.groovy" />
      <entry name="!?*.scala" />
      <entry name="!?*.flex" />
      <entry name="!?*.kt" />
      <entry name="!?*.clj" />
    </wildcardResourcePatterns>
    <annotationProcessing>
      <profile default="true" name="Default" enabled="false">
        <processorPath useClasspath="true" />
      </profile>
    </annotationProcessing>
  </component>
</project>

================================================
FILE: .idea/copyright/Copyright_Header.xml
================================================
<component name="CopyrightManager">
  <copyright>
    <option name="myName" value="Copyright Header" />
    <option name="notice" value="Copyright (C) &amp;#36;today.year. Keegan小钢(http://keeganlee.me)&#10; &#10;Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10;you may not use this file except in compliance with the License.&#10;You may obtain a copy of the License at&#10; &#10;    http://www.apache.org/licenses/LICENSE-2.0&#10; &#10;Unless required by applicable law or agreed to in writing, software&#10;distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10;See the License for the specific language governing permissions and&#10;limitations under the License. " />
  </copyright>
</component>

================================================
FILE: .idea/copyright/profiles_settings.xml
================================================
<component name="CopyrightManager">
  <settings default="Copyright Header">
    <module2copyright>
      <element module="All" copyright="Copyright Header" />
    </module2copyright>
  </settings>
</component>

================================================
FILE: .idea/dictionaries/keegan.xml
================================================
<component name="ProjectDictionaryState">
  <dictionary name="keegan" />
</component>

================================================
FILE: .idea/gradle.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="GradleSettings">
    <option name="linkedExternalProjectsSettings">
      <GradleProjectSettings>
        <option name="distributionType" value="LOCAL" />
        <option name="externalProjectPath" value="$PROJECT_DIR$" />
        <option name="gradleHome" value="$APPLICATION_HOME_DIR$/gradle/gradle-2.4" />
        <option name="gradleJvm" value="1.7" />
        <option name="modules">
          <set>
            <option value="$PROJECT_DIR$" />
            <option value="$PROJECT_DIR$/api" />
            <option value="$PROJECT_DIR$/app" />
            <option value="$PROJECT_DIR$/core" />
            <option value="$PROJECT_DIR$/model" />
          </set>
        </option>
      </GradleProjectSettings>
    </option>
  </component>
</project>

================================================
FILE: .idea/inspectionProfiles/Project_Default.xml
================================================
<component name="InspectionProjectProfileManager">
  <profile version="1.0">
    <option name="myName" value="Project Default" />
    <option name="myLocal" value="true" />
    <inspection_tool class="LoggerInitializedWithForeignClass" enabled="false" level="WARNING" enabled_by_default="false">
      <option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" />
      <option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" />
    </inspection_tool>
  </profile>
</component>

================================================
FILE: .idea/inspectionProfiles/profiles_settings.xml
================================================
<component name="InspectionProjectProfileManager">
  <settings>
    <option name="PROJECT_PROFILE" value="Project Default" />
    <option name="USE_PROJECT_PROFILE" value="true" />
    <version value="1.0" />
  </settings>
</component>

================================================
FILE: .idea/misc.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="EntryPointsManager">
    <entry_points version="2.0" />
  </component>
  <component name="NullableNotNullManager">
    <option name="myDefaultNullable" value="android.support.annotation.Nullable" />
    <option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
    <option name="myNullables">
      <value>
        <list size="4">
          <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
          <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
          <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
          <item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
        </list>
      </value>
    </option>
    <option name="myNotNulls">
      <value>
        <list size="4">
          <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
          <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
          <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
          <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
        </list>
      </value>
    </option>
  </component>
  <component name="ProjectInspectionProfilesVisibleTreeState">
    <entry key="Project Default">
      <profile-state>
        <expanded-state>
          <State>
            <id />
          </State>
          <State>
            <id>Java</id>
          </State>
          <State>
            <id>Security issuesJava</id>
          </State>
          <State>
            <id>Serialization issuesJava</id>
          </State>
        </expanded-state>
        <selected-state>
          <State>
            <id>SerializableHasSerialVersionUIDField</id>
          </State>
        </selected-state>
      </profile-state>
    </entry>
  </component>
  <component name="ProjectLevelVcsManager" settingsEditedManually="false">
    <OptionsSetting value="true" id="Add" />
    <OptionsSetting value="true" id="Remove" />
    <OptionsSetting value="true" id="Checkout" />
    <OptionsSetting value="true" id="Update" />
    <OptionsSetting value="true" id="Status" />
    <OptionsSetting value="true" id="Edit" />
    <ConfirmationsSetting value="0" id="Add" />
    <ConfirmationsSetting value="0" id="Remove" />
  </component>
  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" assert-keyword="true" jdk-15="true" project-jdk-name="1.7" project-jdk-type="JavaSDK">
    <output url="file://$PROJECT_DIR$/build/classes" />
  </component>
  <component name="ProjectType">
    <option name="id" value="Android" />
  </component>
</project>

================================================
FILE: .idea/modules.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectModuleManager">
    <modules>
      <module fileurl="file://$PROJECT_DIR$/KAndroid.iml" filepath="$PROJECT_DIR$/KAndroid.iml" />
      <module fileurl="file://$PROJECT_DIR$/api/api.iml" filepath="$PROJECT_DIR$/api/api.iml" />
      <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
      <module fileurl="file://$PROJECT_DIR$/core/core.iml" filepath="$PROJECT_DIR$/core/core.iml" />
      <module fileurl="file://$PROJECT_DIR$/model/model.iml" filepath="$PROJECT_DIR$/model/model.iml" />
    </modules>
  </component>
</project>

================================================
FILE: .idea/runConfigurations.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="RunConfigurationProducerService">
    <option name="ignoredProducers">
      <set>
        <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
      </set>
    </option>
  </component>
</project>

================================================
FILE: .idea/vcs.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="VcsDirectoryMappings">
    <mapping directory="" vcs="" />
    <mapping directory="$PROJECT_DIR$" vcs="Git" />
  </component>
</project>

================================================
FILE: KAndroid.iml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="KAndroid" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
  <component name="FacetManager">
    <facet type="java-gradle" name="Java-Gradle">
      <configuration>
        <option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
        <option name="BUILDABLE" value="false" />
      </configuration>
    </facet>
  </component>
  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
    <exclude-output />
    <content url="file://$MODULE_DIR$">
      <excludeFolder url="file://$MODULE_DIR$/.gradle" />
    </content>
    <orderEntry type="inheritedJdk" />
    <orderEntry type="sourceFolder" forTests="false" />
  </component>
</module>

================================================
FILE: README.md
================================================
# kandroid
KAndroid是一个Android的简单的架构搭建的学习项目。架构上分为了四个层级:模型层、接口层、核心层和应用层。
四个层级对应的module为:model、api、core和app。

详细的内容可查看博客文章:

[Android项目重构之路:架构篇](http://keeganlee.me/post/android/20150605)

[Android项目重构之路:界面篇](http://keeganlee.me/post/android/20150619)

[Android项目重构之路:实现篇](http://keeganlee.me/post/android/20150629)



================================================
FILE: api/.gitignore
================================================
/build


================================================
FILE: api/api.iml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":api" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="KAndroid" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
  <component name="FacetManager">
    <facet type="android-gradle" name="Android-Gradle">
      <configuration>
        <option name="GRADLE_PROJECT_PATH" value=":api" />
      </configuration>
    </facet>
    <facet type="android" name="Android">
      <configuration>
        <option name="SELECTED_BUILD_VARIANT" value="debug" />
        <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
        <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
        <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
        <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
        <option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
        <afterSyncTasks>
          <task>generateDebugAndroidTestSources</task>
          <task>generateDebugSources</task>
        </afterSyncTasks>
        <option name="ALLOW_USER_CONFIGURATION" value="false" />
        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
        <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
        <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
        <option name="LIBRARY_PROJECT" value="true" />
      </configuration>
    </facet>
  </component>
  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
    <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
    <output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
    <exclude-output />
    <content url="file://$MODULE_DIR$">
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
      <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
      <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
    </content>
    <orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" />
    <orderEntry type="sourceFolder" forTests="false" />
    <orderEntry type="library" exported="" name="support-v4-22.0.0" level="project" />
    <orderEntry type="library" exported="" name="support-annotations-22.0.0" level="project" />
    <orderEntry type="library" exported="" name="appcompat-v7-22.0.0" level="project" />
    <orderEntry type="library" exported="" name="gson-2.3" level="project" />
    <orderEntry type="module" module-name="model" exported="" />
  </component>
</module>

================================================
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;

/**
 * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
 */
public class ApplicationTest extends ApplicationTestCase<Application> {
    public ApplicationTest() {
        super(Application.class);
    }
}

================================================
FILE: api/src/main/AndroidManifest.xml
================================================
<!--
 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="me.keeganlee.kandroid.api">

    <application android:allowBackup="true" android:label="@string/app_name">

    </application>

</manifest>


================================================
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<Void> sendSmsCode4Register(String phoneNum);

    /**
     * 注册
     *
     * @param phoneNum 手机号码
     * @param code     验证码
     * @param password MD5加密的密码
     * @return 成功时返回:{ "code": 0, "msg":"success" }
     */
    public ApiResponse<Void> 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<Void> loginByApp(String loginName, String password, String imei, int loginOS);

    /**
     * 券列表
     *
     * @param currentPage 当前页数
     * @param pageSize    每页显示数量
     * @return 成功时返回:{ "code": 0, "msg":"success", "objList":[...] }
     */
    public ApiResponse<List<CouponBO>> 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<Void> sendSmsCode4Register(String phoneNum) {
        Map<String, String> paramMap = new HashMap<String, String>();
        paramMap.put("appKey", APP_KEY);
        paramMap.put("method", SEND_SMS_CODE);
        paramMap.put("phoneNum", phoneNum);

        Type type = new TypeToken<ApiResponse<Void>>(){}.getType();
        try {
            return httpEngine.postHandle(paramMap, type);
        } catch (IOException e) {
            return new ApiResponse(TIME_OUT_EVENT, TIME_OUT_EVENT_MSG);
        }
    }

    @Override
    public ApiResponse<Void> registerByPhone(String phoneNum, String code, String password) {
        Map<String, String> paramMap = new HashMap<String, String>();
        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<ApiResponse<List<CouponBO>>>(){}.getType();
        try {
            return httpEngine.postHandle(paramMap, type);
        } catch (IOException e) {
            return new ApiResponse(TIME_OUT_EVENT, TIME_OUT_EVENT_MSG);
        }
    }

    @Override
    public ApiResponse<Void> loginByApp(String loginName, String password, String imei, int loginOS) {
        Map<String, String> paramMap = new HashMap<String, String>();
        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<ApiResponse<List<CouponBO>>>(){}.getType();
        try {
            return httpEngine.postHandle(paramMap, type);
        } catch (IOException e) {
            return new ApiResponse(TIME_OUT_EVENT, TIME_OUT_EVENT_MSG);
        }
    }

    @Override
    public ApiResponse<List<CouponBO>> listNewCoupon(int currentPage, int pageSize) {
        Map<String, String> paramMap = new HashMap<String, String>();
        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<ApiResponse<List<CouponBO>>>(){}.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<T> {
    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> T postHandle(Map<String, String> 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<String, String> 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
================================================
<!--
 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.
-->
<resources>
    <string name="app_name">api</string>
</resources>


================================================
FILE: app/.gitignore
================================================
/build


================================================
FILE: app/app.iml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="KAndroid" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
  <component name="FacetManager">
    <facet type="android-gradle" name="Android-Gradle">
      <configuration>
        <option name="GRADLE_PROJECT_PATH" value=":app" />
      </configuration>
    </facet>
    <facet type="android" name="Android">
      <configuration>
        <option name="SELECTED_BUILD_VARIANT" value="debug" />
        <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
        <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
        <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
        <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
        <option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
        <afterSyncTasks>
          <task>generateDebugAndroidTestSources</task>
          <task>generateDebugSources</task>
        </afterSyncTasks>
        <option name="ALLOW_USER_CONFIGURATION" value="false" />
        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
        <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
        <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
      </configuration>
    </facet>
  </component>
  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
    <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
    <output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
    <exclude-output />
    <content url="file://$MODULE_DIR$">
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.0.0/jars" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/22.0.0/jars" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.google.android.gms/play-services/6.1.71/jars" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
      <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
      <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
    </content>
    <orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" />
    <orderEntry type="sourceFolder" forTests="false" />
    <orderEntry type="library" exported="" name="support-v4-22.0.0" level="project" />
    <orderEntry type="library" exported="" name="support-annotations-22.0.0" level="project" />
    <orderEntry type="library" exported="" name="appcompat-v7-22.0.0" level="project" />
    <orderEntry type="library" exported="" name="gson-2.3" level="project" />
    <orderEntry type="library" exported="" name="play-services-6.1.71" level="project" />
    <orderEntry type="module" module-name="core" exported="" />
    <orderEntry type="module" module-name="model" exported="" />
  </component>
</module>

================================================
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;

/**
 * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
 */
public class ApplicationTest extends ApplicationTestCase<Application> {
    public ApplicationTest() {
        super(Application.class);
    }
}

================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="me.keeganlee.kandroid" >

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <application
        android:name=".KApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".activity.LoginActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".activity.RegisterActivity"
            android:label="@string/title_activity_register" >
        </activity>
        <activity
            android:name=".activity.CouponListActivity"
            android:label="@string/title_activity_coupon_list" >
        </activity>
    </application>

</manifest>


================================================
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<List<CouponBO>>() {
            @Override
            public void onSuccess(List<CouponBO> 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<Void>() {
            @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<Void>() {
            @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<Void>() {
            @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<CouponBO> {

    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<T> extends BaseAdapter {

    protected Context context;
    protected LayoutInflater inflater;
    protected List<T> itemList = new ArrayList<T>();

    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<T> itemList) {
        this.itemList.addAll(itemList);
        notifyDataSetChanged();
    }

    /**
     * 设置为新的数据,旧数据会被清空
     *
     * @param itemList
     */
    public void setItems(List<T> 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
================================================
<!--
 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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="me.keeganlee.kandroid.activity.CouponListActivity">
    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <ListView
            android:id="@+id/list_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>


================================================
FILE: app/src/main/res/layout/activity_login.xml
================================================
<!--
 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="me.keeganlee.kandroid.activity.LoginActivity">

    <EditText
        android:id="@+id/edit_phone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/edit_vertical_margin"
        android:layout_marginBottom="@dimen/edit_vertical_margin"
        android:hint="@string/hint_phone"
        android:inputType="phone"
        android:singleLine="true" />

    <EditText
        android:id="@+id/edit_password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/edit_vertical_margin"
        android:layout_marginBottom="@dimen/edit_vertical_margin"
        android:hint="@string/hint_password"
        android:inputType="textPassword"
        android:singleLine="true" />

    <Button
        android:id="@+id/btn_login"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/btn_vertical_margin"
        android:layout_marginBottom="@dimen/btn_vertical_margin"
        android:onClick="toLogin"
        android:text="@string/btn_login" />

    <Button
        android:id="@+id/btn_goto_register"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="@dimen/btn_vertical_margin"
        android:onClick="toRegister"
        android:text="@string/btn_goto_register" />

</LinearLayout>


================================================
FILE: app/src/main/res/layout/activity_register.xml
================================================
<!--
 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="me.keeganlee.kandroid.activity.RegisterActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <EditText
            android:id="@+id/edit_phone"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_marginTop="@dimen/edit_vertical_margin"
            android:layout_marginBottom="@dimen/edit_vertical_margin"
            android:hint="@string/hint_phone"
            android:inputType="phone"
            android:singleLine="true" />
        <Button
            android:id="@+id/btn_send_code"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="toSendCode"
            android:text="@string/btn_send_code"/>
    </LinearLayout>

    <EditText
        android:id="@+id/edit_code"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/edit_vertical_margin"
        android:layout_marginBottom="@dimen/edit_vertical_margin"
        android:hint="@string/hint_code"
        android:inputType="text"
        android:singleLine="true" />

    <EditText
        android:id="@+id/edit_password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/edit_vertical_margin"
        android:layout_marginBottom="@dimen/edit_vertical_margin"
        android:hint="@string/hint_password"
        android:inputType="textPassword"
        android:singleLine="true" />

    <Button
        android:id="@+id/btn_register"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/btn_vertical_margin"
        android:layout_marginBottom="@dimen/btn_vertical_margin"
        android:onClick="toRegister"
        android:text="@string/btn_register" />
</LinearLayout>


================================================
FILE: app/src/main/res/layout/item_list_coupon.xml
================================================
<!--
 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingTop="@dimen/item_list_vertical_padding"
    android:paddingBottom="@dimen/item_list_vertical_padding"
    android:paddingLeft="@dimen/item_list_horizontal_padding"
    android:paddingRight="@dimen/item_list_horizontal_padding"
    android:orientation="vertical">
    <TextView
        android:id="@+id/text_item_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="@dimen/item_list_title_text_size"
        android:textColor="@android:color/black"/>
    <TextView
        android:id="@+id/text_item_info"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="@dimen/item_list_info_text_size"
        android:textColor="@android:color/darker_gray"/>
    <TextView
        android:id="@+id/text_item_price"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="@dimen/item_list_info_text_size"
        android:textColor="@android:color/holo_red_light"/>
</LinearLayout>

================================================
FILE: app/src/main/res/values/dimens.xml
================================================
<!--
 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.
-->
<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>

    <dimen name="item_list_horizontal_padding">16dp</dimen>
    <dimen name="item_list_vertical_padding">16dp</dimen>
    <dimen name="item_list_title_text_size">18sp</dimen>
    <dimen name="item_list_info_text_size">16sp</dimen>

    <dimen name="edit_vertical_margin">5dp</dimen>

    <dimen name="btn_vertical_margin">20dp</dimen>
</resources>


================================================
FILE: app/src/main/res/values/strings.xml
================================================
<!--
 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.
-->
<resources>
    <string name="app_name">KAndroid</string>

    <!-- 页面标题 -->
    <string name="title_activity_login">登录</string>
    <string name="title_activity_register">注册</string>
    <string name="title_activity_coupon_list">最新券</string>

    <!-- ActionBar -->
    <string name="action_register">注册</string>

    <!-- 按钮文字 -->
    <string name="btn_login">登录</string>
    <string name="btn_register">注册</string>
    <string name="btn_goto_register">注册</string>
    <string name="btn_send_code">发送验证码</string>

    <!-- EditText hint -->
    <string name="hint_phone">手机号码</string>
    <string name="hint_password">密码</string>
    <string name="hint_code">验证码</string>

    <!-- Toast信息 -->
    <string name="toast_login_success">登录成功</string>
    <string name="toast_code_has_sent">验证码已发送到手机</string>
    <string name="toast_register_success">注册成功</string>

</resources>


================================================
FILE: app/src/main/res/values/styles.xml
================================================
<!--
 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.
-->
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
        <!-- Customize your theme here. -->
    </style>

</resources>


================================================
FILE: app/src/main/res/values-w820dp/dimens.xml
================================================
<!--
 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.
-->
<resources>
    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
         (such as screen margins) for screens with more than 820dp of available width. This
         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
    <dimen name="activity_horizontal_margin">64dp</dimen>
</resources>


================================================
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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":core" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="KAndroid" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
  <component name="FacetManager">
    <facet type="android-gradle" name="Android-Gradle">
      <configuration>
        <option name="GRADLE_PROJECT_PATH" value=":core" />
      </configuration>
    </facet>
    <facet type="android" name="Android">
      <configuration>
        <option name="SELECTED_BUILD_VARIANT" value="debug" />
        <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
        <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
        <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
        <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
        <option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
        <afterSyncTasks>
          <task>generateDebugAndroidTestSources</task>
          <task>generateDebugSources</task>
        </afterSyncTasks>
        <option name="ALLOW_USER_CONFIGURATION" value="false" />
        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
        <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
        <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
        <option name="LIBRARY_PROJECT" value="true" />
      </configuration>
    </facet>
  </component>
  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
    <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
    <output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
    <exclude-output />
    <content url="file://$MODULE_DIR$">
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
      <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
      <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
    </content>
    <orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" />
    <orderEntry type="sourceFolder" forTests="false" />
    <orderEntry type="library" exported="" name="support-v4-22.0.0" level="project" />
    <orderEntry type="library" exported="" name="support-annotations-22.0.0" level="project" />
    <orderEntry type="library" exported="" name="appcompat-v7-22.0.0" level="project" />
    <orderEntry type="library" exported="" name="gson-2.3" level="project" />
    <orderEntry type="module" module-name="api" exported="" />
    <orderEntry type="module" module-name="model" exported="" />
  </component>
</module>

================================================
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;

/**
 * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
 */
public class ApplicationTest extends ApplicationTestCase<Application> {
    public ApplicationTest() {
        super(Application.class);
    }
}

================================================
FILE: core/src/main/AndroidManifest.xml
================================================
<!--
 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="me.keeganlee.kandroid.core">

    <application android:allowBackup="true" android:label="@string/app_name">

    </application>

</manifest>


================================================
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<T> {
    /**
     * 成功时调用
     *
     * @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<Void> listener);

    /**
     * 注册
     *
     * @param phoneNum 手机号
     * @param code     验证码
     * @param password 密码
     * @param listener 回调监听器
     */
    public void register(String phoneNum, String code, String password, ActionCallbackListener<Void> listener);

    /**
     * 登录
     *
     * @param loginName 登录名
     * @param password  密码
     * @param listener  回调监听器
     */
    public void login(String loginName, String password, ActionCallbackListener<Void> listener);

    /**
     * 券列表
     *
     * @param currentPage 当前页数
     * @param listener    回调监听器
     */
    public void listCoupon(int currentPage, ActionCallbackListener<List<CouponBO>> 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<Void> 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<Void, Void, ApiResponse<Void>>() {
            @Override
            protected ApiResponse<Void> doInBackground(Void... voids) {
                return api.sendSmsCode4Register(phoneNum);
            }

            @Override
            protected void onPostExecute(ApiResponse<Void> 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<Void> 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<Void, Void, ApiResponse<Void>>() {
            @Override
            protected ApiResponse<Void> doInBackground(Void... voids) {
                return api.registerByPhone(phoneNum, code, password);
            }

            @Override
            protected void onPostExecute(ApiResponse<Void> 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<Void> 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<Void, Void, ApiResponse<Void>>() {
            @Override
            protected ApiResponse<Void> 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<Void> 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<List<CouponBO>> listener) {
        // 参数检查
        if (currentPage < 0) {
            if (listener != null) {
                listener.onFailure(ErrorEvent.PARAM_ILLEGAL, "当前页数小于零");
            }
        }

        // 请求Api
        new AsyncTask<Void, Void, ApiResponse<List<CouponBO>>>() {
            @Override
            protected ApiResponse<List<CouponBO>> doInBackground(Void... voids) {
                return api.listNewCoupon(currentPage, PAGE_SIZE);
            }

            @Override
            protected void onPostExecute(ApiResponse<List<CouponBO>> 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
================================================
<!--
 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.
-->
<resources>
    <string name="app_name">core</string>
</resources>


================================================
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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":model" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="KAndroid" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
  <component name="FacetManager">
    <facet type="android-gradle" name="Android-Gradle">
      <configuration>
        <option name="GRADLE_PROJECT_PATH" value=":model" />
      </configuration>
    </facet>
    <facet type="android" name="Android">
      <configuration>
        <option name="SELECTED_BUILD_VARIANT" value="debug" />
        <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
        <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
        <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
        <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
        <option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
        <afterSyncTasks>
          <task>generateDebugAndroidTestSources</task>
          <task>generateDebugSources</task>
        </afterSyncTasks>
        <option name="ALLOW_USER_CONFIGURATION" value="false" />
        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
        <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
        <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
        <option name="LIBRARY_PROJECT" value="true" />
      </configuration>
    </facet>
  </component>
  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
    <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
    <output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
    <exclude-output />
    <content url="file://$MODULE_DIR$">
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
      <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
      <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
    </content>
    <orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" />
    <orderEntry type="sourceFolder" forTests="false" />
    <orderEntry type="library" exported="" name="support-v4-22.0.0" level="project" />
    <orderEntry type="library" exported="" name="support-annotations-22.0.0" level="project" />
    <orderEntry type="library" exported="" name="appcompat-v7-22.0.0" level="project" />
  </component>
</module>

================================================
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;

/**
 * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
 */
public class ApplicationTest extends ApplicationTestCase<Application> {
    public ApplicationTest() {
        super(Application.class);
    }
}

================================================
FILE: model/src/main/AndroidManifest.xml
================================================
<!--
 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="me.keeganlee.kandroid.model">

    <application android:allowBackup="true" android:label="@string/app_name">

    </application>

</manifest>


================================================
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
================================================
<!--
 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.
-->
<resources>
    <string name="app_name">model</string>
</resources>


================================================
FILE: settings.gradle
================================================
include ':app', ':model', ':api', ':core'
Download .txt
gitextract_wub9ezo5/

├── .gitignore
├── .idea/
│   ├── .name
│   ├── compiler.xml
│   ├── copyright/
│   │   ├── Copyright_Header.xml
│   │   └── profiles_settings.xml
│   ├── dictionaries/
│   │   └── keegan.xml
│   ├── gradle.xml
│   ├── inspectionProfiles/
│   │   ├── Project_Default.xml
│   │   └── profiles_settings.xml
│   ├── misc.xml
│   ├── modules.xml
│   ├── runConfigurations.xml
│   └── vcs.xml
├── KAndroid.iml
├── README.md
├── api/
│   ├── .gitignore
│   ├── api.iml
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── me/
│       │           └── keeganlee/
│       │               └── kandroid/
│       │                   └── api/
│       │                       └── ApplicationTest.java
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── me/
│           │       └── keeganlee/
│           │           └── kandroid/
│           │               └── api/
│           │                   ├── Api.java
│           │                   ├── ApiImpl.java
│           │                   ├── ApiResponse.java
│           │                   ├── net/
│           │                   │   └── HttpEngine.java
│           │                   └── utils/
│           │                       └── EncryptUtil.java
│           └── res/
│               └── values/
│                   └── strings.xml
├── app/
│   ├── .gitignore
│   ├── app.iml
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── me/
│       │           └── keeganlee/
│       │               └── kandroid/
│       │                   └── ApplicationTest.java
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── me/
│           │       └── keeganlee/
│           │           └── kandroid/
│           │               ├── KApplication.java
│           │               ├── activity/
│           │               │   ├── CouponListActivity.java
│           │               │   ├── KBaseActivity.java
│           │               │   ├── LoginActivity.java
│           │               │   └── RegisterActivity.java
│           │               ├── adapter/
│           │               │   ├── CouponListAdapter.java
│           │               │   └── KBaseAdapter.java
│           │               └── util/
│           │                   └── CouponPriceUtil.java
│           └── res/
│               ├── layout/
│               │   ├── activity_coupon_list.xml
│               │   ├── activity_login.xml
│               │   ├── activity_register.xml
│               │   └── item_list_coupon.xml
│               ├── values/
│               │   ├── dimens.xml
│               │   ├── strings.xml
│               │   └── styles.xml
│               └── values-w820dp/
│                   └── dimens.xml
├── build.gradle
├── core/
│   ├── .gitignore
│   ├── build.gradle
│   ├── core.iml
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── me/
│       │           └── keeganlee/
│       │               └── kandroid/
│       │                   └── core/
│       │                       └── ApplicationTest.java
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── me/
│           │       └── keeganlee/
│           │           └── kandroid/
│           │               └── core/
│           │                   ├── ActionCallbackListener.java
│           │                   ├── AppAction.java
│           │                   ├── AppActionImpl.java
│           │                   └── ErrorEvent.java
│           └── res/
│               └── values/
│                   └── strings.xml
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── model/
│   ├── .gitignore
│   ├── build.gradle
│   ├── model.iml
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── me/
│       │           └── keeganlee/
│       │               └── kandroid/
│       │                   └── model/
│       │                       └── ApplicationTest.java
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── me/
│           │       └── keeganlee/
│           │           └── kandroid/
│           │               └── model/
│           │                   └── CouponBO.java
│           └── res/
│               └── values/
│                   └── strings.xml
└── settings.gradle
Download .txt
SYMBOL INDEX (121 symbols across 22 files)

FILE: api/src/androidTest/java/me/keeganlee/kandroid/api/ApplicationTest.java
  class ApplicationTest (line 9) | public class ApplicationTest extends ApplicationTestCase<Application> {
    method ApplicationTest (line 10) | public ApplicationTest() {

FILE: api/src/main/java/me/keeganlee/kandroid/api/Api.java
  type Api (line 29) | public interface Api {
    method sendSmsCode4Register (line 45) | public ApiResponse<Void> sendSmsCode4Register(String phoneNum);
    method registerByPhone (line 55) | public ApiResponse<Void> registerByPhone(String phoneNum, String code,...
    method loginByApp (line 66) | public ApiResponse<Void> loginByApp(String loginName, String password,...
    method listNewCoupon (line 75) | public ApiResponse<List<CouponBO>> listNewCoupon(int currentPage, int ...

FILE: api/src/main/java/me/keeganlee/kandroid/api/ApiImpl.java
  class ApiImpl (line 36) | public class ApiImpl implements Api {
    method ApiImpl (line 43) | public ApiImpl() {
    method sendSmsCode4Register (line 47) | @Override
    method registerByPhone (line 62) | @Override
    method loginByApp (line 79) | @Override
    method listNewCoupon (line 97) | @Override

FILE: api/src/main/java/me/keeganlee/kandroid/api/ApiResponse.java
  class ApiResponse (line 25) | public class ApiResponse<T> {
    method ApiResponse (line 35) | public ApiResponse(String event, String msg) {
    method isSuccess (line 40) | public boolean isSuccess() {
    method getEvent (line 44) | public String getEvent() {
    method setEvent (line 48) | public void setEvent(String event) {
    method getMsg (line 52) | public String getMsg() {
    method setMsg (line 56) | public void setMsg(String msg) {
    method getObj (line 60) | public T getObj() {
    method setObj (line 64) | public void setObj(T obj) {
    method getObjList (line 68) | public T getObjList() {
    method setObjList (line 72) | public void setObjList(T objList) {
    method getCurrentPage (line 76) | public int getCurrentPage() {
    method setCurrentPage (line 80) | public void setCurrentPage(int currentPage) {
    method getPageSize (line 84) | public int getPageSize() {
    method setPageSize (line 88) | public void setPageSize(int pageSize) {
    method getMaxCount (line 92) | public int getMaxCount() {
    method setMaxCount (line 96) | public void setMaxCount(int maxCount) {
    method getMaxPage (line 100) | public int getMaxPage() {
    method setMaxPage (line 104) | public void setMaxPage(int maxPage) {

FILE: api/src/main/java/me/keeganlee/kandroid/api/net/HttpEngine.java
  class HttpEngine (line 40) | public class HttpEngine {
    method HttpEngine (line 49) | private HttpEngine() {
    method getInstance (line 53) | public static HttpEngine getInstance() {
    method postHandle (line 60) | public <T> T postHandle(Map<String, String> paramsMap, Type typeOfT) t...
    method getConnection (line 101) | private HttpURLConnection getConnection() {
    method joinParams (line 131) | private String joinParams(Map<String, String> paramsMap) {

FILE: api/src/main/java/me/keeganlee/kandroid/api/utils/EncryptUtil.java
  class EncryptUtil (line 28) | public class EncryptUtil {
    method makeMD5 (line 30) | public static String makeMD5(String password) {

FILE: app/src/androidTest/java/me/keeganlee/kandroid/ApplicationTest.java
  class ApplicationTest (line 9) | public class ApplicationTest extends ApplicationTestCase<Application> {
    method ApplicationTest (line 10) | public ApplicationTest() {

FILE: app/src/main/java/me/keeganlee/kandroid/KApplication.java
  class KApplication (line 28) | public class KApplication extends Application {
    method onCreate (line 32) | @Override
    method getAppAction (line 38) | public AppAction getAppAction() {

FILE: app/src/main/java/me/keeganlee/kandroid/activity/CouponListActivity.java
  class CouponListActivity (line 35) | public class CouponListActivity extends KBaseActivity implements SwipeRe...
    method onCreate (line 41) | @Override
    method initViews (line 52) | private void initViews() {
    method getData (line 60) | private void getData() {
    method onRefresh (line 82) | @Override

FILE: app/src/main/java/me/keeganlee/kandroid/activity/KBaseActivity.java
  class KBaseActivity (line 30) | public abstract class KBaseActivity extends FragmentActivity {
    method onCreate (line 38) | @Override

FILE: app/src/main/java/me/keeganlee/kandroid/activity/LoginActivity.java
  class LoginActivity (line 33) | public class LoginActivity extends KBaseActivity {
    method onCreate (line 39) | @Override
    method initViews (line 48) | private void initViews() {
    method toLogin (line 55) | public void toLogin(View view) {
    method toRegister (line 77) | public void toRegister(View view) {

FILE: app/src/main/java/me/keeganlee/kandroid/activity/RegisterActivity.java
  class RegisterActivity (line 34) | public class RegisterActivity extends KBaseActivity {
    method onCreate (line 42) | @Override
    method onOptionsItemSelected (line 51) | @Override
    method initViews (line 66) | private void initViews() {
    method toSendCode (line 75) | public void toSendCode(View view) {
    method toRegister (line 94) | public void toRegister(View view) {

FILE: app/src/main/java/me/keeganlee/kandroid/adapter/CouponListAdapter.java
  class CouponListAdapter (line 33) | public class CouponListAdapter extends KBaseAdapter<CouponBO> {
    method CouponListAdapter (line 35) | public CouponListAdapter(Context context) {
    method getView (line 39) | @Override
    class ViewHolder (line 75) | static class ViewHolder {

FILE: app/src/main/java/me/keeganlee/kandroid/adapter/KBaseAdapter.java
  class KBaseAdapter (line 32) | public abstract class KBaseAdapter<T> extends BaseAdapter {
    method KBaseAdapter (line 38) | public KBaseAdapter(Context context) {
    method isEmpty (line 48) | public boolean isEmpty() {
    method addItems (line 57) | public void addItems(List<T> itemList) {
    method setItems (line 67) | public void setItems(List<T> itemList) {
    method clearItems (line 76) | public void clearItems() {
    method getCount (line 81) | @Override
    method getItem (line 86) | @Override
    method getItemId (line 91) | @Override
    method getView (line 96) | @Override

FILE: app/src/main/java/me/keeganlee/kandroid/util/CouponPriceUtil.java
  class CouponPriceUtil (line 33) | public class CouponPriceUtil {
    method handleDouble (line 37) | public static String handleDouble(double price) {
    method sp2px (line 45) | public static int sp2px(Context context, float spValue) {
    method getCashPrice (line 53) | public static SpannableString getCashPrice(Context context, double old...
    method getVoucherPrice (line 76) | public static SpannableString getVoucherPrice(Context context, double ...
    method getDiscountPrice (line 105) | public static SpannableString getDiscountPrice(Context context, double...

FILE: core/src/androidTest/java/me/keeganlee/kandroid/core/ApplicationTest.java
  class ApplicationTest (line 9) | public class ApplicationTest extends ApplicationTestCase<Application> {
    method ApplicationTest (line 10) | public ApplicationTest() {

FILE: core/src/main/java/me/keeganlee/kandroid/core/ActionCallbackListener.java
  type ActionCallbackListener (line 25) | public interface ActionCallbackListener<T> {
    method onSuccess (line 31) | public void onSuccess(T data);
    method onFailure (line 39) | public void onFailure(String errorEvent, String message);

FILE: core/src/main/java/me/keeganlee/kandroid/core/AppAction.java
  type AppAction (line 29) | public interface AppAction {
    method sendSmsCode (line 36) | public void sendSmsCode(String phoneNum, ActionCallbackListener<Void> ...
    method register (line 46) | public void register(String phoneNum, String code, String password, Ac...
    method login (line 55) | public void login(String loginName, String password, ActionCallbackLis...
    method listCoupon (line 63) | public void listCoupon(int currentPage, ActionCallbackListener<List<Co...

FILE: core/src/main/java/me/keeganlee/kandroid/core/AppActionImpl.java
  class AppActionImpl (line 39) | public class AppActionImpl implements AppAction {
    method AppActionImpl (line 47) | public AppActionImpl(Context context) {
    method sendSmsCode (line 52) | @Override
    method register (line 90) | @Override
    method login (line 140) | @Override
    method listCoupon (line 178) | @Override

FILE: core/src/main/java/me/keeganlee/kandroid/core/ErrorEvent.java
  class ErrorEvent (line 25) | public class ErrorEvent {

FILE: model/src/androidTest/java/me/keeganlee/kandroid/model/ApplicationTest.java
  class ApplicationTest (line 9) | public class ApplicationTest extends ApplicationTestCase<Application> {
    method ApplicationTest (line 10) | public ApplicationTest() {

FILE: model/src/main/java/me/keeganlee/kandroid/model/CouponBO.java
  class CouponBO (line 31) | public class CouponBO implements Serializable {
    method getId (line 48) | public int getId() {
    method setId (line 52) | public void setId(int id) {
    method getName (line 56) | public String getName() {
    method setName (line 60) | public void setName(String name) {
    method getIntroduce (line 64) | public String getIntroduce() {
    method setIntroduce (line 68) | public void setIntroduce(String introduce) {
    method getModelType (line 72) | public int getModelType() {
    method setModelType (line 76) | public void setModelType(int modelType) {
    method getFaceValue (line 80) | public double getFaceValue() {
    method setFaceValue (line 84) | public void setFaceValue(double faceValue) {
    method getEstimateAmount (line 88) | public double getEstimateAmount() {
    method setEstimateAmount (line 92) | public void setEstimateAmount(double estimateAmount) {
    method getDebitAmount (line 96) | public double getDebitAmount() {
    method setDebitAmount (line 100) | public void setDebitAmount(double debitAmount) {
    method getDiscount (line 104) | public double getDiscount() {
    method setDiscount (line 108) | public void setDiscount(double discount) {
    method getMiniAmount (line 112) | public double getMiniAmount() {
    method setMiniAmount (line 116) | public void setMiniAmount(double miniAmount) {
Condensed preview — 75 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (140K chars).
[
  {
    "path": ".gitignore",
    "chars": 91,
    "preview": ".gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n"
  },
  {
    "path": ".idea/.name",
    "chars": 8,
    "preview": "KAndroid"
  },
  {
    "path": ".idea/compiler.xml",
    "chars": 656,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CompilerConfiguration\">\n    <resourceExt"
  },
  {
    "path": ".idea/copyright/Copyright_Header.xml",
    "chars": 830,
    "preview": "<component name=\"CopyrightManager\">\n  <copyright>\n    <option name=\"myName\" value=\"Copyright Header\" />\n    <option name"
  },
  {
    "path": ".idea/copyright/profiles_settings.xml",
    "chars": 209,
    "preview": "<component name=\"CopyrightManager\">\n  <settings default=\"Copyright Header\">\n    <module2copyright>\n      <element module"
  },
  {
    "path": ".idea/dictionaries/keegan.xml",
    "chars": 85,
    "preview": "<component name=\"ProjectDictionaryState\">\n  <dictionary name=\"keegan\" />\n</component>"
  },
  {
    "path": ".idea/gradle.xml",
    "chars": 834,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleSettings\">\n    <option name=\"linke"
  },
  {
    "path": ".idea/inspectionProfiles/Project_Default.xml",
    "chars": 596,
    "preview": "<component name=\"InspectionProjectProfileManager\">\n  <profile version=\"1.0\">\n    <option name=\"myName\" value=\"Project De"
  },
  {
    "path": ".idea/inspectionProfiles/profiles_settings.xml",
    "chars": 235,
    "preview": "<component name=\"InspectionProjectProfileManager\">\n  <settings>\n    <option name=\"PROJECT_PROFILE\" value=\"Project Defaul"
  },
  {
    "path": ".idea/misc.xml",
    "chars": 2858,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"EntryPointsManager\">\n    <entry_points v"
  },
  {
    "path": ".idea/modules.xml",
    "chars": 656,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectModuleManager\">\n    <modules>\n   "
  },
  {
    "path": ".idea/runConfigurations.xml",
    "chars": 564,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RunConfigurationProducerService\">\n    <o"
  },
  {
    "path": ".idea/vcs.xml",
    "chars": 216,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping dire"
  },
  {
    "path": "KAndroid.iml",
    "chars": 939,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module external.linked.project.id=\"KAndroid\" external.linked.project.path=\"$MODU"
  },
  {
    "path": "README.md",
    "chars": 314,
    "preview": "# kandroid\nKAndroid是一个Android的简单的架构搭建的学习项目。架构上分为了四个层级:模型层、接口层、核心层和应用层。\n四个层级对应的module为:model、api、core和app。\n\n详细的内容可查看博客文章:"
  },
  {
    "path": "api/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "api/api.iml",
    "chars": 7553,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module external.linked.project.id=\":api\" external.linked.project.path=\"$MODULE_D"
  },
  {
    "path": "api/build.gradle",
    "chars": 611,
    "preview": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 22\n    buildToolsVersion \"22.0.0\"\n\n    defaultConfi"
  },
  {
    "path": "api/proguard-rules.pro",
    "chars": 679,
    "preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
  },
  {
    "path": "api/src/androidTest/java/me/keeganlee/kandroid/api/ApplicationTest.java",
    "chars": 356,
    "preview": "package me.keeganlee.kandroid.api;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a "
  },
  {
    "path": "api/src/main/AndroidManifest.xml",
    "chars": 816,
    "preview": "<!--\n Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n\n Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "api/src/main/java/me/keeganlee/kandroid/api/Api.java",
    "chars": 2171,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "api/src/main/java/me/keeganlee/kandroid/api/ApiImpl.java",
    "chars": 4028,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "api/src/main/java/me/keeganlee/kandroid/api/ApiResponse.java",
    "chars": 2367,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "api/src/main/java/me/keeganlee/kandroid/api/net/HttpEngine.java",
    "chars": 4855,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "api/src/main/java/me/keeganlee/kandroid/api/utils/EncryptUtil.java",
    "chars": 1388,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "api/src/main/res/values/strings.xml",
    "chars": 660,
    "preview": "<!--\n Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n\n Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "app/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "app/app.iml",
    "chars": 8038,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module external.linked.project.id=\":app\" external.linked.project.path=\"$MODULE_D"
  },
  {
    "path": "app/build.gradle",
    "chars": 704,
    "preview": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 22\n    buildToolsVersion \"22.0.0\"\n\n    defaultC"
  },
  {
    "path": "app/proguard-rules.pro",
    "chars": 679,
    "preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
  },
  {
    "path": "app/src/androidTest/java/me/keeganlee/kandroid/ApplicationTest.java",
    "chars": 352,
    "preview": "package me.keeganlee.kandroid;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "chars": 1783,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n\n Licensed under the Apac"
  },
  {
    "path": "app/src/main/java/me/keeganlee/kandroid/KApplication.java",
    "chars": 1123,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "app/src/main/java/me/keeganlee/kandroid/activity/CouponListActivity.java",
    "chars": 2876,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "app/src/main/java/me/keeganlee/kandroid/activity/KBaseActivity.java",
    "chars": 1395,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "app/src/main/java/me/keeganlee/kandroid/activity/LoginActivity.java",
    "chars": 2595,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "app/src/main/java/me/keeganlee/kandroid/activity/RegisterActivity.java",
    "chars": 3966,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "app/src/main/java/me/keeganlee/kandroid/adapter/CouponListAdapter.java",
    "chars": 2792,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "app/src/main/java/me/keeganlee/kandroid/adapter/KBaseAdapter.java",
    "chars": 2254,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "app/src/main/java/me/keeganlee/kandroid/util/CouponPriceUtil.java",
    "chars": 6190,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "app/src/main/res/layout/activity_coupon_list.xml",
    "chars": 1281,
    "preview": "<!--\n Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n\n Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "app/src/main/res/layout/activity_login.xml",
    "chars": 2577,
    "preview": "<!--\n Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n\n Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "app/src/main/res/layout/activity_register.xml",
    "chars": 3151,
    "preview": "<!--\n Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n\n Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "app/src/main/res/layout/item_list_coupon.xml",
    "chars": 1825,
    "preview": "<!--\n Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n\n Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "chars": 1141,
    "preview": "<!--\n Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n\n Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "chars": 1471,
    "preview": "<!--\n Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n\n Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "chars": 798,
    "preview": "<!--\n Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n\n Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "app/src/main/res/values-w820dp/dimens.xml",
    "chars": 952,
    "preview": "<!--\n Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n\n Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "build.gradle",
    "chars": 436,
    "preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    r"
  },
  {
    "path": "core/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "core/build.gradle",
    "chars": 595,
    "preview": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 22\n    buildToolsVersion \"22.0.0\"\n\n    defaultConfi"
  },
  {
    "path": "core/core.iml",
    "chars": 7618,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module external.linked.project.id=\":core\" external.linked.project.path=\"$MODULE_"
  },
  {
    "path": "core/proguard-rules.pro",
    "chars": 679,
    "preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
  },
  {
    "path": "core/src/androidTest/java/me/keeganlee/kandroid/core/ApplicationTest.java",
    "chars": 357,
    "preview": "package me.keeganlee.kandroid.core;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a"
  },
  {
    "path": "core/src/main/AndroidManifest.xml",
    "chars": 817,
    "preview": "<!--\n Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n\n Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "core/src/main/java/me/keeganlee/kandroid/core/ActionCallbackListener.java",
    "chars": 1041,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "core/src/main/java/me/keeganlee/kandroid/core/AppAction.java",
    "chars": 1680,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "core/src/main/java/me/keeganlee/kandroid/core/AppActionImpl.java",
    "chars": 6964,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "core/src/main/java/me/keeganlee/kandroid/core/ErrorEvent.java",
    "chars": 894,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "core/src/main/res/values/strings.xml",
    "chars": 661,
    "preview": "<!--\n Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n\n Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 232,
    "preview": "#Wed Apr 10 15:27:10 PDT 2013\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
  },
  {
    "path": "gradle.properties",
    "chars": 855,
    "preview": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will o"
  },
  {
    "path": "gradlew",
    "chars": 5080,
    "preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start "
  },
  {
    "path": "gradlew.bat",
    "chars": 2404,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
  },
  {
    "path": "model/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "model/build.gradle",
    "chars": 537,
    "preview": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 22\n    buildToolsVersion \"22.0.0\"\n\n    defaultConfi"
  },
  {
    "path": "model/model.iml",
    "chars": 7414,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module external.linked.project.id=\":model\" external.linked.project.path=\"$MODULE"
  },
  {
    "path": "model/proguard-rules.pro",
    "chars": 679,
    "preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
  },
  {
    "path": "model/src/androidTest/java/me/keeganlee/kandroid/model/ApplicationTest.java",
    "chars": 358,
    "preview": "package me.keeganlee.kandroid.model;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <"
  },
  {
    "path": "model/src/main/AndroidManifest.xml",
    "chars": 818,
    "preview": "<!--\n Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n\n Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "model/src/main/java/me/keeganlee/kandroid/model/CouponBO.java",
    "chars": 2976,
    "preview": "/**\n * Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "model/src/main/res/values/strings.xml",
    "chars": 662,
    "preview": "<!--\n Copyright (C) 2015. Keegan小钢(http://keeganlee.me)\n\n Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "settings.gradle",
    "chars": 42,
    "preview": "include ':app', ':model', ':api', ':core'\n"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the keeganlee/kandroid GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 75 files (123.4 KB), approximately 33.4k tokens, and a symbol index with 121 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!