| @ -0,0 +1,9 @@ | |||||
| *.iml | |||||
| .gradle | |||||
| /local.properties | |||||
| /.idea/workspace.xml | |||||
| /.idea/libraries | |||||
| .DS_Store | |||||
| /build | |||||
| /captures | |||||
| .externalNativeBuild | |||||
| @ -0,0 +1,22 @@ | |||||
| <?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" /> | |||||
| <entry name="!?*.aj" /> | |||||
| </wildcardResourcePatterns> | |||||
| <annotationProcessing> | |||||
| <profile default="true" name="Default" enabled="false"> | |||||
| <processorPath useClasspath="true" /> | |||||
| </profile> | |||||
| </annotationProcessing> | |||||
| </component> | |||||
| </project> | |||||
| @ -0,0 +1,3 @@ | |||||
| <component name="CopyrightManager"> | |||||
| <settings default="" /> | |||||
| </component> | |||||
| @ -0,0 +1,19 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project version="4"> | |||||
| <component name="GradleSettings"> | |||||
| <option name="linkedExternalProjectsSettings"> | |||||
| <GradleProjectSettings> | |||||
| <option name="distributionType" value="DEFAULT_WRAPPED" /> | |||||
| <option name="externalProjectPath" value="$PROJECT_DIR$" /> | |||||
| <option name="modules"> | |||||
| <set> | |||||
| <option value="$PROJECT_DIR$" /> | |||||
| <option value="$PROJECT_DIR$/app" /> | |||||
| <option value="$PROJECT_DIR$/jsonrpc" /> | |||||
| </set> | |||||
| </option> | |||||
| <option name="resolveModulePerSourceSet" value="false" /> | |||||
| </GradleProjectSettings> | |||||
| </option> | |||||
| </component> | |||||
| </project> | |||||
| @ -0,0 +1,46 @@ | |||||
| <?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="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" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8 (1)" project-jdk-type="JavaSDK"> | |||||
| <output url="file://$PROJECT_DIR$/build/classes" /> | |||||
| </component> | |||||
| <component name="ProjectType"> | |||||
| <option name="id" value="Android" /> | |||||
| </component> | |||||
| </project> | |||||
| @ -0,0 +1,10 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project version="4"> | |||||
| <component name="ProjectModuleManager"> | |||||
| <modules> | |||||
| <module fileurl="file://$PROJECT_DIR$/Ytzh.iml" filepath="$PROJECT_DIR$/Ytzh.iml" /> | |||||
| <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" /> | |||||
| <module fileurl="file://$PROJECT_DIR$/jsonrpc/jsonrpc.iml" filepath="$PROJECT_DIR$/jsonrpc/jsonrpc.iml" /> | |||||
| </modules> | |||||
| </component> | |||||
| </project> | |||||
| @ -0,0 +1,12 @@ | |||||
| <?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> | |||||
| @ -0,0 +1 @@ | |||||
| /build | |||||
| @ -0,0 +1,43 @@ | |||||
| apply plugin: 'com.android.application' | |||||
| apply plugin: 'android-apt' | |||||
| android { | |||||
| compileSdkVersion 26 | |||||
| buildToolsVersion "26.0.0" | |||||
| defaultConfig { | |||||
| applicationId "com.qhclh.ytzh" | |||||
| minSdkVersion 15 | |||||
| targetSdkVersion 26 | |||||
| versionCode 1 | |||||
| versionName "1.0" | |||||
| testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" | |||||
| } | |||||
| buildTypes { | |||||
| release { | |||||
| minifyEnabled false | |||||
| proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | |||||
| } | |||||
| } | |||||
| } | |||||
| dependencies { | |||||
| compile fileTree(include: ['*.jar'], dir: 'libs') | |||||
| androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { | |||||
| exclude group: 'com.android.support', module: 'support-annotations' | |||||
| }) | |||||
| compile 'com.android.support:appcompat-v7:26.+' | |||||
| testCompile 'junit:junit:4.12' | |||||
| compile 'com.jakewharton:butterknife:8.4.0' | |||||
| apt 'com.jakewharton:butterknife-compiler:8.4.0' | |||||
| compile 'com.github.bumptech.glide:glide:3.7.0' | |||||
| compile 'com.android.support:support-v4:26.0.0-alpha1' | |||||
| compile 'com.android.support:design:26.0.0-alpha1' | |||||
| compile 'org.greenrobot:eventbus:3.0.0' | |||||
| compile 'com.umeng.analytics:analytics:latest.integration' | |||||
| compile 'pub.devrel:easypermissions:0.2.1' | |||||
| compile 'com.google.code.gson:gson:2.8.1' | |||||
| compile 'org.litepal.android:core:1.3.0' | |||||
| compile 'com.journeyapps:zxing-android-embedded:3.5.0' | |||||
| compile 'com.google.zxing:core:3.3.0' | |||||
| compile project(':jsonrpc') | |||||
| } | |||||
| @ -0,0 +1,25 @@ | |||||
| # Add project specific ProGuard rules here. | |||||
| # By default, the flags in this file are appended to flags specified | |||||
| # in F:\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 *; | |||||
| #} | |||||
| # Uncomment this to preserve the line number information for | |||||
| # debugging stack traces. | |||||
| #-keepattributes SourceFile,LineNumberTable | |||||
| # If you keep the line number information, uncomment this to | |||||
| # hide the original source file name. | |||||
| #-renamesourcefileattribute SourceFile | |||||
| @ -0,0 +1,26 @@ | |||||
| package com.qhclh.ytzh; | |||||
| import android.content.Context; | |||||
| import android.support.test.InstrumentationRegistry; | |||||
| import android.support.test.runner.AndroidJUnit4; | |||||
| import org.junit.Test; | |||||
| import org.junit.runner.RunWith; | |||||
| import static org.junit.Assert.*; | |||||
| /** | |||||
| * Instrumentation test, which will execute on an Android device. | |||||
| * | |||||
| * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> | |||||
| */ | |||||
| @RunWith(AndroidJUnit4.class) | |||||
| public class ExampleInstrumentedTest { | |||||
| @Test | |||||
| public void useAppContext() throws Exception { | |||||
| // Context of the app under test. | |||||
| Context appContext = InstrumentationRegistry.getTargetContext(); | |||||
| assertEquals("com.qhclh.ytzh", appContext.getPackageName()); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,29 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| package="com.qhclh.ytzh"> | |||||
| <!-- 网络权限 --> | |||||
| <uses-permission android:name="android.permission.INTERNET" /> | |||||
| <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | |||||
| <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> | |||||
| <uses-permission android:name="android.permission.READ_PHONE_STATE" /> | |||||
| <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> | |||||
| <application | |||||
| android:name=".home.MyApplication" | |||||
| android:allowBackup="true" | |||||
| android:icon="@mipmap/ic_launcher" | |||||
| android:label="@string/app_name" | |||||
| android:roundIcon="@mipmap/ic_launcher_round" | |||||
| android:supportsRtl="true" | |||||
| android:theme="@style/AppTheme"> | |||||
| <activity android:name=".splash.SplashActivity"> | |||||
| <intent-filter> | |||||
| <action android:name="android.intent.action.MAIN" /> | |||||
| <category android:name="android.intent.category.LAUNCHER" /> | |||||
| </intent-filter> | |||||
| </activity> | |||||
| <activity android:name=".home.MainActivity"/> | |||||
| </application> | |||||
| </manifest> | |||||
| @ -0,0 +1,9 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <litepal> | |||||
| <dbname value="BWP_ytzh.db"></dbname> | |||||
| <version value="1"></version> | |||||
| <list> | |||||
| <!--<mapping class="com.bwpsoft.bwp_khorchinmall.model.RegisterInputModel"></mapping>--> | |||||
| </list> | |||||
| </litepal> | |||||
| @ -0,0 +1,99 @@ | |||||
| package com.qhclh.ytzh.base; | |||||
| import android.os.Bundle; | |||||
| import android.support.annotation.IdRes; | |||||
| import android.support.annotation.NonNull; | |||||
| import android.support.annotation.Nullable; | |||||
| import android.support.v7.app.ActionBar; | |||||
| import android.support.v7.app.AppCompatActivity; | |||||
| import android.support.v7.widget.Toolbar; | |||||
| import android.view.View; | |||||
| import android.widget.TextView; | |||||
| import android.widget.Toast; | |||||
| import com.qhclh.ytzh.R; | |||||
| import com.qhclh.ytzh.utils.ActivityCollector; | |||||
| import com.umeng.analytics.MobclickAgent; | |||||
| import butterknife.ButterKnife; | |||||
| /** | |||||
| * Created by 青花瓷 on 2017/7/19. | |||||
| */ | |||||
| public abstract class BaseActivity extends AppCompatActivity { | |||||
| @Override | |||||
| protected void onCreate(@Nullable Bundle savedInstanceState) { | |||||
| super.onCreate(savedInstanceState); | |||||
| setContentView(setLayoutId()); | |||||
| ButterKnife.bind(this); | |||||
| ActivityCollector.addActivity(this); | |||||
| initView(); | |||||
| initData(); | |||||
| initOper(); | |||||
| } | |||||
| protected abstract int setLayoutId(); | |||||
| protected abstract void initView(); | |||||
| protected abstract void initData(); | |||||
| protected abstract void initOper(); | |||||
| ///< toolbar设置 | |||||
| public void initToolbar(Toolbar toolbar, String title, View.OnClickListener listener) { | |||||
| if (toolbar == null) { | |||||
| return; | |||||
| } | |||||
| TextView tvTitle = findById(toolbar, R.id.tv_toolbar_title); | |||||
| if (tvTitle != null) { | |||||
| tvTitle.setText(title == null ? "" : title); | |||||
| } | |||||
| setSupportActionBar(toolbar); | |||||
| ActionBar actionBar = getSupportActionBar(); | |||||
| if (actionBar != null) { | |||||
| actionBar.setDisplayShowTitleEnabled(false); | |||||
| } | |||||
| if (listener != null) { | |||||
| toolbar.setNavigationIcon(R.drawable.back); | |||||
| toolbar.setNavigationOnClickListener(listener); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| protected void onResume() { | |||||
| super.onResume(); | |||||
| try { | |||||
| MobclickAgent.onResume(this); | |||||
| } catch (Exception e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| protected void onPause() { | |||||
| super.onPause(); | |||||
| try { | |||||
| MobclickAgent.onPause(this); | |||||
| } catch (Exception e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| protected void onDestroy() { | |||||
| super.onDestroy(); | |||||
| ActivityCollector.removeActivity(this); | |||||
| } | |||||
| public void finishAll(){ActivityCollector.finishAll();} | |||||
| public void showToast(String str) { | |||||
| Toast.makeText(this, str, Toast.LENGTH_SHORT).show(); | |||||
| } | |||||
| protected <T extends View> T findById(@NonNull View view, @IdRes int resId) { | |||||
| return ButterKnife.findById(view, resId); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,31 @@ | |||||
| package com.qhclh.ytzh.configs; | |||||
| import android.content.Context; | |||||
| import android.content.SharedPreferences; | |||||
| import android.preference.PreferenceManager; | |||||
| import com.qhclh.ytzh.utils.StringUtil; | |||||
| public abstract class ConfigBase { | |||||
| String mName; | |||||
| Context mContext; | |||||
| public ConfigBase(Context context, String name) { | |||||
| mName = name; | |||||
| mContext = context; | |||||
| } | |||||
| public SharedPreferences getStore() { | |||||
| if (StringUtil.IsNullOrEmpty(mName)) { | |||||
| return PreferenceManager.getDefaultSharedPreferences(mContext); | |||||
| } else { | |||||
| return mContext.getSharedPreferences(mName, Context.MODE_PRIVATE); | |||||
| } | |||||
| } | |||||
| public abstract void GetValues(SharedPreferences store); | |||||
| public abstract void SetValues(SharedPreferences.Editor store); | |||||
| } | |||||
| @ -0,0 +1,29 @@ | |||||
| package com.qhclh.ytzh.configs; | |||||
| import android.content.Context; | |||||
| import android.content.SharedPreferences; | |||||
| public class ConfigUtil { | |||||
| public static <T extends ConfigBase> void Fill(T config) { | |||||
| SharedPreferences store = config.getStore(); | |||||
| config.GetValues(store); | |||||
| } | |||||
| public static <T extends ConfigBase> void Save(T config) { | |||||
| SharedPreferences store = config.getStore(); | |||||
| SharedPreferences.Editor editor = store.edit(); | |||||
| editor.clear(); | |||||
| config.SetValues(editor); | |||||
| editor.commit(); | |||||
| } | |||||
| public static String getServerUri(Context context) { | |||||
| ServerConfig config = new ServerConfig(context); | |||||
| Fill(config); | |||||
| return config.getServerUri(); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,32 @@ | |||||
| package com.qhclh.ytzh.configs; | |||||
| import android.content.Context; | |||||
| import android.content.SharedPreferences; | |||||
| public class LastLoginInfo extends ConfigBase { | |||||
| public LastLoginInfo(Context context) { | |||||
| super(context, "LastLoginUserInfo"); | |||||
| // TODO Auto-generated constructor stub | |||||
| } | |||||
| @Override | |||||
| public void GetValues(SharedPreferences store) { | |||||
| Username = store.getString("Username", ""); | |||||
| Password = store.getString("Password", ""); | |||||
| CurrentTimeMillis = store.getLong("CurrentTimeMillis", 0); | |||||
| } | |||||
| @Override | |||||
| public void SetValues(SharedPreferences.Editor store) { | |||||
| store.putString("Username", Username); | |||||
| store.putString("Password", Password); | |||||
| store.putLong("CurrentTimeMillis", System.currentTimeMillis()); | |||||
| } | |||||
| public String Username; | |||||
| public String Password; | |||||
| public long CurrentTimeMillis; | |||||
| } | |||||
| @ -0,0 +1,31 @@ | |||||
| package com.qhclh.ytzh.configs; | |||||
| import android.content.Context; | |||||
| import android.content.SharedPreferences; | |||||
| public class LoginUserConfig extends ConfigBase { | |||||
| public LoginUserConfig(Context context) { | |||||
| super(context, "LoginUserConfig"); | |||||
| } | |||||
| @Override | |||||
| public void GetValues(SharedPreferences store) { | |||||
| Username = store.getString("Username", ""); | |||||
| Password = store.getString("Password", ""); | |||||
| DisplayName = store.getString("DisplayName", ""); | |||||
| } | |||||
| @Override | |||||
| public void SetValues(SharedPreferences.Editor store) { | |||||
| store.putString("Username", Username); | |||||
| store.putString("Password", Password); | |||||
| store.putString("DisplayName", DisplayName); | |||||
| } | |||||
| public String Username; | |||||
| public String Password; | |||||
| public String DisplayName; | |||||
| } | |||||
| @ -0,0 +1,93 @@ | |||||
| package com.qhclh.ytzh.configs; | |||||
| import android.content.Context; | |||||
| import android.content.SharedPreferences; | |||||
| import org.forks.jsonrpc.RpcFacade; | |||||
| public class ServerConfig extends ConfigBase { | |||||
| String JPUSH_TAG_KEY="jpushtagkey"; | |||||
| String SYSTEMSET_ENABLE_SCAN="systemset_enable_scan"; | |||||
| String ENABLE_Catogray="systemset_enable_catogray"; | |||||
| String ENABLE_MoneyTipe="systemset_enable_moneytipe"; | |||||
| String ENABLE_StoreGoods="systemset_enable_storegoods"; | |||||
| public ServerConfig(Context context) { | |||||
| super(context, ""); | |||||
| } | |||||
| @Override | |||||
| public void GetValues(SharedPreferences store) { | |||||
| mServerUri = store.getString(RpcFacade.SERVER_URI_PREFKEY, ""); | |||||
| mTag = store.getString(JPUSH_TAG_KEY, ""); | |||||
| mEnableGoodsScan = store.getBoolean(SYSTEMSET_ENABLE_SCAN,false); | |||||
| mEnableGoodsCatogray = store.getBoolean(ENABLE_Catogray,false); | |||||
| mEnablMoneyTipe = store.getBoolean(ENABLE_MoneyTipe,false); | |||||
| mEnablStoreGoods = store.getBoolean(ENABLE_StoreGoods,false); | |||||
| } | |||||
| @Override | |||||
| public void SetValues(SharedPreferences.Editor store) { | |||||
| store.putString(RpcFacade.SERVER_URI_PREFKEY, getServerUri()); | |||||
| store.putString(JPUSH_TAG_KEY, getTag()); | |||||
| store.putBoolean(SYSTEMSET_ENABLE_SCAN,getEnableGoodsScan()); | |||||
| store.putBoolean(ENABLE_Catogray,getEnableGoodsCatogray()); | |||||
| store.putBoolean(ENABLE_MoneyTipe,getEnableMoneyTipe()); | |||||
| store.putBoolean(ENABLE_StoreGoods,getEnableStoreGoods()); | |||||
| } | |||||
| private String mServerUri; | |||||
| private String mTag; | |||||
| public String getServerUri() { | |||||
| return mServerUri; | |||||
| } | |||||
| public void setServerUri(String value) { | |||||
| mServerUri = value; | |||||
| } | |||||
| public String getTag() { | |||||
| return mTag; | |||||
| } | |||||
| public void setTag(String value) { | |||||
| mTag = value; | |||||
| } | |||||
| private boolean mEnableGoodsScan; | |||||
| public boolean getEnableGoodsScan() { | |||||
| return mEnableGoodsScan; | |||||
| } | |||||
| public void setEnableGoodsScang(boolean value) { | |||||
| mEnableGoodsScan = value; | |||||
| } | |||||
| private boolean mEnableGoodsCatogray; | |||||
| public boolean getEnableGoodsCatogray() { | |||||
| return mEnableGoodsCatogray; | |||||
| } | |||||
| public void setEnableGoodsCatogray(boolean value) { | |||||
| mEnableGoodsCatogray = value; | |||||
| } | |||||
| private boolean mEnablMoneyTipe; | |||||
| public boolean getEnableMoneyTipe() { | |||||
| return mEnablMoneyTipe; | |||||
| } | |||||
| public void setEnableMoneyTipe(boolean value) { | |||||
| mEnablMoneyTipe = value; | |||||
| } | |||||
| private boolean mEnablStoreGoods; | |||||
| public boolean getEnableStoreGoods() { | |||||
| return mEnablStoreGoods; | |||||
| } | |||||
| public void setEnableStoreGoods(boolean value) { | |||||
| mEnablStoreGoods = value; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,43 @@ | |||||
| package com.qhclh.ytzh.home; | |||||
| import android.support.v7.widget.Toolbar; | |||||
| import android.view.View; | |||||
| import android.widget.TextView; | |||||
| import com.qhclh.ytzh.R; | |||||
| import com.qhclh.ytzh.base.BaseActivity; | |||||
| import butterknife.BindView; | |||||
| public class MainActivity extends BaseActivity { | |||||
| @BindView(R.id.textaaa) | |||||
| TextView textView; | |||||
| @BindView(R.id.toolbar) | |||||
| Toolbar mToolbar; | |||||
| @Override | |||||
| protected int setLayoutId() { | |||||
| return R.layout.act_main; | |||||
| } | |||||
| @Override | |||||
| protected void initView() { | |||||
| initToolbar(mToolbar, "aaaaa", new View.OnClickListener() { | |||||
| @Override | |||||
| public void onClick(View view) { | |||||
| finish(); | |||||
| } | |||||
| }); | |||||
| } | |||||
| @Override | |||||
| protected void initData() { | |||||
| textView.setText("aaaaaaaaaaa"); | |||||
| } | |||||
| @Override | |||||
| protected void initOper() { | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,61 @@ | |||||
| package com.qhclh.ytzh.home; | |||||
| import android.app.Activity; | |||||
| import com.qhclh.ytzh.configs.ConfigUtil; | |||||
| import com.qhclh.ytzh.configs.LastLoginInfo; | |||||
| import com.qhclh.ytzh.configs.ServerConfig; | |||||
| import org.forks.jsonrpc.RpcFacade; | |||||
| import org.litepal.LitePalApplication; | |||||
| import java.util.LinkedList; | |||||
| import java.util.List; | |||||
| /** | |||||
| * Created by 青花瓷 on 2017/11/23. | |||||
| */ | |||||
| public class MyApplication extends LitePalApplication { | |||||
| private final String NAME ="ytzh"; | |||||
| static LastLoginInfo mLoginInfo; | |||||
| private static MyApplication instance; | |||||
| String url= RpcUrl.BaseAppUrl; | |||||
| ServerConfig config; | |||||
| private List<Activity> activityList = new LinkedList<Activity>(); | |||||
| public static MyApplication getInstance(){ | |||||
| if (null == instance) { | |||||
| instance = new MyApplication(); | |||||
| } | |||||
| return instance; | |||||
| } | |||||
| @Override | |||||
| public void onCreate() { | |||||
| super.onCreate(); | |||||
| mLoginInfo = new LastLoginInfo(this); | |||||
| ConfigUtil.Fill(mLoginInfo); | |||||
| ///< 先暂时必须设置URL | |||||
| config = new ServerConfig(this); | |||||
| config.setServerUri(url); | |||||
| ConfigUtil.Save(config); | |||||
| RpcFacade.init(this.getApplicationContext(),NAME); | |||||
| } | |||||
| public void exit() { | |||||
| for (Activity activity : activityList) { | |||||
| activity.finish(); | |||||
| } | |||||
| System.exit(0); | |||||
| } | |||||
| public static void ClearLogin() { | |||||
| mLoginInfo.Username = ""; | |||||
| ConfigUtil.Save(mLoginInfo); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,11 @@ | |||||
| package com.qhclh.ytzh.home; | |||||
| public class RpcUrl { | |||||
| ///< 域名开发 | |||||
| // public static String BaseAppUrl = "http://192.168.1.9/MarketPlace/"; | |||||
| ///< 正式服务器地址 | |||||
| public static String BaseAppUrl = "http://47.93.175.172/Marketplace/"; | |||||
| ///< 外网测试 | |||||
| // public static String BaseAppUrl = "http://111.198.52.49:81/MarketPlace/"; | |||||
| } | |||||
| @ -0,0 +1,7 @@ | |||||
| package com.qhclh.ytzh.interfaces; | |||||
| public interface OnPromptListener { | |||||
| public void OnPrompt(String prompt); | |||||
| } | |||||
| @ -0,0 +1,66 @@ | |||||
| package com.qhclh.ytzh.splash; | |||||
| import android.content.Intent; | |||||
| import android.view.animation.AlphaAnimation; | |||||
| import android.view.animation.Animation; | |||||
| import android.widget.LinearLayout; | |||||
| import com.qhclh.ytzh.R; | |||||
| import com.qhclh.ytzh.base.BaseActivity; | |||||
| import com.qhclh.ytzh.home.MainActivity; | |||||
| import butterknife.BindView; | |||||
| /** | |||||
| * Created by 青花瓷 on 2017/11/23. | |||||
| */ | |||||
| public class SplashActivity extends BaseActivity { | |||||
| @BindView(R.id.ll_splash) | |||||
| LinearLayout ll_splash; | |||||
| @Override | |||||
| protected int setLayoutId() { | |||||
| return R.layout.act_splash; | |||||
| } | |||||
| @Override | |||||
| protected void initView() { | |||||
| } | |||||
| @Override | |||||
| protected void initData() { | |||||
| } | |||||
| @Override | |||||
| protected void initOper() { | |||||
| //渐变展示启动屏 | |||||
| AlphaAnimation aa = new AlphaAnimation(0.3f, 1.0f); | |||||
| aa.setDuration(2000); | |||||
| ll_splash.startAnimation(aa); | |||||
| aa.setAnimationListener(new Animation.AnimationListener() { | |||||
| @Override | |||||
| public void onAnimationEnd(Animation arg0) { | |||||
| redirectTo(); | |||||
| } | |||||
| @Override | |||||
| public void onAnimationRepeat(Animation animation) { | |||||
| } | |||||
| @Override | |||||
| public void onAnimationStart(Animation animation) { | |||||
| } | |||||
| }); | |||||
| } | |||||
| private void redirectTo() { | |||||
| startActivity(new Intent(this, MainActivity.class)); | |||||
| finish(); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,115 @@ | |||||
| package com.qhclh.ytzh.tasks; | |||||
| import android.app.ProgressDialog; | |||||
| import android.content.Context; | |||||
| import android.os.AsyncTask; | |||||
| import android.util.Log; | |||||
| import com.qhclh.ytzh.utils.DialogUtil; | |||||
| import java.util.concurrent.Callable; | |||||
| public abstract class CallableTask implements Callable<Object> { | |||||
| final String tag = "CallableTask"; | |||||
| Boolean mIsRunning = false; | |||||
| protected abstract void successUI(); | |||||
| void failUI(Exception e) { | |||||
| Log.e(tag, e.getMessage(), e); | |||||
| if(e.getMessage().contains("Unauthorized")) | |||||
| { | |||||
| return; | |||||
| } | |||||
| if(e.getMessage().contains("Socket")) | |||||
| { | |||||
| return; | |||||
| } | |||||
| if(e.getMessage().contains("cannot")) | |||||
| { | |||||
| return; | |||||
| } | |||||
| DialogUtil.AutoHiddenAlert(mContext,e.getMessage()); | |||||
| // DialogUtil.Alert(mContext, e.getMessage()); | |||||
| } | |||||
| Context mContext; | |||||
| ProgressDialog mDialog; | |||||
| String mMessage; | |||||
| public CallableTask(Context context, String message) { | |||||
| mContext = context; | |||||
| mMessage = message; | |||||
| } | |||||
| public final void execute() { | |||||
| if (mIsRunning) { | |||||
| Log.w(tag, "任务重复执行"); | |||||
| return; | |||||
| } | |||||
| new Task().execute(); | |||||
| } | |||||
| class Task extends AsyncTask<Void, Void, Void> { | |||||
| Exception mException; | |||||
| @Override | |||||
| protected Void doInBackground(Void... params) { | |||||
| try { | |||||
| mIsRunning = true; | |||||
| call(); | |||||
| } catch (Exception ex) { | |||||
| mException = ex; | |||||
| cancel(false); | |||||
| } | |||||
| return null; | |||||
| } | |||||
| @Override | |||||
| protected void onCancelled() { | |||||
| super.onCancelled(); | |||||
| endExecute(); | |||||
| failUI(mException); | |||||
| } | |||||
| @Override | |||||
| protected void onPostExecute(Void result) { | |||||
| super.onPostExecute(result); | |||||
| endExecute(); | |||||
| successUI(); | |||||
| } | |||||
| public void endExecute() { | |||||
| // if (mDialog != null && isValidContext(mContext) && mDialog.isShowing()) { | |||||
| if (mDialog != null) { | |||||
| mDialog.dismiss(); | |||||
| } | |||||
| mIsRunning = false; | |||||
| } | |||||
| @Override | |||||
| protected void onPreExecute() { | |||||
| super.onPreExecute(); | |||||
| // if (isValidContext(mContext)) { | |||||
| mDialog = ProgressDialog.show(mContext, "请稍等", mMessage, true); | |||||
| // } | |||||
| } | |||||
| } | |||||
| // private boolean isValidContext (Context c){ | |||||
| // Activity a = (Activity)c; | |||||
| // if (a.isDestroyed() || a.isFinishing()){ | |||||
| // return false; | |||||
| // }else{ | |||||
| // return true; | |||||
| // } | |||||
| // } | |||||
| } | |||||
| @ -0,0 +1,39 @@ | |||||
| package com.qhclh.ytzh.tasks; | |||||
| import android.content.Context; | |||||
| import android.content.DialogInterface; | |||||
| import android.view.View; | |||||
| import com.qhclh.ytzh.utils.DialogUtil; | |||||
| import com.qhclh.ytzh.utils.StringUtil; | |||||
| public abstract class ViewOnClickTask extends CallableTask implements | |||||
| View.OnClickListener { | |||||
| private String mPrompt; | |||||
| public ViewOnClickTask(Context context, String message) { | |||||
| super(context, message); | |||||
| } | |||||
| public void setPrompt(String prompt) { | |||||
| mPrompt = prompt; | |||||
| } | |||||
| public void onClick(View v) { | |||||
| if (!StringUtil.IsNullOrEmpty(mPrompt)) { | |||||
| DialogUtil.Confirm(v.getContext(), mPrompt, "确定", "取消", | |||||
| new DialogInterface.OnClickListener() { | |||||
| public void onClick(DialogInterface dialog, int which) { | |||||
| ViewOnClickTask.this.execute(); | |||||
| } | |||||
| }); | |||||
| } else { | |||||
| super.execute(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,36 @@ | |||||
| package com.qhclh.ytzh.utils; | |||||
| import android.app.Activity; | |||||
| import java.util.ArrayList; | |||||
| import java.util.List; | |||||
| /** | |||||
| * Created by jiangxk on 2016/11/29. | |||||
| */ | |||||
| public class ActivityCollector { | |||||
| private ActivityCollector() { | |||||
| } | |||||
| private static List<Activity> sActivityList = new ArrayList<>(); | |||||
| public static void addActivity(Activity activity) { | |||||
| if (!sActivityList.contains(activity)) { | |||||
| sActivityList.add(activity); | |||||
| } | |||||
| } | |||||
| public static void removeActivity(Activity activity) { | |||||
| sActivityList.remove(activity); | |||||
| } | |||||
| public static void finishAll() { | |||||
| for (Activity activity : sActivityList) { | |||||
| if (!activity.isFinishing()) { | |||||
| activity.finish(); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,72 @@ | |||||
| package com.qhclh.ytzh.utils; | |||||
| import java.text.ParseException; | |||||
| import java.text.SimpleDateFormat; | |||||
| import java.util.Calendar; | |||||
| import java.util.Date; | |||||
| import java.util.Locale; | |||||
| public final class DateTimeUtil { | |||||
| static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); | |||||
| static SimpleDateFormat dateFormatDetail = new SimpleDateFormat("yyyy-MM-dd HH:mm"); | |||||
| public static Date ParseDate(String dateStr) throws ParseException { | |||||
| return dateFormat.parse(dateStr); | |||||
| } | |||||
| public static Date ParseDateDetail(String dateStr) throws ParseException { | |||||
| return dateFormatDetail.parse(dateStr); | |||||
| } | |||||
| public static Date SafeParseDate(String dateStr) { | |||||
| try { | |||||
| if (StringUtil.IsNullOrEmpty(dateStr)) { | |||||
| return new Date(); | |||||
| } | |||||
| return ParseDate(dateStr); | |||||
| } catch (ParseException e) { | |||||
| return new Date(); | |||||
| } | |||||
| } | |||||
| public static Date SafeParseDateDetail(String dateStr) { | |||||
| try { | |||||
| if (StringUtil.IsNullOrEmpty(dateStr)) { | |||||
| return new Date(); | |||||
| } | |||||
| return ParseDateDetail(dateStr); | |||||
| } catch (ParseException e) { | |||||
| return new Date(); | |||||
| } | |||||
| } | |||||
| public static Calendar SafeParseCalendar(String dateStr) { | |||||
| Calendar calendar = Calendar.getInstance(Locale.getDefault()); | |||||
| calendar.setTime(SafeParseDate(dateStr)); | |||||
| return calendar; | |||||
| } | |||||
| public static String FormatDate(Calendar calendar) { | |||||
| return dateFormat.format(calendar.getTime()); | |||||
| } | |||||
| public static String FormatDate(Date date) { | |||||
| if (date == null) { | |||||
| return ""; | |||||
| } | |||||
| return dateFormat.format(date); | |||||
| } | |||||
| public static String FormatDateDetail(Date date) { | |||||
| if (date == null) { | |||||
| return ""; | |||||
| } | |||||
| return dateFormatDetail.format(date); | |||||
| } | |||||
| public static Date RemoveTime(Date time) { | |||||
| return SafeParseDate(FormatDate(time)); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,189 @@ | |||||
| package com.qhclh.ytzh.utils; | |||||
| import android.app.Activity; | |||||
| import android.app.DatePickerDialog; | |||||
| import android.app.ProgressDialog; | |||||
| import android.content.Context; | |||||
| import android.content.DialogInterface; | |||||
| import android.os.Handler; | |||||
| import android.support.v7.app.AlertDialog; | |||||
| import android.view.View; | |||||
| import android.widget.DatePicker; | |||||
| import android.widget.EditText; | |||||
| import android.widget.TextView; | |||||
| import android.widget.Toast; | |||||
| import com.qhclh.ytzh.interfaces.OnPromptListener; | |||||
| import java.util.Calendar; | |||||
| import java.util.Date; | |||||
| import java.util.concurrent.Callable; | |||||
| public class DialogUtil { | |||||
| abstract static class ProgressDialogHelper { | |||||
| Context mContext; | |||||
| String mMessage; | |||||
| Callable<?> mRunnable; | |||||
| Handler mHandler = new Handler(); | |||||
| Callable<?> mCallableAfterSuccess; | |||||
| public void setCallableAfterSuccess(Callable<?> callable) { | |||||
| mCallableAfterSuccess = callable; | |||||
| } | |||||
| public ProgressDialogHelper(Context context, String message, | |||||
| Callable<?> runnable) { | |||||
| this.mContext = context; | |||||
| this.mMessage = message; | |||||
| this.mRunnable = runnable; | |||||
| } | |||||
| ProgressDialog mDialog; | |||||
| public void Start() { | |||||
| mDialog = ProgressDialog.show(mContext, "请稍等", mMessage, true); | |||||
| Thread thread = new Thread() { | |||||
| @Override | |||||
| public void run() { | |||||
| try { | |||||
| mRunnable.call(); | |||||
| } catch (Exception ex) { | |||||
| ex.printStackTrace(); | |||||
| Runnable runnable = new Runnable() { | |||||
| public void run() { | |||||
| DialogUtil.Alert(mContext, mMessage); | |||||
| } | |||||
| String mMessage; | |||||
| public Runnable setMessage(String message) { | |||||
| mMessage = message; | |||||
| return this; | |||||
| } | |||||
| }.setMessage(ex.getMessage()); | |||||
| mHandler.post(runnable); | |||||
| } finally { | |||||
| mDialog.dismiss(); | |||||
| } | |||||
| } | |||||
| }; | |||||
| thread.start(); | |||||
| } | |||||
| } | |||||
| public static void Progress(Context context, String message, | |||||
| Callable<?> callable) { | |||||
| new ProgressDialogHelper(context, message, callable) { | |||||
| }.Start(); | |||||
| } | |||||
| public static void Progress(Context context, String message, | |||||
| Callable<?> callable, Callable<?> callableAfterSuccess) { | |||||
| ProgressDialogHelper helper = new ProgressDialogHelper(context, | |||||
| message, callable) { | |||||
| }; | |||||
| helper.Start(); | |||||
| } | |||||
| public static void Alert(Context context, String message) { | |||||
| new AlertDialog.Builder(context).setMessage(message) | |||||
| .setPositiveButton("确定", null).show(); | |||||
| } | |||||
| public static void AlertAndFinish(final Activity activity, String message) { | |||||
| new AlertDialog.Builder(activity).setMessage(message) | |||||
| .setPositiveButton("确定", new DialogInterface.OnClickListener() { | |||||
| public void onClick(DialogInterface dialog, int which) { | |||||
| activity.finish(); | |||||
| } | |||||
| }).show(); | |||||
| } | |||||
| public static void Alert(View view, String message) { | |||||
| Alert(view.getContext(), message); | |||||
| } | |||||
| public static void AutoHiddenAlert(Context context, String message) { | |||||
| Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); | |||||
| } | |||||
| public static void AutoHiddenAlert(View view, String message) { | |||||
| AutoHiddenAlert(view.getContext(), message); | |||||
| } | |||||
| public interface SetDate { | |||||
| void setDate(Date date); | |||||
| } | |||||
| public static void AddSelectDateDialog(TextView arg0, | |||||
| final SetDate onsetdate) { | |||||
| arg0.setOnClickListener(new View.OnClickListener() { | |||||
| public void onClick(final View view) { | |||||
| final TextView textView = (TextView) view; | |||||
| DatePickerDialog.OnDateSetListener onDateSetListener = new DatePickerDialog.OnDateSetListener() { | |||||
| public void onDateSet(DatePicker datePicker, int year, | |||||
| int month, int day) { | |||||
| Calendar calendar = Calendar.getInstance(); | |||||
| calendar.set(year, month, day); | |||||
| textView.setText(DateTimeUtil.FormatDate(calendar)); | |||||
| onsetdate.setDate(calendar.getTime()); | |||||
| } | |||||
| }; | |||||
| String dateStr = textView.getText().toString(); | |||||
| Calendar calendar = DateTimeUtil.SafeParseCalendar(dateStr); | |||||
| new DatePickerDialog(view.getContext(), onDateSetListener, | |||||
| calendar.get(Calendar.YEAR), calendar | |||||
| .get(Calendar.MONTH), calendar | |||||
| .get(Calendar.DAY_OF_MONTH)).show(); | |||||
| } | |||||
| }); | |||||
| } | |||||
| public static void Prompt(Context context, String title, String message, | |||||
| String inputValue, int inputType, final OnPromptListener onprompt) { | |||||
| final EditText input = new EditText(context); | |||||
| input.setText(inputValue); | |||||
| input.setInputType(inputType); | |||||
| AlertDialog.Builder builder = new AlertDialog.Builder(context); | |||||
| builder.setTitle(title).setMessage(message).setView(input) | |||||
| .setPositiveButton("确定", new DialogInterface.OnClickListener() { | |||||
| public void onClick(DialogInterface arg0, int arg1) { | |||||
| String prompt = input.getText().toString(); | |||||
| onprompt.OnPrompt(prompt); | |||||
| } | |||||
| }) | |||||
| .setNegativeButton("取消", new DialogInterface.OnClickListener() { | |||||
| public void onClick(DialogInterface dialog, int which) { | |||||
| } | |||||
| }).show(); | |||||
| } | |||||
| public static void Confirm(Context context, String title, | |||||
| String trueString, String falseString, | |||||
| DialogInterface.OnClickListener onConfirm) { | |||||
| new AlertDialog.Builder(context).setTitle(title) | |||||
| .setPositiveButton(trueString, onConfirm) | |||||
| .setNegativeButton(falseString, null).show(); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,30 @@ | |||||
| package com.qhclh.ytzh.utils; | |||||
| import android.content.Context; | |||||
| import java.math.BigDecimal; | |||||
| public class StringUtil { | |||||
| public static Boolean IsNullOrEmpty(String s) { | |||||
| return s == null || s.length() == 0; | |||||
| } | |||||
| public String GetResource(Context context, int resId) { | |||||
| return context.getString(resId); | |||||
| } | |||||
| public static String ToString(Object object) { | |||||
| if (object == null) { | |||||
| return ""; | |||||
| } | |||||
| return String.format("%s", object); | |||||
| } | |||||
| public static BigDecimal ParseMoney(String strValue) { | |||||
| if (IsNullOrEmpty(strValue)) { | |||||
| return null; | |||||
| } | |||||
| return BigDecimal.valueOf(Double.valueOf(strValue)); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,9 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <set xmlns:android="http://schemas.android.com/apk/res/android"> | |||||
| <translate | |||||
| android:duration="300" | |||||
| android:fillAfter="true" | |||||
| android:fromXDelta="-100%p" | |||||
| android:interpolator="@android:anim/accelerate_interpolator" | |||||
| android:toXDelta="0"/> | |||||
| </set> | |||||
| @ -0,0 +1,9 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <set xmlns:android="http://schemas.android.com/apk/res/android"> | |||||
| <translate | |||||
| android:duration="300" | |||||
| android:fillAfter="true" | |||||
| android:fromXDelta="100%p" | |||||
| android:interpolator="@android:anim/accelerate_interpolator" | |||||
| android:toXDelta="0"/> | |||||
| </set> | |||||
| @ -0,0 +1,9 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <set xmlns:android="http://schemas.android.com/apk/res/android"> | |||||
| <translate | |||||
| android:duration="300" | |||||
| android:fillAfter="true" | |||||
| android:fromXDelta="0" | |||||
| android:interpolator="@android:anim/accelerate_interpolator" | |||||
| android:toXDelta="-100%p"/> | |||||
| </set> | |||||
| @ -0,0 +1,9 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <set xmlns:android="http://schemas.android.com/apk/res/android"> | |||||
| <translate | |||||
| android:duration="300" | |||||
| android:fillAfter="true" | |||||
| android:fromXDelta="0" | |||||
| android:interpolator="@android:anim/accelerate_interpolator" | |||||
| android:toXDelta="100%p"/> | |||||
| </set> | |||||
| @ -0,0 +1,12 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| android:orientation="vertical" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="match_parent"> | |||||
| <include layout="@layout/include_tool_bar"></include> | |||||
| <TextView | |||||
| android:id="@+id/textaaa" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" /> | |||||
| </LinearLayout> | |||||
| @ -0,0 +1,9 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| android:id="@+id/ll_splash" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="match_parent" | |||||
| android:background="@mipmap/ic_launcher" | |||||
| android:orientation="vertical"> | |||||
| </LinearLayout> | |||||
| @ -0,0 +1,6 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <View xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="1px" | |||||
| android:background="@color/grey_a3a3a3"> | |||||
| </View> | |||||
| @ -0,0 +1,45 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_gravity="bottom" | |||||
| android:background="@color/white" | |||||
| android:orientation="horizontal"> | |||||
| <!-- 底部导航栏--> | |||||
| <LinearLayout | |||||
| android:id="@+id/ll_gonghuoshang" | |||||
| style="@style/TabLayout"> | |||||
| <ImageView | |||||
| android:id="@+id/iv_gonghuoshang" | |||||
| style="@style/TabIcon" /> | |||||
| <TextView | |||||
| android:id="@+id/tv_gonghuoshang" | |||||
| style="@style/TabText" | |||||
| android:text="@string/app_name" /> | |||||
| </LinearLayout> | |||||
| <LinearLayout | |||||
| android:id="@+id/ll_gouwuche" | |||||
| style="@style/TabLayout"> | |||||
| <ImageView | |||||
| android:id="@+id/iv_gouwuche" | |||||
| style="@style/TabIcon" /> | |||||
| <TextView | |||||
| android:id="@+id/tv_gouwuche" | |||||
| style="@style/TabText" | |||||
| android:text="@string/app_name" /> | |||||
| </LinearLayout> | |||||
| <LinearLayout | |||||
| android:id="@+id/ll_dingdan" | |||||
| style="@style/TabLayout"> | |||||
| <ImageView | |||||
| android:id="@+id/iv_dingdan" | |||||
| style="@style/TabIcon" /> | |||||
| <TextView | |||||
| android:id="@+id/tv_dingdan" | |||||
| style="@style/TabText" | |||||
| android:text="@string/app_name" /> | |||||
| </LinearLayout> | |||||
| </LinearLayout> | |||||
| @ -0,0 +1,17 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <!-- toolbar--> | |||||
| <android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| xmlns:app="http://schemas.android.com/apk/res-auto" | |||||
| android:id="@+id/toolbar" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="?attr/actionBarSize" | |||||
| android:background="@color/colorPrimary" | |||||
| android:theme="@style/ThemeOverlay.AppCompat.ActionBar" | |||||
| android:elevation="@dimen/dp_2" | |||||
| app:popupTheme="@style/ThemeOverlay.AppCompat.Light"> | |||||
| <TextView | |||||
| android:id="@+id/tv_toolbar_title" | |||||
| style="@style/TextView_18_black_282828" | |||||
| android:layout_gravity="center"/> | |||||
| </android.support.v7.widget.Toolbar> | |||||
| @ -0,0 +1,24 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <resources> | |||||
| <!--标题栏 --> | |||||
| <color name="colorPrimary">#E3383B</color> | |||||
| <!-- 状态栏--> | |||||
| <color name="colorPrimaryDark">#E3383B</color> | |||||
| <!-- 各种控件默认选中颜色--> | |||||
| <color name="colorAccent">#E3383B</color> | |||||
| <color name="grey999">#999999</color> | |||||
| <color name="greyeee">#EEEEEE</color> | |||||
| <color name="red">#E3383B</color> | |||||
| <color name="red_e6">#E6E3383B</color> | |||||
| <color name="red_ee">#eeee4242</color> | |||||
| <color name="white">#ffffff</color> | |||||
| <color name="black">#000000</color> | |||||
| <color name="grey_767c82">#767c82</color> | |||||
| <color name="black_282828">#282828</color> | |||||
| <color name="tran">#00000000</color> | |||||
| <color name="grey_a3a3a3">#a3a3a3</color> | |||||
| </resources> | |||||
| @ -0,0 +1,174 @@ | |||||
| <resources> | |||||
| <!-- Default screen margins, per the Android Design guidelines. --> | |||||
| <dimen name="activity_horizontal_margin">16dp</dimen> | |||||
| <dimen name="activity_vertical_margin">16dp</dimen> | |||||
| <!--text大小--> | |||||
| <dimen name="text_size_6">6sp</dimen> | |||||
| <dimen name="text_size_10">10sp</dimen> | |||||
| <dimen name="text_size_12">12sp</dimen> | |||||
| <dimen name="text_size_14">14sp</dimen> | |||||
| <dimen name="text_size_16">16sp</dimen> | |||||
| <dimen name="text_size_18">18sp</dimen> | |||||
| <dimen name="text_size_20">20sp</dimen> | |||||
| <dimen name="text_size_22">22sp</dimen> | |||||
| <dimen name="text_size_24">24sp</dimen> | |||||
| <dimen name="text_size_26">26sp</dimen> | |||||
| <dimen name="text_size_30">30sp</dimen> | |||||
| <dimen name="text_size_32">32sp</dimen> | |||||
| <dimen name="text_size_34">34sp</dimen> | |||||
| <dimen name="text_size_38">38sp</dimen> | |||||
| <dimen name="text_size_48">48sp</dimen> | |||||
| <dimen name="dp_0">0dp</dimen> | |||||
| <dimen name="dp_0.1">0.1dp</dimen> | |||||
| <dimen name="dp_0.2">0.2dp</dimen> | |||||
| <dimen name="dp_0.4">0.4dp</dimen> | |||||
| <dimen name="dp_0.5">0.5dp</dimen> | |||||
| <dimen name="dp_1">1dp</dimen> | |||||
| <dimen name="dp_2">2dp</dimen> | |||||
| <dimen name="dp_3">3dp</dimen> | |||||
| <dimen name="dp_5">5dp</dimen> | |||||
| <dimen name="dp_6">6dp</dimen> | |||||
| <dimen name="dp_7">7dp</dimen> | |||||
| <dimen name="dp_8">8dp</dimen> | |||||
| <dimen name="dp_10">10dp</dimen> | |||||
| <dimen name="dp_11">11dp</dimen> | |||||
| <dimen name="dp_12">12dp</dimen> | |||||
| <dimen name="dp_13">13dp</dimen> | |||||
| <dimen name="dp_14">14dp</dimen> | |||||
| <dimen name="dp_15">15dp</dimen> | |||||
| <dimen name="dp_16">16dp</dimen> | |||||
| <dimen name="dp_18">18dp</dimen> | |||||
| <dimen name="dp_20">20dp</dimen> | |||||
| <dimen name="dp_22">22dp</dimen> | |||||
| <dimen name="dp_24">24dp</dimen> | |||||
| <dimen name="dp_25">25dp</dimen> | |||||
| <dimen name="dp_26">26dp</dimen> | |||||
| <dimen name="dp_28">28dp</dimen> | |||||
| <dimen name="dp_30">30dp</dimen> | |||||
| <dimen name="dp_32">32dp</dimen> | |||||
| <dimen name="dp_34">34dp</dimen> | |||||
| <dimen name="dp_35">35dp</dimen> | |||||
| <dimen name="dp_36">36dp</dimen> | |||||
| <dimen name="dp_38">38dp</dimen> | |||||
| <dimen name="dp_40">40dp</dimen> | |||||
| <dimen name="dp_42">42dp</dimen> | |||||
| <dimen name="dp_44">44dp</dimen> | |||||
| <dimen name="dp_46">46dp</dimen> | |||||
| <dimen name="dp_48">48dp</dimen> | |||||
| <dimen name="dp_50">50dp</dimen> | |||||
| <dimen name="dp_52">52dp</dimen> | |||||
| <dimen name="dp_53">53dp</dimen> | |||||
| <dimen name="dp_54">54dp</dimen> | |||||
| <dimen name="dp_55">55dp</dimen> | |||||
| <dimen name="dp_56">56dp</dimen> | |||||
| <dimen name="dp_59">59dp</dimen> | |||||
| <dimen name="dp_60">60dp</dimen> | |||||
| <dimen name="dp_62">62dp</dimen> | |||||
| <dimen name="dp_64">64dp</dimen> | |||||
| <dimen name="dp_65">65dp</dimen> | |||||
| <dimen name="dp_66">66dp</dimen> | |||||
| <dimen name="dp_68">68dp</dimen> | |||||
| <dimen name="dp_69">69dp</dimen> | |||||
| <dimen name="dp_70">70dp</dimen> | |||||
| <dimen name="dp_72">72dp</dimen> | |||||
| <dimen name="dp_74">74dp</dimen> | |||||
| <dimen name="dp_75">75dp</dimen> | |||||
| <dimen name="dp_78">78dp</dimen> | |||||
| <dimen name="dp_80">80dp</dimen> | |||||
| <dimen name="dp_82">82dp</dimen> | |||||
| <dimen name="dp_84">84dp</dimen> | |||||
| <dimen name="dp_85">85dp</dimen> | |||||
| <dimen name="dp_86">86dp</dimen> | |||||
| <dimen name="dp_88">88dp</dimen> | |||||
| <dimen name="dp_90">90dp</dimen> | |||||
| <dimen name="dp_94">94dp</dimen> | |||||
| <dimen name="dp_95">95dp</dimen> | |||||
| <dimen name="dp_96">96dp</dimen> | |||||
| <dimen name="dp_98">98dp</dimen> | |||||
| <dimen name="dp_100">100dp</dimen> | |||||
| <dimen name="dp_102">102dp</dimen> | |||||
| <dimen name="dp_103">103dp</dimen> | |||||
| <dimen name="dp_105">105dp</dimen> | |||||
| <dimen name="dp_106">106dp</dimen> | |||||
| <dimen name="dp_108">108dp</dimen> | |||||
| <dimen name="dp_110">110dp</dimen> | |||||
| <dimen name="dp_112">112dp</dimen> | |||||
| <dimen name="dp_114">114dp</dimen> | |||||
| <dimen name="dp_117">117dp</dimen> | |||||
| <dimen name="dp_120">120dp</dimen> | |||||
| <dimen name="dp_122">122dp</dimen> | |||||
| <dimen name="dp_124">124dp</dimen> | |||||
| <dimen name="dp_129">129dp</dimen> | |||||
| <dimen name="dp_130">130dp</dimen> | |||||
| <dimen name="dp_138">138dp</dimen> | |||||
| <dimen name="dp_150">150dp</dimen> | |||||
| <dimen name="dp_154">154dp</dimen> | |||||
| <dimen name="dp_156">156dp</dimen> | |||||
| <dimen name="dp_157">157dp</dimen> | |||||
| <dimen name="dp_158">158dp</dimen> | |||||
| <dimen name="dp_172">172dp</dimen> | |||||
| <dimen name="dp_175">175dp</dimen> | |||||
| <dimen name="dp_176">176dp</dimen> | |||||
| <dimen name="dp_180">180dp</dimen> | |||||
| <dimen name="dp_188">188dp</dimen> | |||||
| <dimen name="dp_200">200dp</dimen> | |||||
| <dimen name="dp_207">207dp</dimen> | |||||
| <dimen name="dp_218">218dp</dimen> | |||||
| <dimen name="dp_220">220dp</dimen> | |||||
| <dimen name="dp_230">230dp</dimen> | |||||
| <dimen name="dp_240">240dp</dimen> | |||||
| <dimen name="dp_250">250dp</dimen> | |||||
| <dimen name="dp_252">252dp</dimen> | |||||
| <dimen name="dp_274">274dp</dimen> | |||||
| <dimen name="dp_275">275dp</dimen> | |||||
| <dimen name="dp_280">280dp</dimen> | |||||
| <dimen name="dp_300">300dp</dimen> | |||||
| <dimen name="dp_350">350dp</dimen> | |||||
| <dimen name="px_1">1px</dimen> | |||||
| <dimen name="px_8">8px</dimen> | |||||
| <dimen name="px_20">20px</dimen> | |||||
| <dimen name="tab_text_size">@dimen/text_size_12</dimen> | |||||
| <dimen name="tab_icon_height">24dp</dimen> | |||||
| <dimen name="tab_icon_width">24dp</dimen> | |||||
| <dimen name="tab_layout_padding">8dp</dimen> | |||||
| <!-- ************************************************************分割线************************************************************--> | |||||
| <!-- 所有页面的titlebar高度 --> | |||||
| <dimen name="titlebar_height">50dp</dimen> | |||||
| <!-- 所有页面的footerbar高度 --> | |||||
| <dimen name="footerbar_height">50dp</dimen> | |||||
| <dimen name="title_size">22dp</dimen> | |||||
| <!-- 页面title栏title字体大小 --> | |||||
| <!-- ListView item高度 --> | |||||
| <dimen name="list_one_item_height">65dp</dimen> | |||||
| <!-- 普通输入区单行高度 --> | |||||
| <dimen name="text_one_item_height">50dp</dimen> | |||||
| <!-- 普通显示区单行高度 --> | |||||
| <dimen name="showtext_one_item_height">38dp</dimen> | |||||
| <!-- 页面主字体大小 --> | |||||
| <dimen name="main_text_size">15dp</dimen> | |||||
| <!-- 页面按钮上的字体大小 --> | |||||
| <dimen name="size_in_button">16dp</dimen> | |||||
| <!-- 带照片的行高 --> | |||||
| <dimen name="line_height_with_photo">60dp</dimen> | |||||
| <!-- 照片高 --> | |||||
| <dimen name="photo_height_in_line">57dp</dimen> | |||||
| <!-- 页面布局 --> | |||||
| <dimen name="page_to_left">15dip</dimen> | |||||
| <dimen name="page_to_top">20dip</dimen> | |||||
| </resources> | |||||
| @ -0,0 +1,3 @@ | |||||
| <resources> | |||||
| <string name="app_name">Ytzh</string> | |||||
| </resources> | |||||
| @ -0,0 +1,54 @@ | |||||
| <resources> | |||||
| <!-- Base application theme. --> | |||||
| <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> | |||||
| <!-- Customize your theme here. --> | |||||
| <item name="colorPrimary">@color/colorPrimary</item> | |||||
| <item name="colorPrimaryDark">@color/colorPrimaryDark</item> | |||||
| <item name="colorAccent">@color/colorAccent</item> | |||||
| <item name="drawerArrowStyle">@style/DrawerArrowStyle</item> | |||||
| <item name="actionMenuTextColor">@color/colorPrimaryDark</item> | |||||
| <!-- 设置activity切换动画 --> | |||||
| <item name="android:windowAnimationStyle">@style/activityAnimation</item> | |||||
| </style> | |||||
| <style name="activityAnimation" parent="@android:style/Animation"> | |||||
| <item name="android:activityOpenEnterAnimation">@anim/anim_in_right</item> | |||||
| <item name="android:activityOpenExitAnimation">@anim/anim_out_left</item> | |||||
| <item name="android:activityCloseEnterAnimation">@anim/anim_in_left</item> | |||||
| <item name="android:activityCloseExitAnimation">@anim/anim_out_right</item> | |||||
| </style> | |||||
| <style name="DrawerArrowStyle" parent="Widget.AppCompat.DrawerArrowToggle"> | |||||
| <item name="spinBars">true</item> | |||||
| <item name="color">@android:color/white</item> | |||||
| </style> | |||||
| <style name="TextView_18_black_282828"> | |||||
| <item name="android:layout_height">wrap_content</item> | |||||
| <item name="android:layout_width">wrap_content</item> | |||||
| <item name="android:textColor">@color/white</item> | |||||
| <item name="android:textSize">@dimen/text_size_18</item> | |||||
| </style> | |||||
| <style name="TabLayout"> | |||||
| <item name="android:padding">@dimen/tab_layout_padding</item> | |||||
| <item name="android:layout_height">match_parent</item> | |||||
| <!--<item name="android:background">@drawable/selector_white2gray</item>--> | |||||
| <item name="android:layout_width">0dp</item> | |||||
| <item name="android:layout_weight">1</item> | |||||
| <item name="android:gravity">center</item> | |||||
| <item name="android:orientation">vertical</item> | |||||
| </style> | |||||
| <style name="TabIcon"> | |||||
| <item name="android:layout_height">@dimen/tab_icon_height</item> | |||||
| <item name="android:layout_width">@dimen/tab_icon_width</item> | |||||
| </style> | |||||
| <style name="TabText"> | |||||
| <item name="android:layout_height">wrap_content</item> | |||||
| <item name="android:layout_width">wrap_content</item> | |||||
| <item name="android:textColor">@color/grey_767c82</item> | |||||
| <item name="android:textSize">@dimen/tab_text_size</item> | |||||
| </style> | |||||
| </resources> | |||||
| @ -0,0 +1,17 @@ | |||||
| package com.qhclh.ytzh; | |||||
| import org.junit.Test; | |||||
| import static org.junit.Assert.*; | |||||
| /** | |||||
| * Example local unit test, which will execute on the development machine (host). | |||||
| * | |||||
| * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> | |||||
| */ | |||||
| public class ExampleUnitTest { | |||||
| @Test | |||||
| public void addition_isCorrect() throws Exception { | |||||
| assertEquals(4, 2 + 2); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,23 @@ | |||||
| // 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:2.3.2' | |||||
| classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' | |||||
| // NOTE: Do not place your application dependencies here; they belong | |||||
| // in the individual module build.gradle files | |||||
| } | |||||
| } | |||||
| allprojects { | |||||
| repositories { | |||||
| jcenter() | |||||
| } | |||||
| } | |||||
| task clean(type: Delete) { | |||||
| delete rootProject.buildDir | |||||
| } | |||||
| @ -0,0 +1,17 @@ | |||||
| # 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. | |||||
| org.gradle.jvmargs=-Xmx1536m | |||||
| # 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 | |||||
| @ -0,0 +1,6 @@ | |||||
| #Thu Nov 23 10:54:26 CST 2017 | |||||
| distributionBase=GRADLE_USER_HOME | |||||
| distributionPath=wrapper/dists | |||||
| zipStoreBase=GRADLE_USER_HOME | |||||
| zipStorePath=wrapper/dists | |||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip | |||||
| @ -0,0 +1,160 @@ | |||||
| #!/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 | |||||
| # 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\"`/" >/dev/null | |||||
| APP_HOME="`pwd -P`" | |||||
| cd "$SAVED" >/dev/null | |||||
| 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"` | |||||
| JAVACMD=`cygpath --unix "$JAVACMD"` | |||||
| # 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 "$@" | |||||
| @ -0,0 +1,90 @@ | |||||
| @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 | |||||
| @ -0,0 +1 @@ | |||||
| /build | |||||
| @ -0,0 +1,27 @@ | |||||
| apply plugin: 'com.android.library' | |||||
| android { | |||||
| compileSdkVersion 23 | |||||
| buildToolsVersion '25.0.0' | |||||
| defaultConfig { | |||||
| minSdkVersion 15 | |||||
| targetSdkVersion 23 | |||||
| versionCode 1 | |||||
| versionName "1.0" | |||||
| } | |||||
| buildTypes { | |||||
| release { | |||||
| minifyEnabled false | |||||
| proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | |||||
| } | |||||
| } | |||||
| productFlavors { | |||||
| } | |||||
| } | |||||
| dependencies { | |||||
| compile fileTree(include: ['*.jar'], dir: 'libs') | |||||
| testCompile 'junit:junit:4.12' | |||||
| compile 'com.android.support:appcompat-v7:23.3.0' | |||||
| compile 'org.jbundle.util.osgi.wrapped:org.jbundle.util.osgi.wrapped.org.apache.http.client:4.1.2' | |||||
| } | |||||
| @ -0,0 +1,17 @@ | |||||
| # Add project specific ProGuard rules here. | |||||
| # By default, the flags in this file are appended to flags specified | |||||
| # in /opt/android-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 *; | |||||
| #} | |||||
| @ -0,0 +1,13 @@ | |||||
| package org.forks.jsonrpc; | |||||
| 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); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,11 @@ | |||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| package="org.forks.jsonrpc"> | |||||
| <application | |||||
| android:allowBackup="true" | |||||
| android:label="@string/app_name" | |||||
| android:supportsRtl="true"> | |||||
| </application> | |||||
| </manifest> | |||||
| @ -0,0 +1,38 @@ | |||||
| package org.forks.jsonrpc; | |||||
| import java.util.ArrayList; | |||||
| import java.util.HashMap; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| public final class DFDataTable { | |||||
| private Map<String, RpcPropertyMeta> mColumns; | |||||
| private List<RpcObject> mRows; | |||||
| private RpcObject mSumRow; | |||||
| DFDataTable() { | |||||
| this(new HashMap<String, RpcPropertyMeta>(), | |||||
| new ArrayList<RpcObject>(), null); | |||||
| } | |||||
| DFDataTable(Map<String, RpcPropertyMeta> columns, List<RpcObject> rows, | |||||
| RpcObject sumRow) { | |||||
| super(); | |||||
| mColumns = columns; | |||||
| mRows = rows; | |||||
| mSumRow = sumRow; | |||||
| } | |||||
| public Map<String, RpcPropertyMeta> getColumns() { | |||||
| return mColumns; | |||||
| } | |||||
| public List<RpcObject> getRows() { | |||||
| return mRows; | |||||
| } | |||||
| public RpcObject getSumRow() { | |||||
| return mSumRow; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,76 @@ | |||||
| package org.forks.jsonrpc; | |||||
| import org.json.JSONArray; | |||||
| import org.json.JSONException; | |||||
| import org.json.JSONObject; | |||||
| import org.json.JSONTokener; | |||||
| final class Helper { | |||||
| static String getString(JSONObject obj, String name) | |||||
| throws JsonRpcException { | |||||
| try { | |||||
| return obj.getString(name); | |||||
| } catch (JSONException e) { | |||||
| throw new JsonRpcException(e); | |||||
| } | |||||
| } | |||||
| static int getInt(JSONObject obj, String name) throws JsonRpcException { | |||||
| try { | |||||
| return obj.getInt(name); | |||||
| } catch (JSONException e) { | |||||
| throw new JsonRpcException(e); | |||||
| } | |||||
| } | |||||
| static JSONObject parseJsonObject(String json) throws JsonRpcException { | |||||
| try { | |||||
| return (JSONObject) new JSONTokener(json).nextValue(); | |||||
| } catch (JSONException e) { | |||||
| throw new JsonRpcException(e); | |||||
| } | |||||
| } | |||||
| static JSONArray getJsonArray(JSONObject obj, String name) | |||||
| throws JsonRpcException { | |||||
| try { | |||||
| return obj.getJSONArray(name); | |||||
| } catch (JSONException e) { | |||||
| throw new JsonRpcException(e); | |||||
| } | |||||
| } | |||||
| public static JSONArray getJsonArray(JSONArray array, int i) | |||||
| throws JsonRpcException { | |||||
| try { | |||||
| return array.getJSONArray(i); | |||||
| } catch (JSONException e) { | |||||
| throw new JsonRpcException(e); | |||||
| } | |||||
| } | |||||
| static JSONObject getJsonObject(JSONArray arr, int idx) | |||||
| throws JsonRpcException { | |||||
| try { | |||||
| return arr.getJSONObject(idx); | |||||
| } catch (JSONException e) { | |||||
| throw new JsonRpcException(e); | |||||
| } | |||||
| } | |||||
| static Object get(JSONObject obj, String name) throws JsonRpcException { | |||||
| try { | |||||
| return obj.get(name); | |||||
| } catch (JSONException e) { | |||||
| throw new JsonRpcException(e); | |||||
| } | |||||
| } | |||||
| public static Object get(JSONArray array, int idx) throws JsonRpcException { | |||||
| try { | |||||
| return array.get(idx); | |||||
| } catch (JSONException e) { | |||||
| throw new JsonRpcException(e); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,84 @@ | |||||
| package org.forks.jsonrpc; | |||||
| import java.io.IOException; | |||||
| import java.io.UnsupportedEncodingException; | |||||
| import org.apache.http.HttpEntity; | |||||
| import org.apache.http.HttpResponse; | |||||
| import org.apache.http.HttpVersion; | |||||
| import org.apache.http.client.ClientProtocolException; | |||||
| import org.apache.http.client.CookieStore; | |||||
| import org.apache.http.client.methods.HttpPost; | |||||
| import org.apache.http.entity.StringEntity; | |||||
| import org.apache.http.impl.client.BasicCookieStore; | |||||
| import org.apache.http.impl.client.DefaultHttpClient; | |||||
| import org.apache.http.params.BasicHttpParams; | |||||
| import org.apache.http.params.HttpConnectionParams; | |||||
| import org.apache.http.params.HttpParams; | |||||
| import org.apache.http.params.HttpProtocolParams; | |||||
| import org.apache.http.util.EntityUtils; | |||||
| public final class HttpJsonRpcClient extends JsonRpcClient { | |||||
| private final String mRequestUri; | |||||
| final static CookieStore sCookieStore = new BasicCookieStore(); | |||||
| private static DefaultHttpClient sHttpClient; | |||||
| private static String sAppName = "[android] "; | |||||
| static { | |||||
| HttpParams params = new BasicHttpParams(); | |||||
| HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); | |||||
| HttpConnectionParams.setSoTimeout(params, 45 * 1000); | |||||
| sHttpClient = new DefaultHttpClient(params); | |||||
| sHttpClient.setCookieStore(sCookieStore); | |||||
| } | |||||
| public HttpJsonRpcClient(String requestUri, String method) { | |||||
| super(method); | |||||
| mRequestUri = requestUri; | |||||
| } | |||||
| @Override | |||||
| protected String doRequest(String json) throws JsonRpcException { | |||||
| HttpPost request = new HttpPost(mRequestUri); | |||||
| request.setEntity(newStringEntity(json)); | |||||
| request.setHeader("User-Agent", sAppName); | |||||
| HttpResponse response = getResponse(request); | |||||
| return getResponseString(response); | |||||
| } | |||||
| static void sSetClientAppName(String appName) { | |||||
| sAppName = "[android] " + appName; | |||||
| } | |||||
| private static String getResponseString(HttpResponse response) | |||||
| throws JsonRpcException { | |||||
| HttpEntity back = response.getEntity(); | |||||
| try { | |||||
| return EntityUtils.toString(back); | |||||
| } catch (IOException e) { | |||||
| throw new JsonRpcException(e); | |||||
| } | |||||
| } | |||||
| private static StringEntity newStringEntity(String json) | |||||
| throws JsonRpcException { | |||||
| try { | |||||
| return new StringEntity(json, "UTF-8"); | |||||
| } catch (UnsupportedEncodingException e) { | |||||
| throw new JsonRpcException(e); | |||||
| } | |||||
| } | |||||
| private static HttpResponse getResponse(HttpPost request) | |||||
| throws JsonRpcException { | |||||
| try { | |||||
| return sHttpClient.execute(request); | |||||
| } catch (ClientProtocolException e) { | |||||
| throw new JsonRpcException(e); | |||||
| } catch (IOException e) { | |||||
| throw new JsonRpcException(e); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,23 @@ | |||||
| package org.forks.jsonrpc; | |||||
| @SuppressWarnings("serial") | |||||
| public final class InvalidPropertyValueException extends | |||||
| JsonRpcRuntimeException { | |||||
| public InvalidPropertyValueException() { | |||||
| } | |||||
| public InvalidPropertyValueException(String detailMessage) { | |||||
| super(detailMessage); | |||||
| } | |||||
| public InvalidPropertyValueException(Throwable throwable) { | |||||
| super(throwable); | |||||
| } | |||||
| public InvalidPropertyValueException(String detailMessage, | |||||
| Throwable throwable) { | |||||
| super(detailMessage, throwable); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,69 @@ | |||||
| package org.forks.jsonrpc; | |||||
| import java.util.ArrayList; | |||||
| import java.util.Iterator; | |||||
| import java.util.NoSuchElementException; | |||||
| final class IterHelper { | |||||
| private IterHelper() { | |||||
| } | |||||
| public static <T> ArrayList<T> toList(Iterable<T> iter) { | |||||
| ArrayList<T> result = new ArrayList<T>(); | |||||
| for (T item : iter) { | |||||
| result.add(item); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| public static <T> ArrayList<T> toList(T... items) { | |||||
| ArrayList<T> result = new ArrayList<T>(items.length); | |||||
| for (T item : items) { | |||||
| result.add(item); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| public static <T> int count(Iterable<T> iter) { | |||||
| int result = 0; | |||||
| for (@SuppressWarnings("unused") | |||||
| T item : iter) { | |||||
| result++; | |||||
| } | |||||
| return result; | |||||
| } | |||||
| public static <T> Iterable<T> skip(final int n, final Iterable<T> iter) { | |||||
| return new Iterable<T>() { | |||||
| public Iterator<T> iterator() { | |||||
| return new Iterator<T>() { | |||||
| Iterator<T> mIter = iter.iterator(); | |||||
| { | |||||
| try { | |||||
| for (int i = 0; i < n; i++) { | |||||
| mIter.next(); | |||||
| } | |||||
| } catch (NoSuchElementException e) { | |||||
| } | |||||
| } | |||||
| public boolean hasNext() { | |||||
| return mIter.hasNext(); | |||||
| } | |||||
| public T next() { | |||||
| return mIter.next(); | |||||
| } | |||||
| public void remove() { | |||||
| mIter.remove(); | |||||
| } | |||||
| }; | |||||
| } | |||||
| }; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,93 @@ | |||||
| package org.forks.jsonrpc; | |||||
| import org.json.JSONException; | |||||
| import org.json.JSONObject; | |||||
| import android.util.Log; | |||||
| public abstract class JsonRpcClient { | |||||
| private final String mMethod; | |||||
| private final static String LOG_TAG = "JsonRpcClient"; | |||||
| private long requestTime; | |||||
| protected JsonRpcClient(String method) { | |||||
| mMethod = method; | |||||
| } | |||||
| public final JsonRpcResult call(Object... args) throws JsonRpcException { | |||||
| try { | |||||
| JSONObject req = newRequest(args); | |||||
| JSONObject res = request(req); | |||||
| checkResponseId(req, res); | |||||
| checkError(res); | |||||
| return new JsonRpcResult(res.get("result")); | |||||
| } catch (JSONException e) { | |||||
| Log.w(LOG_TAG, "", e); | |||||
| throw new JsonRpcException(e); | |||||
| } | |||||
| catch (JsonRpcException e){ | |||||
| throw e; | |||||
| } | |||||
| catch (Exception e){ | |||||
| throw new JsonRpcException(JsonRpcException.NETWORK_ERROR_CODE,e.getMessage()); | |||||
| } | |||||
| } | |||||
| private static void checkResponseId(JSONObject req, JSONObject res) | |||||
| throws JSONException, JsonRpcException { | |||||
| int reqId = req.getInt("id"); | |||||
| int resId = res.getInt("id"); | |||||
| if (reqId != resId) { | |||||
| throw new JsonRpcException(String.format( | |||||
| "response id (%d) does not match to request id (%d)", | |||||
| reqId, resId)); | |||||
| } | |||||
| } | |||||
| private static void checkError(JSONObject res) throws JSONException, | |||||
| JsonRpcException { | |||||
| JSONObject err = res.optJSONObject("error"); | |||||
| if (err != null) { | |||||
| int code = err.getInt("code"); | |||||
| String msg = err.getString("message"); | |||||
| String trace = err.optString("detail"); | |||||
| throw new JsonRpcException(code, msg, trace); | |||||
| } | |||||
| } | |||||
| private JSONObject request(JSONObject req) throws JsonRpcException { | |||||
| String json = req.toString(); | |||||
| log_request(json); | |||||
| String jsonBack = doRequest(json); | |||||
| log_response(jsonBack); | |||||
| return Helper.parseJsonObject(jsonBack); | |||||
| } | |||||
| private void log_response(String jsonBack) { | |||||
| long duration = System.currentTimeMillis() - requestTime; | |||||
| Log.i(LOG_TAG, String.format("get response using %d ms, response: %s", | |||||
| duration, jsonBack)); | |||||
| } | |||||
| private void log_request(String reqJson) { | |||||
| requestTime = System.currentTimeMillis(); | |||||
| Log.v(LOG_TAG, "start request: " + reqJson); | |||||
| } | |||||
| protected abstract String doRequest(String json) throws JsonRpcException; | |||||
| private JSONObject newRequest(Object[] args) throws JSONException { | |||||
| Object params = RpcTypeParser.toJson(args); | |||||
| return newRequest(params); | |||||
| } | |||||
| private static int mIdSeed; | |||||
| private JSONObject newRequest(Object params) throws JSONException { | |||||
| JSONObject result = new JSONObject(); | |||||
| result.put("id", mIdSeed++); | |||||
| result.put("method", mMethod); | |||||
| result.put("params", params); | |||||
| return result; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,51 @@ | |||||
| package org.forks.jsonrpc; | |||||
| public final class JsonRpcException extends Exception { | |||||
| public boolean isNetworkError(){ | |||||
| return mCode == NETWORK_ERROR_CODE; | |||||
| } | |||||
| static int NETWORK_ERROR_CODE = -1; | |||||
| private static final long serialVersionUID = -3409287525569584913L; | |||||
| private int mCode = 0; | |||||
| private String mTrace; | |||||
| public JsonRpcException() { | |||||
| super(); | |||||
| } | |||||
| public JsonRpcException(String message, Throwable throwable) { | |||||
| super(message, throwable); | |||||
| } | |||||
| public JsonRpcException(String message) { | |||||
| super(message); | |||||
| } | |||||
| public JsonRpcException(Throwable throwable) { | |||||
| super(throwable); | |||||
| } | |||||
| public JsonRpcException(int code, String message) { | |||||
| this(code, message, null); | |||||
| } | |||||
| public JsonRpcException(int code, String message, String trace) { | |||||
| super(message); | |||||
| mCode = code; | |||||
| mTrace = trace; | |||||
| } | |||||
| public int getCode() { | |||||
| return mCode; | |||||
| } | |||||
| public boolean is5xxError() { | |||||
| return mCode >= 500 && mCode < 600; | |||||
| } | |||||
| public String getDetailMessage() { | |||||
| return mTrace; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,179 @@ | |||||
| package org.forks.jsonrpc; | |||||
| import java.math.BigDecimal; | |||||
| import java.util.Date; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| import org.json.JSONObject; | |||||
| public final class JsonRpcResult { | |||||
| private final Object mResult; | |||||
| JsonRpcResult(Object result) { | |||||
| super(); | |||||
| mResult = result; | |||||
| } | |||||
| public Object getRaw() { | |||||
| return mResult; | |||||
| } | |||||
| public Integer getInt() { | |||||
| return (Integer) RpcTypeParser.sInt.fromJson(mResult); | |||||
| } | |||||
| public String getString() { | |||||
| return (String) RpcTypeParser.sString.fromJson(mResult); | |||||
| } | |||||
| public Short getShort() { | |||||
| return (Short) RpcTypeParser.sShort.fromJson(mResult); | |||||
| } | |||||
| public Long getLong() { | |||||
| return (Long) RpcTypeParser.sLong.fromJson(mResult); | |||||
| } | |||||
| public Short getByte() { | |||||
| return (Short) RpcTypeParser.sByte.fromJson(mResult); | |||||
| } | |||||
| public Double getDouble() { | |||||
| return (Double) RpcTypeParser.sDouble.fromJson(mResult); | |||||
| } | |||||
| public Boolean getBoolean() { | |||||
| return (Boolean) RpcTypeParser.sBoolean.fromJson(mResult); | |||||
| } | |||||
| public BigDecimal getDecimal() { | |||||
| return (BigDecimal) RpcTypeParser.sDecimal.fromJson(mResult); | |||||
| } | |||||
| public BigDecimal getMoney() { | |||||
| return getDecimal(); | |||||
| } | |||||
| public boolean isNull() { | |||||
| return mResult == JSONObject.NULL; | |||||
| } | |||||
| public Date getDate() { | |||||
| return (Date) RpcTypeParser.sDate.fromJson(mResult); | |||||
| } | |||||
| public RpcObject getRpcObject(String objPath) throws JsonRpcException { | |||||
| return (RpcObject) RpcTypeParser.fromJson(mResult, objPath); | |||||
| } | |||||
| public DFDataTable getTable() { | |||||
| return (DFDataTable) RpcTypeParser.sTable.fromJson(mResult); | |||||
| } | |||||
| public ManyList getManyList(String objPath) throws JsonRpcException { | |||||
| return getByType("onetomany(" + objPath + ")"); | |||||
| } | |||||
| public List<Integer> getIntList() throws JsonRpcException { | |||||
| return getByType("[int]"); | |||||
| } | |||||
| public List<Short> getShortList() throws JsonRpcException { | |||||
| return getByType("[short]"); | |||||
| } | |||||
| public List<Short> getByteList() throws JsonRpcException { | |||||
| return getByType("[byte]"); | |||||
| } | |||||
| public List<Long> getLongList() throws JsonRpcException { | |||||
| return getByType("[long]"); | |||||
| } | |||||
| public List<Boolean> getBooleanList() throws JsonRpcException { | |||||
| return getByType("[bool]"); | |||||
| } | |||||
| public List<Double> getDoubleList() throws JsonRpcException { | |||||
| return getByType("[double]"); | |||||
| } | |||||
| public List<String> getStringList() throws JsonRpcException { | |||||
| return getByType("[str]"); | |||||
| } | |||||
| public List<Date> getDateList() throws JsonRpcException { | |||||
| return getByType("[datetime]"); | |||||
| } | |||||
| public List<BigDecimal> getDecimalList() throws JsonRpcException { | |||||
| return getByType("[decimal]"); | |||||
| } | |||||
| public List<BigDecimal> getMoneyList() throws JsonRpcException { | |||||
| return getByType("[money]"); | |||||
| } | |||||
| public List<RpcObject> getRpcObjectList(String objPath) | |||||
| throws JsonRpcException { | |||||
| return getByType("[" + objPath + "]"); | |||||
| } | |||||
| public Map<String, Integer> getIntMap() throws JsonRpcException { | |||||
| return getByType("{str:int}"); | |||||
| } | |||||
| public Map<String, Short> getByteMap() throws JsonRpcException { | |||||
| return getByType("{str:byte}"); | |||||
| } | |||||
| public Map<String, Short> getShortMap() throws JsonRpcException { | |||||
| return getByType("{str:short}"); | |||||
| } | |||||
| public Map<String, Long> getLongMap() throws JsonRpcException { | |||||
| return getByType("{str:long}"); | |||||
| } | |||||
| public Map<String, Boolean> getBooleanMap() throws JsonRpcException { | |||||
| return getByType("{str:bool}"); | |||||
| } | |||||
| public Map<String, Double> getDoubleMap() throws JsonRpcException { | |||||
| return getByType("{str:double}"); | |||||
| } | |||||
| public Map<String, String> getStringMap() throws JsonRpcException { | |||||
| return getByType("{str:str}"); | |||||
| } | |||||
| public Map<String, Date> getDateMap() throws JsonRpcException { | |||||
| return getByType("{str:datetime}"); | |||||
| } | |||||
| public Map<String, BigDecimal> getDecimalMap() throws JsonRpcException { | |||||
| return getByType("{str:decimal}"); | |||||
| } | |||||
| public Map<String, BigDecimal> getMoneyMap() throws JsonRpcException { | |||||
| return getByType("{str:money}"); | |||||
| } | |||||
| public Map<String, RpcObject> getRpcObjectMap(String objPath) | |||||
| throws JsonRpcException { | |||||
| return getByType("{str:" + objPath + "}"); | |||||
| } | |||||
| private <T> T getByType(String rpcType) throws JsonRpcException { | |||||
| RpcType type = RpcTypeParser.parse(rpcType); | |||||
| return (T) type.fromJson(mResult); | |||||
| } | |||||
| JSONObject getJsonObject() { | |||||
| if (isNull()) { | |||||
| return null; | |||||
| } | |||||
| return (JSONObject) mResult; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,22 @@ | |||||
| package org.forks.jsonrpc; | |||||
| public class JsonRpcRuntimeException extends RuntimeException { | |||||
| private static final long serialVersionUID = 4017156582402768366L; | |||||
| public JsonRpcRuntimeException() { | |||||
| } | |||||
| public JsonRpcRuntimeException(String detailMessage) { | |||||
| super(detailMessage); | |||||
| } | |||||
| public JsonRpcRuntimeException(Throwable throwable) { | |||||
| super(throwable); | |||||
| } | |||||
| public JsonRpcRuntimeException(String detailMessage, Throwable throwable) { | |||||
| super(detailMessage, throwable); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,199 @@ | |||||
| package org.forks.jsonrpc; | |||||
| import java.util.ArrayList; | |||||
| import java.util.Collection; | |||||
| import android.util.Pair; | |||||
| import org.json.JSONException; | |||||
| import org.json.JSONObject; | |||||
| @SuppressWarnings("serial") | |||||
| public final class ManyList extends ArrayList<RpcObject> { | |||||
| private final RpcObjectMeta mItemMeta; | |||||
| private final ArrayList<RpcObject> mNewItems = new ArrayList<RpcObject>(); | |||||
| private final ArrayList<RpcObject> mDeletedItems = new ArrayList<RpcObject>(); | |||||
| public ManyList(RpcObjectMeta itemMeta) { | |||||
| mItemMeta = itemMeta; | |||||
| } | |||||
| public static ManyList create(String itemObjectPath) | |||||
| throws JsonRpcException { | |||||
| return new ManyList(RpcObjectMeta.get(itemObjectPath)); | |||||
| } | |||||
| @Override | |||||
| public void add(int index, RpcObject object) { | |||||
| checkMetaType(object); | |||||
| addNew(object); | |||||
| super.add(index, object); | |||||
| } | |||||
| @Override | |||||
| public boolean add(RpcObject object) { | |||||
| checkMetaType(object); | |||||
| addNew(object); | |||||
| return super.add(object); | |||||
| } | |||||
| @Override | |||||
| public boolean addAll(Collection<? extends RpcObject> collection) { | |||||
| checkMetaTypes(collection); | |||||
| addNewObjects(collection); | |||||
| return super.addAll(collection); | |||||
| } | |||||
| @Override | |||||
| public boolean addAll(int location, | |||||
| Collection<? extends RpcObject> collection) { | |||||
| checkMetaTypes(collection); | |||||
| addNewObjects(collection); | |||||
| return super.addAll(location, collection); | |||||
| } | |||||
| @Override | |||||
| public void clear() { | |||||
| for (RpcObject item : this) { | |||||
| addDeleted(item); | |||||
| } | |||||
| super.clear(); | |||||
| } | |||||
| @Override | |||||
| public RpcObject remove(int index) { | |||||
| RpcObject result = super.remove(index); | |||||
| addDeleted(result); | |||||
| return result; | |||||
| } | |||||
| @Override | |||||
| protected void removeRange(int fromIndex, int toIndex) { | |||||
| for (int i = fromIndex; i < toIndex; i++) { | |||||
| addDeleted(get(i)); | |||||
| } | |||||
| super.removeRange(fromIndex, toIndex); | |||||
| } | |||||
| @Override | |||||
| public boolean removeAll(Collection<?> collection) { | |||||
| // it is too complex to test modify track, and rarely used | |||||
| throw new UnsupportedOperationException(); | |||||
| } | |||||
| @Override | |||||
| public boolean retainAll(Collection<?> collection) { | |||||
| // it is too complex to test modify track, and rarely used | |||||
| throw new UnsupportedOperationException(); | |||||
| } | |||||
| @Override | |||||
| public RpcObject set(int index, RpcObject object) { | |||||
| RpcObject result = super.set(index, object); | |||||
| addDeleted(result); | |||||
| addNew(object); | |||||
| return result; | |||||
| } | |||||
| private void addNewObjects(Collection<? extends RpcObject> collection) { | |||||
| for (RpcObject item : collection) { | |||||
| addNew(item); | |||||
| } | |||||
| } | |||||
| private void addNew(RpcObject object) { | |||||
| if (!mDeletedItems.remove(object)) { | |||||
| mNewItems.add(object); | |||||
| } | |||||
| } | |||||
| private void addDeleted(RpcObject object) { | |||||
| if (!mNewItems.remove(object)) { | |||||
| mDeletedItems.add(object); | |||||
| } | |||||
| } | |||||
| JSONObject toJson() throws JSONException { | |||||
| JSONObject result = new JSONObject(); | |||||
| Pair<Collection<RpcObject>, Collection<RpcObject>> changedUnChanged = getChangedUnChangedItems(); | |||||
| putArrayField(result, "_data", changedUnChanged.second); | |||||
| putArrayField(result, "_new", getNewItems()); | |||||
| putArrayField(result, "_deleted", getDeletedItems()); | |||||
| putArrayField(result, "_modified", changedUnChanged.first); | |||||
| return result; | |||||
| } | |||||
| private static void putArrayField(JSONObject result, String fieldName, | |||||
| Collection<RpcObject> list) throws JSONException { | |||||
| if (!list.isEmpty()) { | |||||
| result.put(fieldName, RpcTypeParser.toJson(list)); | |||||
| } | |||||
| } | |||||
| private void checkMetaTypes(Collection<? extends RpcObject> collection) { | |||||
| if (!RpcFacade.sDebug) { | |||||
| return; | |||||
| } | |||||
| for (RpcObject rpcObject : collection) { | |||||
| checkMetaType(rpcObject); | |||||
| } | |||||
| } | |||||
| private void checkMetaType(RpcObject object) { | |||||
| if (!RpcFacade.sDebug) { | |||||
| return; | |||||
| } | |||||
| RpcObjectMeta objMeta = object.getMeta(); | |||||
| if (objMeta != mItemMeta) { | |||||
| throwMetaMisMatchError(objMeta); | |||||
| } | |||||
| } | |||||
| private void throwMetaMisMatchError(RpcObjectMeta objMeta) { | |||||
| String msg = "RpcObject %s can not add to %s ManyList"; | |||||
| msg = String.format(msg, objMeta.getName(), mItemMeta.getName()); | |||||
| throw new IllegalArgumentException(msg); | |||||
| } | |||||
| Collection<RpcObject> getNewItems() { | |||||
| return mNewItems; | |||||
| } | |||||
| Collection<RpcObject> getDeletedItems() { | |||||
| return mDeletedItems; | |||||
| } | |||||
| Pair<Collection<RpcObject>, Collection<RpcObject>> | |||||
| getChangedUnChangedItems() { | |||||
| ArrayList<RpcObject> changed = new ArrayList<RpcObject>(); | |||||
| ArrayList<RpcObject> unchanged = new ArrayList<RpcObject>(); | |||||
| for (RpcObject item : this) { | |||||
| if (!isNewObject(item)) { | |||||
| if (!item.isModified()) { | |||||
| unchanged.add(item); | |||||
| } else { | |||||
| changed.add(item); | |||||
| } | |||||
| } | |||||
| } | |||||
| return new Pair<Collection<RpcObject>, Collection<RpcObject>>(changed, | |||||
| unchanged); | |||||
| } | |||||
| void acceptChanges() { | |||||
| mNewItems.clear(); | |||||
| mDeletedItems.clear(); | |||||
| for (RpcObject obj : this) { | |||||
| obj.acceptChanges(); | |||||
| } | |||||
| } | |||||
| private boolean isNewObject(RpcObject item) { | |||||
| return mNewItems.contains(item); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,66 @@ | |||||
| package org.forks.jsonrpc; | |||||
| import java.util.HashMap; | |||||
| import android.util.SparseArray; | |||||
| public final class NamedValue { | |||||
| private final short mValue; | |||||
| private final String mName; | |||||
| private NamedValue(short value, String name) { | |||||
| super(); | |||||
| mValue = value; | |||||
| mName = name; | |||||
| } | |||||
| public short getValue() { | |||||
| return mValue; | |||||
| } | |||||
| public String getName() { | |||||
| return mName; | |||||
| } | |||||
| @Override | |||||
| public String toString() { | |||||
| return getName(); | |||||
| } | |||||
| private static final HashMap<String, SparseArray<NamedValue>> sInstancePool = new HashMap<String, SparseArray<NamedValue>>(); | |||||
| public static synchronized NamedValue create(String idtype, short val) { | |||||
| SparseArray<NamedValue> array = sInstancePool.get(idtype); | |||||
| if (array == null) { | |||||
| array = createArray(idtype); | |||||
| sInstancePool.put(idtype, array); | |||||
| } | |||||
| NamedValue result = array.get(val); | |||||
| if (result == null) { | |||||
| result = new NamedValue(val, Short.toString(val)); | |||||
| array.put(val, result); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| private static SparseArray<NamedValue> createArray(String idtype) { | |||||
| try { | |||||
| SparseArray<String> pairs = NamedValueMeta.get(idtype); | |||||
| SparseArray<NamedValue> result = new SparseArray<NamedValue>( | |||||
| pairs.size()); | |||||
| for (int i = 0; i < pairs.size(); i++) { | |||||
| int key = pairs.keyAt(i); | |||||
| NamedValue v = new NamedValue((short) key, pairs.valueAt(i)); | |||||
| result.append(key, v); | |||||
| } | |||||
| return result; | |||||
| } catch (JsonRpcException e) { | |||||
| throw new JsonRpcRuntimeException(e); | |||||
| } | |||||
| } | |||||
| static void reset() { | |||||
| sInstancePool.clear(); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,84 @@ | |||||
| package org.forks.jsonrpc; | |||||
| import java.util.HashMap; | |||||
| import java.util.List; | |||||
| import android.util.SparseArray; | |||||
| import static org.forks.jsonrpc.RpcFacade.toFullErtPathByPrefixedErt; | |||||
| public final class NamedValueMeta { | |||||
| private static final String NAMED_VALUE_PAIR = "/sbin/NamedValuePair"; | |||||
| private static final String METHOD = "/sbin/NamedValue/GetDefinition"; | |||||
| private static final HashMap<String, SparseArray<String>> sCache = new HashMap<String, SparseArray<String>>(); | |||||
| public static final class Pair { | |||||
| public final int Value; | |||||
| public final String Name; | |||||
| Pair(int value, String name) { | |||||
| super(); | |||||
| Value = value; | |||||
| Name = name; | |||||
| } | |||||
| } | |||||
| public static final SparseArray<String> get(String idType) | |||||
| throws JsonRpcException { | |||||
| synchronized (sCache) { | |||||
| SparseArray<String> result = sCache.get(idType); | |||||
| if (result != null) { | |||||
| return result; | |||||
| } | |||||
| } | |||||
| SparseArray<String> result = doGet(idType); | |||||
| synchronized (sCache) { | |||||
| sCache.put(idType, result); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| private static SparseArray<String> doGet(String idType) | |||||
| throws JsonRpcException { | |||||
| String methodPath = toFullErtPathByPrefixedErt(idType, METHOD); | |||||
| String rpcPath = toFullErtPathByPrefixedErt(idType, NAMED_VALUE_PAIR); | |||||
| JsonRpcResult res = RpcFacade.rpcCall(methodPath, idType); | |||||
| List<RpcObject> pairs = res.getRpcObjectList(rpcPath); | |||||
| return toNamedValueMap(pairs); | |||||
| } | |||||
| private static SparseArray<String> toNamedValueMap(List<RpcObject> pairs) { | |||||
| SparseArray<String> result = new SparseArray<String>(pairs.size()); | |||||
| for (RpcObject pair : pairs) { | |||||
| Integer key = pair.getInt("Value"); | |||||
| String value = pair.getString("Name"); | |||||
| result.put(key, value); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| static void reset() { | |||||
| sCache.clear(); | |||||
| } | |||||
| static void register(String idType, SparseArray<String> names) { | |||||
| sCache.put(idType, names); | |||||
| } | |||||
| public static String toName(Short val, String namedValueType) | |||||
| throws JsonRpcException { | |||||
| if (val == null) { | |||||
| return ""; | |||||
| } | |||||
| String idType = RpcTypeParser.getNamedValueTidType(namedValueType); | |||||
| SparseArray<String> names = get(idType); | |||||
| String result = names.get(val); | |||||
| if (result == null) { | |||||
| result = val.toString(); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,22 @@ | |||||
| package org.forks.jsonrpc; | |||||
| @SuppressWarnings("serial") | |||||
| public final class NoSuchRpcPropertyException extends JsonRpcRuntimeException { | |||||
| NoSuchRpcPropertyException() { | |||||
| super(); | |||||
| } | |||||
| NoSuchRpcPropertyException(String detailMessage, Throwable throwable) { | |||||
| super(detailMessage, throwable); | |||||
| } | |||||
| NoSuchRpcPropertyException(String detailMessage) { | |||||
| super(detailMessage); | |||||
| } | |||||
| NoSuchRpcPropertyException(Throwable throwable) { | |||||
| super(throwable); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,269 @@ | |||||
| package org.forks.jsonrpc; | |||||
| import android.content.Context; | |||||
| import android.content.SharedPreferences; | |||||
| import android.content.SharedPreferences.OnSharedPreferenceChangeListener; | |||||
| import android.os.Build; | |||||
| import android.os.Handler; | |||||
| import android.preference.PreferenceManager; | |||||
| import android.util.Log; | |||||
| import android.webkit.CookieManager; | |||||
| import android.webkit.CookieSyncManager; | |||||
| import android.webkit.WebView; | |||||
| import org.apache.http.cookie.Cookie; | |||||
| public final class RpcFacade { | |||||
| private static final PreferenceMonitor PREFERENCE_MONITOR = new PreferenceMonitor(); | |||||
| public static final String SERVER_URI_PREFKEY = "ServerUri"; | |||||
| private static final String DEBUG_PREFKEY = "Debug"; | |||||
| private static final String TAG = "RpcFacade"; | |||||
| private static final String REST_URI = ""; | |||||
| private static String sRestUri = REST_URI; | |||||
| private static Context sContext; | |||||
| private static String sUsername = ""; | |||||
| private static String sPasswd; | |||||
| private static final String LOGOUT_METHOD = "/MainSystem/MainSystem/Auth/Logout"; | |||||
| private static final String LOGIN_METHOD = "/MainSystem/MainSystem/Auth/Login"; | |||||
| static boolean sDebug = true; | |||||
| // keep reference | |||||
| @SuppressWarnings("unused") | |||||
| private static KeepSession sKeepSession; | |||||
| private RpcFacade() { | |||||
| } | |||||
| public static void syncCookie(WebView webView){ | |||||
| CookieSyncManager.createInstance(webView.getContext()); | |||||
| CookieManager cookieManager = CookieManager.getInstance(); | |||||
| cookieManager.setAcceptCookie(true); | |||||
| cookieManager.removeAllCookie(); | |||||
| for(Cookie cookie:HttpJsonRpcClient.sCookieStore.getCookies()){ | |||||
| if(cookie.getName().equals("EasyAuth")) { | |||||
| String cookieString = cookie.getName() + "=" + cookie.getValue() + "; path=" + cookie.getPath(); | |||||
| cookieManager.setCookie(RpcFacade.getServerUri(), cookieString); | |||||
| } | |||||
| } | |||||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | |||||
| cookieManager.flush(); | |||||
| } | |||||
| CookieSyncManager.getInstance().sync(); | |||||
| } | |||||
| public static void init(Context context, String appName) { | |||||
| Context appContext = context.getApplicationContext(); | |||||
| if (sContext == null) { | |||||
| Log.v(TAG, "init RpcFacade, appname: " + appName); | |||||
| sContext = appContext; | |||||
| sKeepSession = new KeepSession(); | |||||
| HttpJsonRpcClient.sSetClientAppName(appName); | |||||
| initPreference(); | |||||
| } else if (sContext != appContext) { | |||||
| Log.v(TAG, "invalid reinit RpcFacade"); | |||||
| String message = "RpcFacade is inited, can not init again"; | |||||
| throw new JsonRpcRuntimeException(message); | |||||
| } | |||||
| } | |||||
| public static void setServerUri(String uri){ | |||||
| SharedPreferences prefs = PreferenceManager | |||||
| .getDefaultSharedPreferences(sContext); | |||||
| SharedPreferences.Editor editor = prefs.edit(); | |||||
| editor.putString(SERVER_URI_PREFKEY,uri); | |||||
| editor.commit(); | |||||
| } | |||||
| public static boolean isInited() { | |||||
| return sContext != null; | |||||
| } | |||||
| private static void initPreference() { | |||||
| Log.d(TAG, | |||||
| "restore settings from preference, monitor preference change"); | |||||
| SharedPreferences prefs = PreferenceManager | |||||
| .getDefaultSharedPreferences(sContext); | |||||
| restoreDebugFromPref(prefs); | |||||
| restoreServerUriFromPref(prefs); | |||||
| // OnSharedPreferenceChangeListener is a weeked event, | |||||
| // we must keep the event listener instance ourself | |||||
| prefs.registerOnSharedPreferenceChangeListener(PREFERENCE_MONITOR); | |||||
| } | |||||
| public static JsonRpcResult rpcCall(String method, Object... args) | |||||
| throws JsonRpcException { | |||||
| try { | |||||
| return doRpcCall(method, args); | |||||
| } catch (JsonRpcException e) { | |||||
| if (e.getCode() == 401 && isLogedIn()) { | |||||
| autoLogin(); | |||||
| return doRpcCall(method, args); | |||||
| } | |||||
| throw e; | |||||
| } | |||||
| } | |||||
| public static String getUsername() { | |||||
| return sUsername; | |||||
| } | |||||
| public static void login(String user, String passwd) | |||||
| throws JsonRpcException { | |||||
| Log.i(TAG, "Start login: " + user); | |||||
| callLogin(user, passwd); | |||||
| sUsername = user; | |||||
| sPasswd = passwd; | |||||
| } | |||||
| public static void restoreLogined(String username,String password){ | |||||
| sUsername = username; | |||||
| sPasswd = password; | |||||
| } | |||||
| public static void logout() { | |||||
| try { | |||||
| if (isLogedIn()) { | |||||
| Log.i(TAG, "logout user: " + sUsername); | |||||
| sUsername = ""; | |||||
| doRpcCall(LOGOUT_METHOD); | |||||
| } | |||||
| } catch (JsonRpcException e) { | |||||
| } | |||||
| } | |||||
| private static void autoLogin() throws JsonRpcException { | |||||
| String user = sUsername; | |||||
| String pwd = sPasswd; | |||||
| if (user.length() > 0) { | |||||
| callLogin(user, pwd); | |||||
| } | |||||
| } | |||||
| private static JsonRpcResult doRpcCall(String method, Object... args) | |||||
| throws JsonRpcException { | |||||
| JsonRpcClient client = new HttpJsonRpcClient(getRestUri(method), method); | |||||
| return client.call(args); | |||||
| } | |||||
| private static void callLogin(String user, String passwd) | |||||
| throws JsonRpcException { | |||||
| doRpcCall(LOGIN_METHOD, user, passwd); | |||||
| } | |||||
| private static String getRestUri(String method) { | |||||
| StringBuilder sb = new StringBuilder(); | |||||
| appendServerUri(sb); | |||||
| String subSystem = getSystem(method); | |||||
| if (!IsMainSystem(subSystem)) { | |||||
| appendSubSystem(sb, subSystem); | |||||
| } | |||||
| sb.append("Rest.aspx"); | |||||
| return sb.toString(); | |||||
| } | |||||
| private static boolean IsMainSystem(String subSystem) { | |||||
| return subSystem.equals("MainSystem"); | |||||
| } | |||||
| private static void appendSubSystem(StringBuilder sb, String subSystem) { | |||||
| sb.append("O/"); | |||||
| sb.append(subSystem); | |||||
| sb.append('/'); | |||||
| } | |||||
| private static void appendServerUri(StringBuilder sb) { | |||||
| String serverUri = getServerUri(); | |||||
| if (serverUri.charAt(serverUri.length() - 1) != '/') { | |||||
| sb.append('/'); | |||||
| } | |||||
| sb.append(serverUri); | |||||
| } | |||||
| public static String getServerUri() { | |||||
| return sRestUri; | |||||
| } | |||||
| static String getSystem(String method) { | |||||
| int endIdx = method.indexOf('/', 1); | |||||
| return method.substring(1, endIdx); | |||||
| } | |||||
| public static String toFullErtPathByPrefixedErt(String objPath, | |||||
| String subMethodPath) { | |||||
| String subSystem = getSystem(objPath); | |||||
| return "/" + subSystem + subMethodPath; | |||||
| } | |||||
| public static boolean isLogedIn() { | |||||
| return sUsername.length() > 0; | |||||
| } | |||||
| private static void restoreServerUriFromPref(SharedPreferences pref) { | |||||
| sRestUri = pref.getString(SERVER_URI_PREFKEY, REST_URI); | |||||
| Log.d(TAG, "get serverUri from preference: " + sRestUri); | |||||
| } | |||||
| private static void restoreDebugFromPref(SharedPreferences pref) { | |||||
| sDebug = pref.getBoolean(DEBUG_PREFKEY, true); | |||||
| Log.d(TAG, "get debug mode from preference: " + sDebug); | |||||
| } | |||||
| private static class KeepSession implements Runnable { | |||||
| private static Handler handler; | |||||
| public KeepSession() { | |||||
| if (handler != null) { | |||||
| throw new RuntimeException("More than one instance?"); | |||||
| } | |||||
| handler = new Handler(); | |||||
| startAgain(); | |||||
| } | |||||
| public void run() { | |||||
| if (isLogedIn()) { | |||||
| Log.i(TAG, "connecting to server to maintain session"); | |||||
| try { | |||||
| heartBeat(); | |||||
| } catch (Exception e) { | |||||
| Log.w(TAG, e.toString()); | |||||
| } | |||||
| } else { | |||||
| Log.i(TAG, "no need to connect server, not logged in"); | |||||
| } | |||||
| startAgain(); | |||||
| } | |||||
| private static void heartBeat() throws JsonRpcException { | |||||
| // use auto login, because the cell phone maybe just awake after | |||||
| // sleep, and server is kick the user out because of not activity | |||||
| // in 2 minutes. | |||||
| rpcCall("/MainSystem/MainSystem/Auth/Ping"); | |||||
| } | |||||
| private void startAgain() { | |||||
| handler.postDelayed(this, 60 * 1000); | |||||
| } | |||||
| } | |||||
| private static final class PreferenceMonitor implements | |||||
| OnSharedPreferenceChangeListener { | |||||
| public void onSharedPreferenceChanged( | |||||
| SharedPreferences sharedPreferences, String key) { | |||||
| Log.d(TAG, "preference changed, key: " + key); | |||||
| if (key.equals(DEBUG_PREFKEY)) { | |||||
| restoreDebugFromPref(sharedPreferences); | |||||
| } else if (key.equals(SERVER_URI_PREFKEY)) { | |||||
| restoreServerUriFromPref(sharedPreferences); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,386 @@ | |||||
| package org.forks.jsonrpc; | |||||
| import java.math.BigDecimal; | |||||
| import java.util.ArrayList; | |||||
| import java.util.Date; | |||||
| import java.util.HashMap; | |||||
| import java.util.LinkedHashMap; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| import org.json.JSONArray; | |||||
| import org.json.JSONException; | |||||
| import org.json.JSONObject; | |||||
| @SuppressWarnings("serial") | |||||
| public final class RpcObject extends HashMap<String, Object> { | |||||
| private final RpcObjectMeta mMeta; | |||||
| private HashMap<String, Object> mBackup; | |||||
| RpcObject(RpcObjectMeta meta) throws JsonRpcException { | |||||
| super(); | |||||
| mMeta = meta; | |||||
| initValueStore(mMeta.getProps()); | |||||
| acceptChanges(); | |||||
| } | |||||
| public final static RpcObject create(String objPath) | |||||
| throws JsonRpcException { | |||||
| return new RpcObject(RpcObjectMeta.get(objPath)); | |||||
| } | |||||
| public RpcObjectMeta getMeta() { | |||||
| return mMeta; | |||||
| } | |||||
| public Short getByte(String propName) { | |||||
| return (Short) get(propName, RpcTypeParser.BYTE); | |||||
| } | |||||
| public void setByte(String propName, Short value) { | |||||
| set(propName, value, RpcTypeParser.BYTE); | |||||
| } | |||||
| public Integer getInt(String propName) { | |||||
| return (Integer) get(propName, RpcTypeParser.INT); | |||||
| } | |||||
| public void setInt(String propName, Integer value) { | |||||
| set(propName, value, RpcTypeParser.INT); | |||||
| } | |||||
| public Short getShort(String propName) { | |||||
| return (Short) get(propName, RpcTypeParser.SHORT); | |||||
| } | |||||
| public void setShort(String propName, Short value) { | |||||
| set(propName, value, RpcTypeParser.SHORT); | |||||
| } | |||||
| public Long getLong(String propName) { | |||||
| return (Long) get(propName, RpcTypeParser.LONG); | |||||
| } | |||||
| public void setLong(String propName, Long value) { | |||||
| set(propName, value, RpcTypeParser.LONG); | |||||
| } | |||||
| public Double getDouble(String propName) { | |||||
| return (Double) get(propName, RpcTypeParser.DOUBLE); | |||||
| } | |||||
| public void setDouble(String propName, Double value) { | |||||
| set(propName, value, RpcTypeParser.DOUBLE); | |||||
| } | |||||
| public BigDecimal getDecimal(String propName) { | |||||
| return (BigDecimal) get(propName, RpcTypeParser.DECIMAL); | |||||
| } | |||||
| public void setDecimal(String propName, BigDecimal value) { | |||||
| set(propName, value, RpcTypeParser.DECIMAL); | |||||
| } | |||||
| public BigDecimal getMoney(String propName) { | |||||
| return (BigDecimal) get(propName, RpcTypeParser.MONEY); | |||||
| } | |||||
| public void setMoney(String propName, BigDecimal value) { | |||||
| set(propName, value, RpcTypeParser.MONEY); | |||||
| } | |||||
| public Date getDate(String propName) { | |||||
| return (Date) get(propName, RpcTypeParser.DATE); | |||||
| } | |||||
| public void setDate(String propName, Date value) { | |||||
| set(propName, value, RpcTypeParser.DATE); | |||||
| } | |||||
| public String getString(String propName) { | |||||
| return (String) get(propName, RpcTypeParser.STRING); | |||||
| } | |||||
| public String getGuid(String propName){ | |||||
| return (String)get(propName,RpcTypeParser.GUID); | |||||
| } | |||||
| public void setString(String propName, String value) { | |||||
| set(propName, value, RpcTypeParser.STRING); | |||||
| } | |||||
| public Integer getLookupValue(String propName) { | |||||
| return (Integer) get(propName, RpcTypeParser.LOOKUPVALUE); | |||||
| } | |||||
| public void setLookupValue(String propName, Integer value) { | |||||
| set(propName, value, RpcTypeParser.LOOKUPVALUE); | |||||
| } | |||||
| public NamedValue getNamedValue(String propName) { | |||||
| checkNamedValueProp(propName); | |||||
| return (NamedValue) get(propName); | |||||
| } | |||||
| public void setNamedValue(String propName, Short value) { | |||||
| NamedValue val = toNamedValue(propName, value); | |||||
| setNamedValue(propName, val); | |||||
| } | |||||
| private NamedValue toNamedValue(String propName, Short value) { | |||||
| if (value == null) { | |||||
| return null; | |||||
| } | |||||
| RpcPropertyMeta prop = getPropertyMeta(propName); | |||||
| String idtype = RpcTypeParser.getNamedValueTidType(prop.getType()); | |||||
| return NamedValue.create(idtype, value); | |||||
| } | |||||
| public void setNamedValue(String propName, NamedValue value) { | |||||
| checkNamedValueProp(propName); | |||||
| put(propName, value); | |||||
| } | |||||
| public Long getEntityKey(String propName) { | |||||
| return (Long) get(propName, RpcTypeParser.ENTITYKEY); | |||||
| } | |||||
| public void setEntityKey(String propName, Long value) { | |||||
| set(propName, value, RpcTypeParser.ENTITYKEY); | |||||
| } | |||||
| private void initValueStore(Map<String, RpcPropertyMeta> props) | |||||
| throws JsonRpcException { | |||||
| for (RpcPropertyMeta prop : props.values()) { | |||||
| put(prop.getName(), createDefaultValue(prop)); | |||||
| } | |||||
| } | |||||
| private static Object createDefaultValue(RpcPropertyMeta prop) | |||||
| throws JsonRpcException { | |||||
| if (RpcTypeParser.isManyList(prop.getType())) { | |||||
| return createManyList(prop); | |||||
| } else if (RpcTypeParser.isList(prop.getType())) { | |||||
| return new ArrayList<Object>(); | |||||
| } else if (RpcTypeParser.isMap(prop.getType())) { | |||||
| return new HashMap<Object, Object>(); | |||||
| } | |||||
| return prop.getDefaultValue(); | |||||
| } | |||||
| private static ManyList createManyList(RpcPropertyMeta prop) | |||||
| throws JsonRpcException { | |||||
| return ManyList | |||||
| .create(RpcTypeParser.getManyListItemType(prop.getType())); | |||||
| } | |||||
| private void set(String propName, Object value, String type) { | |||||
| isPropDefined(propName, type); | |||||
| put(propName, value); | |||||
| } | |||||
| private Object get(String propName, String type) { | |||||
| isPropDefined(propName, type); | |||||
| return get(propName); | |||||
| } | |||||
| private void isPropDefined(String propName, String type) { | |||||
| if (RpcFacade.sDebug) { | |||||
| RpcPropertyMeta prop = getPropertyMeta(propName); | |||||
| checkPropertyType(type, prop); | |||||
| } | |||||
| } | |||||
| private RpcPropertyMeta getPropertyMeta(String propName) { | |||||
| RpcPropertyMeta prop = mMeta.getProps().get(propName); | |||||
| if (prop == null) { | |||||
| throw new NoSuchRpcPropertyException(propName); | |||||
| } | |||||
| return prop; | |||||
| } | |||||
| public Boolean getBoolean(String propName) { | |||||
| return (Boolean) get(propName, RpcTypeParser.BOOL); | |||||
| } | |||||
| public void setBoolean(String propName, Boolean value) { | |||||
| set(propName, value, RpcTypeParser.BOOL); | |||||
| } | |||||
| public ManyList getManyList(String propName) { | |||||
| if (RpcFacade.sDebug) { | |||||
| checkManyListProp(propName); | |||||
| } | |||||
| return (ManyList) get(propName); | |||||
| } | |||||
| public <T> List<T> getList(String propName) { | |||||
| if (RpcFacade.sDebug) { | |||||
| checkListProp(propName); | |||||
| } | |||||
| return (List<T>) get(propName); | |||||
| } | |||||
| public <K, V> Map<K, V> getMap(String propName) { | |||||
| if (RpcFacade.sDebug) { | |||||
| checkMapProp(propName); | |||||
| } | |||||
| return (Map<K, V>) get(propName); | |||||
| } | |||||
| public RpcObject getRpcObject(String propName) throws JsonRpcException { | |||||
| checkRpcObjectType(propName, null); | |||||
| return (RpcObject) get(propName); | |||||
| } | |||||
| public void setRpcObject(String propName, RpcObject value) | |||||
| throws JsonRpcException { | |||||
| checkRpcObjectType(propName, value); | |||||
| put(propName, value); | |||||
| } | |||||
| private void checkPropertyType(String type, RpcPropertyMeta prop) { | |||||
| if (!prop.getType().equals(type)) { | |||||
| String msg = String.format( | |||||
| "Type: %s, prop: %s, prop type: %s, access type: %s", | |||||
| mMeta.getName(), prop.getName(), prop.getType(), type); | |||||
| throw new InvalidPropertyValueException(msg); | |||||
| } | |||||
| } | |||||
| private void checkListProp(String propName) { | |||||
| RpcPropertyMeta propMeta = getPropertyMeta(propName); | |||||
| if (!RpcTypeParser.isList(propMeta.getType())) { | |||||
| String pattern = "Property '%s' is '%s', not List"; | |||||
| String message = String.format(pattern, propName, | |||||
| propMeta.getType()); | |||||
| throw new InvalidPropertyValueException(message); | |||||
| } | |||||
| } | |||||
| private void checkMapProp(String propName) { | |||||
| RpcPropertyMeta propMeta = getPropertyMeta(propName); | |||||
| if (!RpcTypeParser.isMap(propMeta.getType())) { | |||||
| String pattern = "Property '%s' is '%s', not Map"; | |||||
| String message = String.format(pattern, propName, | |||||
| propMeta.getType()); | |||||
| throw new InvalidPropertyValueException(message); | |||||
| } | |||||
| } | |||||
| private void checkManyListProp(String propName) { | |||||
| RpcPropertyMeta propMeta = getPropertyMeta(propName); | |||||
| if (!RpcTypeParser.isManyList(propMeta.getType())) { | |||||
| String pattern = "Property '%s' is '%s', not ManyList"; | |||||
| String message = String.format(pattern, propName, | |||||
| propMeta.getType()); | |||||
| throw new InvalidPropertyValueException(message); | |||||
| } | |||||
| } | |||||
| private void checkRpcObjectType(String propName, RpcObject value) | |||||
| throws JsonRpcException { | |||||
| if (!RpcFacade.sDebug) { | |||||
| return; | |||||
| } | |||||
| RpcPropertyMeta prop = getPropertyMeta(propName); | |||||
| String propPath = prop.getType(); | |||||
| if (!RpcTypeParser.isRpcObject(propPath)) { | |||||
| String pattern = "Property '%s' is '%s', not rpc object"; | |||||
| String message = String.format(pattern, propName, propPath); | |||||
| throw new InvalidPropertyValueException(message); | |||||
| } | |||||
| RpcObjectMeta propMeta = RpcObjectMeta.get(propPath); | |||||
| if (value != null && propMeta != value.mMeta) { | |||||
| String pattern = "Property '%s' only accept object '%s'"; | |||||
| String message = String.format(pattern, propName, propPath); | |||||
| throw new InvalidPropertyValueException(message); | |||||
| } | |||||
| } | |||||
| private void checkNamedValueProp(String propName) { | |||||
| RpcPropertyMeta prop = getPropertyMeta(propName); | |||||
| if (!RpcTypeParser.isNamedValue(prop.getType())) { | |||||
| String msg = String.format( | |||||
| "Type: %s, prop: %s, prop type: %s, is not namedvalue", | |||||
| mMeta.getName(), prop.getName(), prop.getType()); | |||||
| throw new InvalidPropertyValueException(msg); | |||||
| } | |||||
| } | |||||
| static RpcObject create(RpcObjectMeta meta, JSONArray tableRow) | |||||
| throws JsonRpcException { | |||||
| RpcObject result = new RpcObject(meta); | |||||
| // must be LinkedHashMap<> because we rely on the value order | |||||
| LinkedHashMap<String, RpcPropertyMeta> props = (LinkedHashMap<String, RpcPropertyMeta>) meta | |||||
| .getProps(); | |||||
| int i = 0; | |||||
| for (RpcPropertyMeta prop : props.values()) { | |||||
| String name = prop.getName(); | |||||
| Object value = Helper.get(tableRow, i); | |||||
| if (value != null) { | |||||
| value = RpcTypeParser.fromJson(value, prop.getType()); | |||||
| } | |||||
| result.put(name, value); | |||||
| i++; | |||||
| } | |||||
| result.acceptChanges(); | |||||
| return result; | |||||
| } | |||||
| JSONObject toJson() throws JSONException { | |||||
| JSONObject result = new JSONObject(); | |||||
| for (Entry<String, Object> entry : entrySet()) { | |||||
| RpcPropertyMeta prop = mMeta.getProps().get(entry.getKey()); | |||||
| Object value = entry.getValue(); | |||||
| if (!equalToDefaultValue(prop, value)) { | |||||
| value = RpcTypeParser.toJson(value); | |||||
| result.put(entry.getKey(), value); | |||||
| } | |||||
| } | |||||
| return result; | |||||
| } | |||||
| void acceptChanges() { | |||||
| mBackup = new HashMap<String, Object>(this); | |||||
| for (RpcPropertyMeta prop : mMeta.getProps().values()) { | |||||
| if (RpcTypeParser.isManyList(prop.getType())) { | |||||
| getManyList(prop.getName()).acceptChanges(); | |||||
| } | |||||
| } | |||||
| } | |||||
| boolean isModified() { | |||||
| return !super.equals(mBackup); | |||||
| } | |||||
| @Override | |||||
| public boolean equals(Object object) { | |||||
| return object == this; | |||||
| } | |||||
| @Override | |||||
| public int hashCode() { | |||||
| return super.hashCode(); | |||||
| } | |||||
| private static boolean equalToDefaultValue(RpcPropertyMeta prop, | |||||
| Object value) { | |||||
| Object defaultValue = prop.getDefaultValue(); | |||||
| if (defaultValue == value) { | |||||
| return true; | |||||
| } | |||||
| if (defaultValue != null) { | |||||
| return defaultValue.equals(value); | |||||
| } | |||||
| return false; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,75 @@ | |||||
| package org.forks.jsonrpc; | |||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| import org.json.JSONArray; | |||||
| import org.json.JSONObject; | |||||
| import static org.forks.jsonrpc.Helper.*; | |||||
| import static org.forks.jsonrpc.RpcFacade.rpcCall; | |||||
| public final class RpcObjectMeta { | |||||
| private final String mName; | |||||
| private final Map<String, RpcPropertyMeta> mProps; | |||||
| public RpcObjectMeta(String name, Map<String, RpcPropertyMeta> props) { | |||||
| super(); | |||||
| mName = name; | |||||
| mProps = props; | |||||
| } | |||||
| public String getName() { | |||||
| return mName; | |||||
| } | |||||
| public Map<String, RpcPropertyMeta> getProps() { | |||||
| return mProps; | |||||
| } | |||||
| public static RpcObjectMeta parse(JSONObject jsonObj) | |||||
| throws JsonRpcException { | |||||
| String name = getString(jsonObj, "name"); | |||||
| JSONArray propsJson = getJsonArray(jsonObj, "props"); | |||||
| Map<String, RpcPropertyMeta> props = RpcPropertyMeta.parse(propsJson); | |||||
| return new RpcObjectMeta(name, props); | |||||
| } | |||||
| private static Map<String, RpcObjectMeta> sCache = new HashMap<String, RpcObjectMeta>(); | |||||
| public static RpcObjectMeta get(String objPath) throws JsonRpcException { | |||||
| RpcObjectMeta result; | |||||
| synchronized (sCache) { | |||||
| result = sCache.get(objPath); | |||||
| if (result != null) { | |||||
| return result; | |||||
| } | |||||
| } | |||||
| result = getFromServer(objPath); | |||||
| synchronized (sCache) { | |||||
| sCache.put(objPath, result); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| private static RpcObjectMeta getFromServer(String objPath) | |||||
| throws JsonRpcException { | |||||
| String getMethod = "/sbin/ObjectMeta/Get"; | |||||
| String method = RpcFacade.toFullErtPathByPrefixedErt(objPath, getMethod); | |||||
| JsonRpcResult result = rpcCall(method, objPath); | |||||
| return parse(result.getJsonObject()); | |||||
| } | |||||
| // only used in unit test | |||||
| static void registerMeta(String objPath, RpcObjectMeta meta) { | |||||
| if (sCache.put(objPath, meta) != null) { | |||||
| throw new JsonRpcRuntimeException(objPath); | |||||
| } | |||||
| } | |||||
| static void reset() { | |||||
| sCache.clear(); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,61 @@ | |||||
| package org.forks.jsonrpc; | |||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| import org.json.JSONArray; | |||||
| import org.json.JSONObject; | |||||
| import static org.forks.jsonrpc.Helper.*; | |||||
| public final class RpcPropertyMeta { | |||||
| private final String mName; | |||||
| private final String mType; | |||||
| private final Object mDefaultValue; | |||||
| public RpcPropertyMeta(String name, String type, Object defaultValue) { | |||||
| super(); | |||||
| mName = name; | |||||
| mType = type; | |||||
| mDefaultValue = defaultValue; | |||||
| } | |||||
| public String getName() { | |||||
| return mName; | |||||
| } | |||||
| public String getType() { | |||||
| return mType; | |||||
| } | |||||
| public Object getDefaultValue() { | |||||
| return mDefaultValue; | |||||
| } | |||||
| static Map<String, RpcPropertyMeta> parse(JSONArray arr) | |||||
| throws JsonRpcException { | |||||
| Map<String, RpcPropertyMeta> result = new HashMap<String, RpcPropertyMeta>( | |||||
| arr.length()); | |||||
| return parse(result, arr); | |||||
| } | |||||
| static Map<String, RpcPropertyMeta> parse( | |||||
| Map<String, RpcPropertyMeta> result, JSONArray arr) | |||||
| throws JsonRpcException { | |||||
| for (int i = 0; i < arr.length(); i++) { | |||||
| JSONObject item = getJsonObject(arr, i); | |||||
| RpcPropertyMeta prop = parse(item); | |||||
| result.put(prop.getName(), prop); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| private static RpcPropertyMeta parse(JSONObject json) | |||||
| throws JsonRpcException { | |||||
| String name = getString(json, "name"); | |||||
| String type = getString(json, "type"); | |||||
| Object defaultValue = json.opt("defaultvalue"); | |||||
| defaultValue = RpcTypeParser.fromJson(defaultValue, type); | |||||
| return new RpcPropertyMeta(name, type, defaultValue); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,5 @@ | |||||
| package org.forks.jsonrpc; | |||||
| interface RpcType { | |||||
| Object fromJson(Object jsonValue); | |||||
| } | |||||
| @ -0,0 +1,557 @@ | |||||
| package org.forks.jsonrpc; | |||||
| import static org.forks.jsonrpc.Helper.getJsonObject; | |||||
| import java.lang.reflect.Array; | |||||
| import java.math.BigDecimal; | |||||
| import java.util.ArrayList; | |||||
| import java.util.Date; | |||||
| import java.util.HashMap; | |||||
| import java.util.Iterator; | |||||
| import java.util.LinkedHashMap; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| import java.util.Map.Entry; | |||||
| import org.json.JSONArray; | |||||
| import org.json.JSONException; | |||||
| import org.json.JSONObject; | |||||
| public final class RpcTypeParser { | |||||
| static final String BYTE = "byte"; | |||||
| static final String SHORT = "short"; | |||||
| static final String INT = "int"; | |||||
| static final String LONG = "long"; | |||||
| static final String BOOL = "bool"; | |||||
| static final String DOUBLE = "double"; | |||||
| static final String SINGLE = "single"; | |||||
| static final String DECIMAL = "decimal"; | |||||
| static final String DATE = "datetime"; | |||||
| static final String STRING = "str"; | |||||
| static final String GUID = "guid"; | |||||
| static final String BIN = "bin"; | |||||
| static final String MONEY = "money"; | |||||
| static final String LOOKUPVALUE = "lookup"; | |||||
| static final String ENTITYKEY = "entitykey"; | |||||
| static final String TABLE = "table"; | |||||
| public static boolean isManyList(String rpcType) { | |||||
| return rpcType.startsWith("onetomany(") && rpcType.endsWith(")"); | |||||
| } | |||||
| public static String getManyListItemType(String rpcType) { | |||||
| return rpcType.substring("onetomany(".length(), rpcType.indexOf(')')); | |||||
| } | |||||
| public static boolean isList(String rpcType) { | |||||
| return startWith(rpcType, '['); | |||||
| } | |||||
| public static boolean isMap(String rpcType) { | |||||
| return startWith(rpcType, '{'); | |||||
| } | |||||
| public static boolean isRpcObject(String rpcType) { | |||||
| return startWith(rpcType, '/'); | |||||
| } | |||||
| public static boolean isNamedValue(String rpcType) { | |||||
| return rpcType.startsWith("namedvalue(") && rpcType.endsWith(")"); | |||||
| } | |||||
| public static String getNamedValueTidType(String rpcType) { | |||||
| return rpcType.substring("namedvalue(".length(), rpcType.length() - 1); | |||||
| } | |||||
| private static boolean startWith(String s, char ch) { | |||||
| return s.length() != 0 && s.charAt(0) == ch; | |||||
| } | |||||
| public static String getMapValueType(String rpcType) { | |||||
| int start = rpcType.indexOf(':'); | |||||
| return rpcType.substring(start + 1, rpcType.length() - 1); | |||||
| } | |||||
| public static String getListItemType(String rpcType) { | |||||
| return rpcType.substring(1, rpcType.length() - 1); | |||||
| } | |||||
| public static Object fromJson(Object jsonValue, String rpcType) | |||||
| throws JsonRpcException { | |||||
| if (isNull(jsonValue)) { | |||||
| return null; | |||||
| } | |||||
| RpcType type = parse(rpcType); | |||||
| return type.fromJson(jsonValue); | |||||
| } | |||||
| public static Object toJson(Object value) throws JSONException { | |||||
| if (value == null) { | |||||
| return JSONObject.NULL; | |||||
| } else if (value instanceof Date) { | |||||
| return dateToJson((Date) value); | |||||
| } else if (value instanceof RpcObject) { | |||||
| return ((RpcObject) value).toJson(); | |||||
| } else if (value instanceof ManyList) { | |||||
| return ((ManyList) value).toJson(); | |||||
| } else if (value instanceof List<?>) { | |||||
| return listToJson((List<?>) value); | |||||
| } else if (value.getClass().isArray()) { | |||||
| return arrayToJson(value); | |||||
| } else if (value instanceof Map<?, ?>) { | |||||
| return mapToJson((Map<?, ?>) value); | |||||
| } else if (value instanceof NamedValue) { | |||||
| return namedValueToJson((NamedValue) value); | |||||
| } else { | |||||
| return value; | |||||
| } | |||||
| } | |||||
| private static Object namedValueToJson(NamedValue value) { | |||||
| return value.getValue(); | |||||
| } | |||||
| private static Object mapToJson(Map<?, ?> map) throws JSONException { | |||||
| JSONObject result = new JSONObject(); | |||||
| for (Entry<?, ?> entry : map.entrySet()) { | |||||
| Object value = entry.getValue(); | |||||
| value = toJson(value); | |||||
| result.put((String) entry.getKey(), value); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| private static Object listToJson(List<?> coll) throws JSONException { | |||||
| JSONArray result = new JSONArray(); | |||||
| for (Object item : coll) { | |||||
| item = toJson(item); | |||||
| result.put(item); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| private static Object arrayToJson(Object array) throws JSONException { | |||||
| JSONArray result = new JSONArray(); | |||||
| int len = Array.getLength(array); | |||||
| for (int i = 0; i < len; i++) { | |||||
| Object item = Array.get(array, i); | |||||
| item = toJson(item); | |||||
| result.put(item); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| private static Object dateToJson(Date val) { | |||||
| return String.format("/Date(%tQ+0800)/", val); | |||||
| } | |||||
| synchronized static final RpcType parse(String rpcType) | |||||
| throws JsonRpcException { | |||||
| RpcType result = sRpcTypeCache.get(rpcType); | |||||
| if (result == null) { | |||||
| result = doParse(rpcType); | |||||
| sRpcTypeCache.put(rpcType, result); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| static final boolean isNull(Object jsonObj) { | |||||
| return jsonObj == null || jsonObj == JSONObject.NULL; | |||||
| } | |||||
| private static final Map<String, RpcType> sRpcTypeCache = new HashMap<String, RpcType>(); | |||||
| private static RpcType doParse(String rpcType) throws JsonRpcException { | |||||
| if (rpcType.equals(BOOL)) { | |||||
| return sBoolean; | |||||
| } else if (rpcType.equals(BYTE)) { | |||||
| return sByte; | |||||
| } else if (rpcType.equals(SHORT)) { | |||||
| return sShort; | |||||
| } else if (rpcType.equals(INT)) { | |||||
| return sInt; | |||||
| } else if (rpcType.equals(LONG)) { | |||||
| return sLong; | |||||
| } else if (rpcType.equals(SINGLE)) { | |||||
| return sDouble; | |||||
| } else if (rpcType.equals(DOUBLE)) { | |||||
| return sDouble; | |||||
| } else if (rpcType.equals(STRING)) { | |||||
| return sString; | |||||
| } else if(rpcType.equals(GUID)){ | |||||
| return sString; | |||||
| } else if (rpcType.equals(BIN)){ | |||||
| return sString; | |||||
| } | |||||
| else if (rpcType.equals(DECIMAL)) { | |||||
| return sDecimal; | |||||
| } else if (rpcType.equals(DATE)) { | |||||
| return sDate; | |||||
| } else if (rpcType.equals(MONEY)) { | |||||
| return sDecimal; | |||||
| } else if (rpcType.equals(LOOKUPVALUE)) { | |||||
| return sInt; | |||||
| } else if (isNamedValue(rpcType)) { | |||||
| return new RpcTypeNullWrap(new NamedValueRpcType(rpcType)); | |||||
| } else if (rpcType.equals(ENTITYKEY)) { | |||||
| return sLong; | |||||
| } else if (isList(rpcType)) { | |||||
| return new RpcTypeNullWrap(new ListRpcType(rpcType)); | |||||
| } else if (isRpcObject(rpcType)) { | |||||
| RpcObjectMeta meta = RpcObjectMeta.get(rpcType); | |||||
| return new RpcTypeNullWrap(new RpcObjectRpcType(meta)); | |||||
| } else if (isMap(rpcType)) { | |||||
| return new RpcTypeNullWrap(new MapRpcType(rpcType)); | |||||
| } else if (isManyList(rpcType)) { | |||||
| return new RpcTypeNullWrap(new ManyListRpcType(rpcType)); | |||||
| } else if (rpcType.equals(TABLE)) { | |||||
| return sTable; | |||||
| } else { | |||||
| throw new JsonRpcException("Unknown rpc type '" + rpcType + "'"); | |||||
| } | |||||
| } | |||||
| private static final RpcType sDirect = new RpcTypeNullWrap( | |||||
| new DirectRpcType()); | |||||
| static final RpcType sInt = sDirect; | |||||
| static final RpcType sBoolean = sDirect; | |||||
| static final RpcType sShort = new RpcTypeNullWrap(new ShortRpcType()); | |||||
| static final RpcType sByte = new RpcTypeNullWrap(new ByteRpcType()); | |||||
| static final RpcType sLong = new RpcTypeNullWrap(new LongRpcType()); | |||||
| static final RpcType sDouble = new RpcTypeNullWrap(new DoubleRpcType()); | |||||
| static final RpcType sString = sDirect; | |||||
| static final RpcType sDecimal = new RpcTypeNullWrap(new DecimalRpcType()); | |||||
| static final RpcType sDate = new RpcTypeNullWrap(new DateRpcType()); | |||||
| static final RpcType sTable = new RpcTypeNullWrap(new TableRpcType()); | |||||
| static final void reset() { | |||||
| sRpcTypeCache.clear(); | |||||
| } | |||||
| private static final class NamedValueRpcType implements RpcType { | |||||
| private final String mIdtype; | |||||
| public NamedValueRpcType(String rpcType) { | |||||
| mIdtype = getNamedValueTidType(rpcType); | |||||
| } | |||||
| public Object fromJson(Object jsonValue) { | |||||
| Short val = (Short) sShort.fromJson(jsonValue); | |||||
| return NamedValue.create(mIdtype, val); | |||||
| } | |||||
| } | |||||
| private static final class TableRpcType implements RpcType { | |||||
| public Object fromJson(Object jsonValue) { | |||||
| try { | |||||
| JSONObject obj = (JSONObject) jsonValue; | |||||
| Map<String, RpcPropertyMeta> cols = getTableColumns(obj); | |||||
| RpcObjectMeta meta = new RpcObjectMeta("table", cols); | |||||
| ArrayList<RpcObject> rows = getTableRows(obj, meta); | |||||
| RpcObject sumRow = getTableSumRow(obj, meta); | |||||
| return new DFDataTable(cols, rows, sumRow); | |||||
| } catch (JsonRpcException e) { | |||||
| throw new JsonRpcRuntimeException(e); | |||||
| } | |||||
| } | |||||
| private static Map<String, RpcPropertyMeta> getTableColumns( | |||||
| JSONObject obj) throws JsonRpcException { | |||||
| Map<String, RpcPropertyMeta> cols; | |||||
| JSONArray meta = obj.optJSONArray("_meta"); | |||||
| if (meta != null) { | |||||
| cols = parseTableCols(meta); | |||||
| } else { | |||||
| cols = new HashMap<String, RpcPropertyMeta>(); | |||||
| } | |||||
| return cols; | |||||
| } | |||||
| private static ArrayList<RpcObject> getTableRows(JSONObject obj, | |||||
| RpcObjectMeta rpcObjectMeta) throws JsonRpcException { | |||||
| ArrayList<RpcObject> rows; | |||||
| JSONArray rowsJson = obj.optJSONArray("rows"); | |||||
| if (rowsJson == null) { | |||||
| rows = new ArrayList<RpcObject>(); | |||||
| } else { | |||||
| rows = doGetTableRows(rowsJson, rpcObjectMeta); | |||||
| } | |||||
| return rows; | |||||
| } | |||||
| private static RpcObject getTableSumRow(JSONObject obj, | |||||
| RpcObjectMeta meta) throws JsonRpcException { | |||||
| RpcObject sumRow = null; | |||||
| JSONArray sumRowJson = obj.optJSONArray("sumrow"); | |||||
| if (sumRowJson != null) { | |||||
| sumRow = RpcObject.create(meta, sumRowJson); | |||||
| } | |||||
| return sumRow; | |||||
| } | |||||
| private static ArrayList<RpcObject> doGetTableRows(JSONArray rows, | |||||
| RpcObjectMeta meta) throws JsonRpcException { | |||||
| ArrayList<RpcObject> result = new ArrayList<RpcObject>( | |||||
| rows.length()); | |||||
| for (int i = 0; i < rows.length(); i++) { | |||||
| result.add(RpcObject.create(meta, Helper.getJsonArray(rows, i))); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| private static Map<String, RpcPropertyMeta> parseTableCols( | |||||
| JSONArray _meta) throws JsonRpcException { | |||||
| // use LinkedHashMap to maintain column order | |||||
| Map<String, RpcPropertyMeta> result = new LinkedHashMap<String, RpcPropertyMeta>( | |||||
| _meta.length()); | |||||
| return RpcPropertyMeta.parse(result, _meta); | |||||
| } | |||||
| } | |||||
| private static final class MapRpcType implements RpcType { | |||||
| private final RpcType mValueType; | |||||
| public MapRpcType(String rpcType) throws JsonRpcException { | |||||
| checkMapKeyType(rpcType); | |||||
| String valueType = getMapValueType(rpcType); | |||||
| mValueType = parse(valueType); | |||||
| } | |||||
| public Object fromJson(Object jsonValue) { | |||||
| JSONObject obj = (JSONObject) jsonValue; | |||||
| Map<Object, Object> result = new HashMap<Object, Object>( | |||||
| obj.length()); | |||||
| Iterator<String> iter = obj.keys(); | |||||
| while (iter.hasNext()) { | |||||
| String key = iter.next(); | |||||
| Object value; | |||||
| try { | |||||
| value = Helper.get(obj, key); | |||||
| } catch (JsonRpcException e) { | |||||
| throw new JsonRpcRuntimeException(e); | |||||
| } | |||||
| if (RpcTypeParser.isNull(value)) { | |||||
| result.put(key, null); | |||||
| } else { | |||||
| result.put(key, mValueType.fromJson(value)); | |||||
| } | |||||
| } | |||||
| return result; | |||||
| } | |||||
| private static void checkMapKeyType(String rpcType) { | |||||
| String keyType = getMapKeyType(rpcType); | |||||
| if (!keyType.equals(STRING)) { | |||||
| String message = "jsonrpc android client only support `str' map key. " | |||||
| + rpcType; | |||||
| throw new JsonRpcRuntimeException(message); | |||||
| } | |||||
| } | |||||
| private static String getMapKeyType(String rpcType) { | |||||
| int end = rpcType.indexOf(':'); | |||||
| return rpcType.substring(1, end); | |||||
| } | |||||
| } | |||||
| private static final class RpcTypeNullWrap implements RpcType { | |||||
| private final RpcType mInner; | |||||
| public RpcTypeNullWrap(RpcType inner) { | |||||
| mInner = inner; | |||||
| } | |||||
| public Object fromJson(Object jsonValue) { | |||||
| if (jsonValue == JSONObject.NULL) { | |||||
| return null; | |||||
| } | |||||
| return mInner.fromJson(jsonValue); | |||||
| } | |||||
| } | |||||
| private static final class ManyListRpcType implements RpcType { | |||||
| private final RpcObjectMeta mItemMeta; | |||||
| private final RpcType mItemType; | |||||
| public ManyListRpcType(String rpcType) throws JsonRpcException { | |||||
| String itemRpcType = getManyListItemType(rpcType); | |||||
| mItemType = parse(itemRpcType); | |||||
| mItemMeta = RpcObjectMeta.get(itemRpcType); | |||||
| } | |||||
| public Object fromJson(Object jsonValue) { | |||||
| try { | |||||
| ManyList result = new ManyList(mItemMeta); | |||||
| restoreFromJson((JSONObject) jsonValue, result); | |||||
| return result; | |||||
| } catch (JsonRpcException e) { | |||||
| throw new JsonRpcRuntimeException(e); | |||||
| } | |||||
| } | |||||
| private void restoreFromJson(JSONObject json, ManyList result) | |||||
| throws JsonRpcException { | |||||
| JSONArray _data = json.optJSONArray("_data"); | |||||
| if (_data != null && _data.length() != 0) { | |||||
| fromJson(result, _data); | |||||
| } | |||||
| } | |||||
| private void fromJson(ManyList result, JSONArray _data) | |||||
| throws JsonRpcException { | |||||
| for (int i = 0; i < _data.length(); i++) { | |||||
| JSONObject itemJson = getJsonObject(_data, i); | |||||
| RpcObject item = (RpcObject) mItemType.fromJson(itemJson); | |||||
| result.add(item); | |||||
| } | |||||
| result.acceptChanges(); | |||||
| } | |||||
| } | |||||
| private static final class DirectRpcType implements RpcType { | |||||
| public Object fromJson(Object jsonValue) { | |||||
| return jsonValue; | |||||
| } | |||||
| } | |||||
| private static final class LongRpcType implements RpcType { | |||||
| public Object fromJson(Object jsonValue) { | |||||
| if (jsonValue instanceof Long) { | |||||
| return jsonValue; | |||||
| } | |||||
| return ((Integer) jsonValue).longValue(); | |||||
| } | |||||
| } | |||||
| private static final class ShortRpcType implements RpcType { | |||||
| public Object fromJson(Object jsonValue) { | |||||
| return ((Integer) jsonValue).shortValue(); | |||||
| } | |||||
| } | |||||
| private static final class ByteRpcType implements RpcType { | |||||
| public Object fromJson(Object jsonValue) { | |||||
| short result = ((Integer) jsonValue).shortValue(); | |||||
| if (result > 255 || result < 0) { | |||||
| throw new JsonRpcRuntimeException("can not cast value " | |||||
| + result + " to dotnet byte type"); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| } | |||||
| private static final class DoubleRpcType implements RpcType { | |||||
| public Object fromJson(Object jsonValue) { | |||||
| if (jsonValue instanceof Double) { | |||||
| return jsonValue; | |||||
| } | |||||
| return ((Number) jsonValue).doubleValue(); | |||||
| } | |||||
| } | |||||
| private static final class DecimalRpcType implements RpcType { | |||||
| public Object fromJson(Object jsonValue) { | |||||
| return new BigDecimal(jsonValue.toString()); | |||||
| } | |||||
| } | |||||
| private static final class ListRpcType implements RpcType { | |||||
| private final RpcType mItemType; | |||||
| public ListRpcType(String rpcType) throws JsonRpcException { | |||||
| String itemRpcType = getListItemType(rpcType); | |||||
| mItemType = parse(itemRpcType); | |||||
| } | |||||
| public Object fromJson(Object jsonValue) { | |||||
| JSONArray json = (JSONArray) jsonValue; | |||||
| List<Object> result = new ArrayList<Object>(json.length()); | |||||
| for (int i = 0; i < json.length(); i++) { | |||||
| Object itemJson; | |||||
| try { | |||||
| itemJson = Helper.get(json, i); | |||||
| } catch (JsonRpcException e) { | |||||
| throw new JsonRpcRuntimeException(e); | |||||
| } | |||||
| result.add(mItemType.fromJson(itemJson)); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| } | |||||
| private static final class RpcObjectRpcType implements RpcType { | |||||
| private final RpcObjectMeta mMeta; | |||||
| public RpcObjectRpcType(RpcObjectMeta meta) { | |||||
| mMeta = meta; | |||||
| } | |||||
| public Object fromJson(Object jsonValue) { | |||||
| try { | |||||
| JSONObject json = (JSONObject) jsonValue; | |||||
| RpcObject result = new RpcObject(mMeta); | |||||
| for (RpcPropertyMeta prop : mMeta.getProps().values()) { | |||||
| String name = prop.getName(); | |||||
| Object value = json.opt(name); | |||||
| if (value != null) { | |||||
| result.put(name, | |||||
| RpcTypeParser.fromJson(value, prop.getType())); | |||||
| } else if (json.has(name)) { | |||||
| result.put(name, null); | |||||
| } | |||||
| } | |||||
| result.acceptChanges(); | |||||
| return result; | |||||
| } catch (JsonRpcException e) { | |||||
| throw new JsonRpcRuntimeException(e); | |||||
| } | |||||
| } | |||||
| } | |||||
| private static final class DateRpcType implements RpcType { | |||||
| public Object fromJson(Object jsonValue) { | |||||
| String s = (String) jsonValue; | |||||
| if (!RpcFacade.sDebug || isDateString(s)) { | |||||
| return new Date(parseMillsSinceEpoch(s)); | |||||
| } | |||||
| String message = "can not convert '" + s + "' to Date"; | |||||
| throw new JsonRpcRuntimeException(message); | |||||
| } | |||||
| private static boolean isDateString(String s) { | |||||
| return s.startsWith("/Date(") && s.endsWith("+0800)/"); | |||||
| } | |||||
| private static final long parseMillsSinceEpoch(String s) { | |||||
| int endIdx = s.indexOf('+'); | |||||
| String numStr = s.substring(6, endIdx); | |||||
| return Long.parseLong(numStr); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,3 @@ | |||||
| <resources> | |||||
| <string name="app_name">jsonrpc</string> | |||||
| </resources> | |||||
| @ -0,0 +1,15 @@ | |||||
| package org.forks.jsonrpc; | |||||
| import org.junit.Test; | |||||
| import static org.junit.Assert.*; | |||||
| /** | |||||
| * To work on unit tests, switch the Test Artifact in the Build Variants view. | |||||
| */ | |||||
| public class ExampleUnitTest { | |||||
| @Test | |||||
| public void addition_isCorrect() throws Exception { | |||||
| assertEquals(4, 2 + 2); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1 @@ | |||||
| include ':app', ':jsonrpc' | |||||