| @ -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' | |||