├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── qaplug_profiles.xml └── runConfigurations.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── libs │ └── butterknife-6.0.0.jar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── zone │ │ └── forgithubproject │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── zone │ │ │ └── forgithubproject │ │ │ ├── AdActivity.java │ │ │ ├── LoginActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── WebViewActivity.java │ │ │ ├── base │ │ │ ├── BaseActivity.java │ │ │ ├── BaseBean.java │ │ │ ├── IBaseView.java │ │ │ ├── IPBase.java │ │ │ └── PBase.java │ │ │ ├── contract │ │ │ ├── AdContract.java │ │ │ ├── LoginContract.java │ │ │ └── MainContract.java │ │ │ ├── model │ │ │ ├── AdModelImpl.java │ │ │ ├── LoginModelImpl.java │ │ │ ├── MainModelImpl.java │ │ │ └── bean │ │ │ │ ├── AdMessageBean.java │ │ │ │ └── LoginCheckBean.java │ │ │ ├── myApplication │ │ │ └── MyApplication.java │ │ │ ├── presenter │ │ │ ├── AdPresenterImpl.java │ │ │ ├── LoginPresenterImpl.java │ │ │ └── MainPresenterImpl.java │ │ │ ├── service │ │ │ ├── RetrofitService.java │ │ │ └── RetrofitServiceInstance.java │ │ │ ├── utils │ │ │ ├── L.java │ │ │ ├── NetUtils.java │ │ │ ├── Rx │ │ │ │ ├── RxHelper.java │ │ │ │ └── RxSubscribe.java │ │ │ ├── SPUtils.java │ │ │ └── T.java │ │ │ └── weidge │ │ │ └── ProgressWebView.java │ └── res │ │ ├── drawable │ │ ├── jyz_png.png │ │ ├── splash_jyz.jpg │ │ └── splash_skip.xml │ │ ├── layout │ │ ├── activity_ad.xml │ │ ├── activity_login.xml │ │ ├── activity_main.xml │ │ ├── activity_webview.xml │ │ └── head.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── zone │ └── forgithubproject │ └── ExampleUnitTest.java ├── build.gradle ├── freeline.py ├── freeline ├── freeline.py ├── freeline_core │ ├── .idea │ │ ├── dictionaries │ │ │ └── huangyong.xml │ │ ├── inspectionProfiles │ │ │ ├── Project_Default.xml │ │ │ └── profiles_settings.xml │ │ ├── misc.xml │ │ ├── modules.xml │ │ ├── vcs.xml │ │ └── workspace.xml │ ├── __init__.py │ ├── android_tools.py │ ├── build_commands.py │ ├── builder.py │ ├── command.py │ ├── cursor.py │ ├── dispatcher.py │ ├── exceptions.py │ ├── freeline_build.py │ ├── gradle_clean_build.py │ ├── gradle_inc_build.py │ ├── gradle_tools.py │ ├── hackwindows.py │ ├── init.py │ ├── logger.py │ ├── sync_client.py │ ├── task.py │ ├── task_engine.py │ ├── terminal.py │ ├── tracing.py │ ├── utils.py │ └── version.py └── release-tools │ ├── DexMerge.jar │ ├── FreelineAapt │ ├── FreelineAapt.exe │ ├── FreelineAapt_ │ ├── databinding-cli.jar │ ├── databinding-cli8.jar │ ├── dexmerger │ ├── dx │ ├── dx.bat │ ├── dx.jar │ ├── jasmin │ ├── jasmin.jar │ ├── lib64 │ └── libc++.so │ ├── mainDexClasses │ ├── mainDexClasses.bat │ ├── mainDexClasses.rules │ └── manifest.txt ├── freeline_core ├── .idea │ ├── dictionaries │ │ └── huangyong.xml │ ├── inspectionProfiles │ │ ├── Project_Default.xml │ │ └── profiles_settings.xml │ ├── misc.xml │ ├── modules.xml │ ├── vcs.xml │ └── workspace.xml ├── __init__.py ├── android_tools.py ├── build_commands.py ├── builder.py ├── command.py ├── cursor.py ├── dispatcher.py ├── exceptions.py ├── freeline_build.py ├── gradle_clean_build.py ├── gradle_inc_build.py ├── gradle_tools.py ├── hackwindows.py ├── init.py ├── logger.py ├── sync_client.py ├── task.py ├── task_engine.py ├── terminal.py ├── tracing.py ├── utils.py └── version.py ├── freeline_project_description.json ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 49 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.antfortune.freeline' 3 | 4 | android { 5 | compileSdkVersion 24 6 | buildToolsVersion "24.0.3" 7 | defaultConfig { 8 | applicationId "com.zone.forgithubproject" 9 | minSdkVersion 15 10 | targetSdkVersion 24 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile fileTree(include: ['*.jar'], dir: 'libs') 25 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 26 | exclude group: 'com.android.support', module: 'support-annotations' 27 | }) 28 | compile 'com.android.support:appcompat-v7:24.2.1' 29 | testCompile 'junit:junit:4.12' 30 | compile 'com.zhy:autolayout:1.4.5' 31 | 32 | compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4' 33 | compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4' 34 | compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4' 35 | 36 | compile 'io.reactivex:rxandroid:1.2.0' 37 | compile 'io.reactivex:rxjava:1.1.0' 38 | 39 | 40 | compile 'com.android.support:design:24.2.0' 41 | compile files('libs/butterknife-6.0.0.jar') 42 | compile 'com.github.bumptech.glide:glide:3.7.0' 43 | 44 | compile 'com.squareup.okhttp3:okhttp:3.0.1' 45 | compile 'com.squareup.okhttp3:logging-interceptor:3.0.1' 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/libs/butterknife-6.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/app/libs/butterknife-6.0.0.jar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in Z:\SDK/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/zone/forgithubproject/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.zone.forgithubproject", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | > 28 | 29 | 30 | 31 | 32 | 33 | 34 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/AdActivity.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject; 2 | 3 | import android.content.Intent; 4 | import android.graphics.Bitmap; 5 | import android.os.Bundle; 6 | import android.os.Handler; 7 | import android.os.Message; 8 | import android.text.TextUtils; 9 | import android.view.View; 10 | import android.view.WindowManager; 11 | import android.widget.ImageView; 12 | import android.widget.LinearLayout; 13 | import android.widget.TextView; 14 | 15 | import com.bumptech.glide.Glide; 16 | import com.zone.forgithubproject.myApplication.MyApplication; 17 | import com.zone.forgithubproject.base.BaseActivity; 18 | import com.zone.forgithubproject.contract.AdContract; 19 | import com.zone.forgithubproject.model.bean.LoginCheckBean; 20 | import com.zone.forgithubproject.presenter.AdPresenterImpl; 21 | import com.zone.forgithubproject.utils.L; 22 | import com.zone.forgithubproject.utils.NetUtils; 23 | import com.zone.forgithubproject.utils.SPUtils; 24 | 25 | import butterknife.ButterKnife; 26 | import butterknife.InjectView; 27 | import butterknife.OnClick; 28 | 29 | 30 | 31 | /** 32 | * Created by zone on 2016/7/18. 33 | */ 34 | public class AdActivity extends BaseActivity implements AdContract.View { 35 | @InjectView(R.id.tv_second) 36 | TextView tvSecond; 37 | @InjectView(R.id.layout_skip) 38 | LinearLayout layoutSkip; 39 | int timeCount = 0; 40 | boolean continueCount = true; 41 | @InjectView(R.id.iv_advertising) 42 | ImageView ivAdvertising; 43 | private LoginCheckBean loginCheckBean; 44 | Handler handler = new Handler() { 45 | @Override 46 | public void handleMessage(Message msg) { 47 | super.handleMessage(msg); 48 | countNum(); 49 | if (continueCount) { 50 | handler.sendMessageDelayed(handler.obtainMessage(-1),1000); 51 | } 52 | } 53 | }; 54 | private Bitmap bitmap; 55 | private AdPresenterImpl pAd; 56 | private int initTimeCount; 57 | 58 | 59 | @Override 60 | protected void onCreate(Bundle savedInstanceState) { 61 | super.onCreate(savedInstanceState); 62 | setContentView(R.layout.activity_ad); 63 | ButterKnife.inject(this); 64 | pAd = new AdPresenterImpl(); 65 | pAd.attachView(this); 66 | initTimeCount = 6; 67 | loginCheckBean = new LoginCheckBean(); 68 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); 69 | if (NetUtils.isConnected(AdActivity.this)) { 70 | pAd.getLoginCheck(); 71 | } 72 | layoutSkip.setVisibility(View.INVISIBLE); 73 | handler.sendMessageDelayed(handler.obtainMessage(-1),1000); 74 | } 75 | 76 | 77 | @OnClick({R.id.iv_advertising, R.id.layout_skip}) 78 | public void onClick(View view) { 79 | switch (view.getId()) { 80 | case R.id.iv_advertising: 81 | String url = (String) SPUtils.get(this, "adUrl", "null"); 82 | if (!url.equals("null")) { 83 | continueCount = false; 84 | Intent intent = new Intent(AdActivity.this, WebViewActivity.class); 85 | intent.putExtra("title", "震惊,程序员竟然如此写代码,真相竟然是这个..."); 86 | intent.putExtra("url", url); 87 | intent.putExtra("from", "advertising"); 88 | startActivity(intent); 89 | finish(); 90 | } 91 | break; 92 | case R.id.layout_skip: 93 | continueCount = false; 94 | toNextActivity(); 95 | finish(); 96 | break; 97 | } 98 | } 99 | 100 | 101 | private int countNum() {//数秒 102 | timeCount++; 103 | if (timeCount == 3) {//数秒,超过3秒后如果没有网络,则进入下一个页面 104 | if (!NetUtils.isConnected(AdActivity.this)) { 105 | continueCount = false; 106 | toNextActivity(); 107 | finish(); 108 | } 109 | if (!loginCheckBean.isPlayAd()) {//如果服务端不允许播放广告,则直接进入下一个页面 110 | continueCount = false; 111 | toNextActivity(); 112 | finish(); 113 | } else {//如果播放,则获取本地的图片 114 | ivAdvertising.setVisibility(View.VISIBLE); 115 | layoutSkip.setVisibility(View.VISIBLE); 116 | } 117 | } 118 | if (timeCount == initTimeCount) { 119 | continueCount = false; 120 | toNextActivity(); 121 | finish(); 122 | } 123 | return timeCount; 124 | } 125 | 126 | public void toNextActivity() {//根据是否保存有 token 判断去登录界面还是主界面 127 | L.d("到下一个界面"); 128 | Intent intent = null; 129 | String token = (String) SPUtils.get(AdActivity.this, "token", ""); 130 | if (TextUtils.isEmpty(token)) { 131 | intent = new Intent(AdActivity.this, LoginActivity.class); 132 | } else { 133 | intent = new Intent(AdActivity.this, MainActivity.class); 134 | MyApplication.setToken(token); 135 | } 136 | startActivity(intent); 137 | finish(); 138 | } 139 | 140 | 141 | 142 | 143 | @Override 144 | public void setAdTime(int count) { 145 | initTimeCount = count + 3; 146 | } 147 | 148 | @Override 149 | public void setLoginCheckBean(LoginCheckBean loginCheckBean) { 150 | this.loginCheckBean = loginCheckBean; 151 | } 152 | 153 | @Override 154 | public void setAdImg(Bitmap bitmap) { 155 | if (bitmap != null) { 156 | ivAdvertising.setImageBitmap(bitmap); 157 | } else {//加强用户体验,如果是获取到的bitmap为null,则直接跳过 158 | continueCount = false; 159 | toNextActivity(); 160 | finish(); 161 | } 162 | } 163 | 164 | @Override 165 | protected void onDestroy() { 166 | super.onDestroy(); 167 | if (bitmap != null) {//进行bitmap回收 168 | bitmap.recycle(); 169 | } 170 | pAd.detachView(); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/LoginActivity.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.zone.forgithubproject.base.BaseActivity; 6 | 7 | public class LoginActivity extends BaseActivity { 8 | 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.activity_login); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.zone.forgithubproject.base.BaseActivity; 6 | 7 | public class MainActivity extends BaseActivity { 8 | 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.activity_main); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/WebViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.os.SystemClock; 6 | import android.support.v7.widget.Toolbar; 7 | import android.text.TextUtils; 8 | import android.view.KeyEvent; 9 | import android.webkit.WebSettings; 10 | import android.webkit.WebView; 11 | import android.webkit.WebViewClient; 12 | import android.widget.Toast; 13 | 14 | 15 | import com.zone.forgithubproject.myApplication.MyApplication; 16 | import com.zone.forgithubproject.weidge.ProgressWebView; 17 | import com.zone.forgithubproject.base.BaseActivity; 18 | import com.zone.forgithubproject.utils.SPUtils; 19 | 20 | import butterknife.ButterKnife; 21 | 22 | /** 23 | * Created by john on 2016/7/18. 24 | */ 25 | public class WebViewActivity extends BaseActivity { 26 | 27 | 28 | ProgressWebView wv; 29 | private long djtime[] = new long[2]; 30 | private Toolbar toolbar; 31 | private String from; 32 | 33 | 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | // setTheme(); 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_webview); 40 | ButterKnife.inject(this); 41 | toolbar = (Toolbar) findViewById(R.id.toolbar); 42 | wv = (ProgressWebView) findViewById(R.id.wv); 43 | setSupportActionBar(toolbar); 44 | iniBase(); 45 | String title = getIntent().getStringExtra("title"); 46 | String url = getIntent().getStringExtra("url"); 47 | from = getIntent().getStringExtra("from"); 48 | toolbar.setTitle(title); 49 | wv.setWebViewClient(new WebViewClient() {//防止调用系统的浏览器 50 | @Override 51 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 52 | view.loadUrl(url); 53 | return true; 54 | } 55 | }); 56 | // wv.setWebChromeClient(new WebChromeClient(){ 57 | // @Override 58 | // public void onProgressChanged(WebView view, int newProgress) { 59 | // //get the newProgress and refresh progress bar 60 | // L.d("==progress==>"+String.valueOf(newProgress)); 61 | // } 62 | // 63 | // @Override 64 | // public void onReceivedTitle(WebView view, String title) { 65 | // super.onReceivedTitle(view, title); 66 | // L.d("==Title==>"+title); 67 | // 68 | // } 69 | // }); 70 | wv.getSettings().setDefaultTextEncodingName("utf-8"); 71 | //支持js 72 | wv.getSettings().setJavaScriptEnabled(true); 73 | //设置背景颜色 透明 74 | // wv.setBackgroundColor(Color.argb(0, 0, 0, 0)); 75 | WebSettings webSettings = wv.getSettings(); 76 | webSettings.setSupportZoom(true); 77 | //启用数据库 78 | // webSettings.setDatabaseEnabled(true); 79 | // 80 | ////设置定位的数据库路径 81 | // 82 | // String dir = this.getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath(); 83 | // 84 | // webSettings.setGeolocationDatabasePath(dir); 85 | //启用地理定位 86 | webSettings.setGeolocationEnabled(true); 87 | //开启DomStorage缓存 88 | 89 | webSettings.setDomStorageEnabled(true); 90 | 91 | // wv.setWebChromeClient(new WebChromeClient() {//有这个好像就能通过webview定位了 92 | // 93 | // @Override 94 | // public void onReceivedIcon(WebView view, Bitmap icon) { 95 | // super.onReceivedIcon(view, icon); 96 | // } 97 | // 98 | // @Override 99 | // public void onGeolocationPermissionsShowPrompt(String origin,GeolocationPermissions.Callback callback) { 100 | // callback.invoke(origin, true, false); 101 | // super.onGeolocationPermissionsShowPrompt(origin, callback); 102 | // } 103 | // }); 104 | wv.loadUrl(url); 105 | } 106 | 107 | @Override 108 | public void onBackPressed() { 109 | super.onBackPressed(); 110 | if (from != null && from.equals("advertising")) { 111 | Intent intent = null; 112 | String token = (String) SPUtils.get(WebViewActivity.this, "token", ""); 113 | if (TextUtils.isEmpty(token)) { 114 | intent = new Intent(WebViewActivity.this, LoginActivity.class); 115 | } else { 116 | intent = new Intent(WebViewActivity.this, MainActivity.class); 117 | MyApplication.setToken(token); 118 | } 119 | startActivity(intent); 120 | finish(); 121 | } 122 | 123 | } 124 | 125 | @Override 126 | public boolean onKeyDown(int keyCode, KeyEvent event) { 127 | if (from != null && from.equals("advertising")) {//判断是否来自广告页面 128 | toNextActivity(); 129 | finish(); 130 | return true; 131 | } 132 | 133 | if (keyCode == KeyEvent.KEYCODE_BACK && wv.canGoBack()) { 134 | wv.goBack(); 135 | return true; 136 | } else { 137 | System.arraycopy(djtime, 1, djtime, 0, djtime.length - 1); 138 | djtime[djtime.length - 1] = SystemClock.uptimeMillis(); 139 | if (djtime[0] >= (SystemClock.uptimeMillis() - 1000)) { 140 | finish(); 141 | } else { 142 | Toast.makeText(this, "再按一次返回", Toast.LENGTH_SHORT).show(); 143 | } 144 | return true; 145 | } 146 | } 147 | 148 | private void toNextActivity() {//判断去登录界面还是主界面 149 | Intent intent = null; 150 | String token = (String) SPUtils.get(WebViewActivity.this, "token", ""); 151 | if (TextUtils.isEmpty(token)) { 152 | intent = new Intent(WebViewActivity.this, LoginActivity.class); 153 | } else { 154 | intent = new Intent(WebViewActivity.this, MainActivity.class); 155 | MyApplication.setToken(token); 156 | } 157 | startActivity(intent); 158 | } 159 | 160 | 161 | } 162 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/base/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.base; 2 | 3 | import android.app.AlertDialog; 4 | import android.app.ProgressDialog; 5 | import android.content.Context; 6 | import android.content.DialogInterface; 7 | import android.os.Bundle; 8 | import android.view.LayoutInflater; 9 | import android.view.MenuItem; 10 | import android.view.View; 11 | 12 | 13 | import com.zhy.autolayout.AutoLayoutActivity; 14 | import com.zone.forgithubproject.utils.T; 15 | 16 | 17 | /** 18 | * Created by john on 2016/7/26. 19 | */ 20 | public abstract class BaseActivity extends AutoLayoutActivity { 21 | 22 | private AlertDialog dialog; 23 | private ProgressDialog mProgressDialog = null; 24 | 25 | public interface Convert { 26 | void transaction(View dialogView, AlertDialog alertDialog); 27 | } 28 | 29 | public interface DialogOnClickListener { 30 | void positiveListener(DialogInterface dialog, int which); 31 | 32 | void negativeListener(DialogInterface dialog, int which); 33 | } 34 | 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | } 40 | 41 | protected void iniBase() { 42 | getSupportActionBar().setHomeButtonEnabled(true); //设置返回键可用 43 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);//创建返回键,并实现打开关/闭监听 44 | getSupportActionBar().setDisplayShowTitleEnabled(false);//隐藏系统默认的Title 45 | } 46 | 47 | protected void backhome(MenuItem item) { 48 | switch (item.getItemId()) { 49 | case android.R.id.home: 50 | onBackPressed(); 51 | break; 52 | } 53 | } 54 | 55 | public void showProgressDialog(String message) { 56 | if (mProgressDialog == null) { 57 | mProgressDialog = new ProgressDialog(this); 58 | } 59 | mProgressDialog.setMessage(message); 60 | mProgressDialog.setCanceledOnTouchOutside(false); 61 | mProgressDialog.show(); 62 | } 63 | 64 | 65 | public void hideProgressDialog() { 66 | if (mProgressDialog != null) { 67 | mProgressDialog.hide(); 68 | } 69 | } 70 | 71 | 72 | public void showAlertDialog(String title, String message) { 73 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 74 | builder.setTitle(title); 75 | builder.setMessage(message); 76 | builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { 77 | @Override 78 | public void onClick(DialogInterface dialog, int which) { 79 | 80 | } 81 | }); 82 | AlertDialog dialog = builder.create(); 83 | dialog.show(); 84 | } 85 | 86 | 87 | 88 | public void showToast(String message) { 89 | T.showShort(this, message); 90 | } 91 | 92 | @Override 93 | protected void onResume() { 94 | super.onResume(); 95 | } 96 | 97 | 98 | @Override 99 | protected void onDestroy() { 100 | super.onDestroy(); 101 | if (mProgressDialog != null) { 102 | mProgressDialog.dismiss(); 103 | } 104 | } 105 | 106 | @Override 107 | protected void onPause() { 108 | super.onPause(); 109 | 110 | } 111 | 112 | @Override 113 | public boolean onOptionsItemSelected(MenuItem item) { 114 | switch (item.getItemId()) { 115 | case android.R.id.home: 116 | onBackPressed(); 117 | break; 118 | } 119 | return super.onOptionsItemSelected(item); 120 | } 121 | 122 | public void onBackPressed() { 123 | super.onBackPressed(); 124 | finish(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/base/BaseBean.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.base; 2 | 3 | /** 4 | * Created by john on 2016/9/8. 5 | */ 6 | public class BaseBean { 7 | public String code; 8 | public String message; 9 | public T data; 10 | 11 | public boolean success() { 12 | return code.equals("200"); 13 | } 14 | 15 | public boolean error404() { 16 | return code.equals("404"); 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | return "code=>"+code+"\nmessage=>"+message+"\ndata=>"+data; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/base/IBaseView.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.base; 2 | 3 | /** 4 | * Created by john on 2016/7/26. 5 | */ 6 | public interface IBaseView { 7 | void showToast(String message); 8 | void showAlertDialog(String title, String message); 9 | void hideProgressDialog(); 10 | void showProgressDialog(String message); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/base/IPBase.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.base; 2 | 3 | /** 4 | * Created by john on 2016/9/13. 5 | */ 6 | public interface IPBase { 7 | void attachView(V view); 8 | void detachView(); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/base/PBase.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.base; 2 | 3 | import com.zone.forgithubproject.utils.L; 4 | 5 | 6 | /** 7 | * Created by zone on 2016/9/13. 8 | */ 9 | public class PBase implements IPBase { 10 | public V view; 11 | 12 | @Override 13 | public void attachView(V view) { 14 | this.view = view; 15 | } 16 | 17 | @Override 18 | public void detachView() { 19 | this.view = null; 20 | } 21 | 22 | public boolean isAttachView() { 23 | return view != null; 24 | } 25 | 26 | public V getMyView() { 27 | return view; 28 | } 29 | 30 | public void checkAttachView() { 31 | if (!isAttachView()) { 32 | L.d("====>View没有连接!!!"); 33 | } 34 | } 35 | 36 | 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/contract/AdContract.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.contract; 2 | 3 | import android.graphics.Bitmap; 4 | 5 | import com.zone.forgithubproject.base.IBaseView; 6 | import com.zone.forgithubproject.model.bean.LoginCheckBean; 7 | 8 | /** 9 | * Created by zone on 2017/3/26. 10 | */ 11 | 12 | public class AdContract { 13 | public interface View extends IBaseView{ 14 | void setAdTime(int count); 15 | 16 | void setLoginCheckBean(LoginCheckBean loginCheckBean); 17 | 18 | void setAdImg(Bitmap bitmap); 19 | } 20 | 21 | public interface Presenter { 22 | } 23 | 24 | public interface Model{ 25 | } 26 | 27 | 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/contract/LoginContract.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.contract; 2 | 3 | /** 4 | * Created by zone on 2017/3/26. 5 | */ 6 | 7 | public class LoginContract { 8 | public interface View{ 9 | 10 | } 11 | 12 | public interface Presenter{ 13 | 14 | } 15 | 16 | public interface Model{ 17 | 18 | } 19 | 20 | 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/contract/MainContract.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.contract; 2 | 3 | /** 4 | * Created by zone on 2017/3/26. 5 | */ 6 | 7 | public class MainContract { 8 | 9 | public interface View{ 10 | } 11 | 12 | public interface Presenter{ 13 | } 14 | 15 | public interface Model{ 16 | } 17 | 18 | 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/model/AdModelImpl.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.model; 2 | 3 | import com.zone.forgithubproject.contract.AdContract; 4 | import com.zone.forgithubproject.model.bean.AdMessageBean; 5 | import com.zone.forgithubproject.model.bean.LoginCheckBean; 6 | import com.zone.forgithubproject.service.RetrofitService; 7 | import com.zone.forgithubproject.service.RetrofitServiceInstance; 8 | 9 | import okhttp3.ResponseBody; 10 | import rx.Observable; 11 | import rx.Subscriber; 12 | 13 | /** 14 | * Created by zone on 2017/03/26 15 | */ 16 | 17 | public class AdModelImpl implements AdContract.Model { 18 | RetrofitService retrofitService; 19 | public AdModelImpl() { 20 | retrofitService = RetrofitServiceInstance.getInstance(); 21 | } 22 | public Observable getLoginCheck() {//假装服务器要展示广告 23 | return Observable.create(new Observable.OnSubscribe() { 24 | @Override 25 | public void call(Subscriber subscriber) { 26 | subscriber.onNext(new LoginCheckBean(true)); 27 | subscriber.onCompleted(); 28 | } 29 | }); 30 | } 31 | 32 | public Observable getAdMessage() { 33 | return Observable.create(new Observable.OnSubscribe() { 34 | @Override 35 | public void call(Subscriber subscriber) {//假装要展示 3 秒广告,且广告图为如下图 36 | subscriber.onNext(new AdMessageBean(3,"http://odjfpxwey.bkt.clouddn.com/2017-3-3-20-141110180-Screenshot_2017-02-23-23-10-26-062_com.tmall.wireless.png","http://www.baidu.com")); 37 | subscriber.onCompleted(); 38 | } 39 | }); 40 | } 41 | 42 | public Observable downLoadFile(String fileUrl) { 43 | return retrofitService.downLoadFile(fileUrl); 44 | } 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/model/LoginModelImpl.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.model; 2 | import com.zone.forgithubproject.contract.LoginContract; 3 | 4 | /** 5 | * Created by john on 2017/03/26 6 | */ 7 | 8 | public class LoginModelImpl implements LoginContract.Model{ 9 | 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/model/MainModelImpl.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.model; 2 | import com.zone.forgithubproject.contract.MainContract; 3 | 4 | /** 5 | * Created by john on 2017/03/26 6 | */ 7 | 8 | public class MainModelImpl implements MainContract.Model{ 9 | 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/model/bean/AdMessageBean.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.model.bean; 2 | 3 | /** 4 | * Created by john on 2016/12/30. 5 | */ 6 | public class AdMessageBean { 7 | int adTime;//播放广告的时常 8 | String adPictureUrl;//广告图片的地址 9 | String adUrl;//点击广告图片时,展示详情 webview 的地址 10 | 11 | public AdMessageBean(int adTime, String adPictureUrl, String adUrl) { 12 | this.adTime = adTime; 13 | this.adPictureUrl = adPictureUrl; 14 | this.adUrl = adUrl; 15 | } 16 | 17 | public int getAdTime() { 18 | return adTime; 19 | } 20 | 21 | public void setAdTime(int adTime) { 22 | this.adTime = adTime; 23 | } 24 | 25 | public String getAdPictureUrl() { 26 | return adPictureUrl; 27 | } 28 | 29 | public void setAdPictureUrl(String adPictureUrl) { 30 | this.adPictureUrl = adPictureUrl; 31 | } 32 | 33 | public String getAdUrl() { 34 | return adUrl; 35 | } 36 | 37 | public void setAdUrl(String adUrl) { 38 | this.adUrl = adUrl; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/model/bean/LoginCheckBean.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.model.bean; 2 | 3 | /** 4 | * Created by john on 2016/12/30. 5 | */ 6 | public class LoginCheckBean { 7 | private boolean isPlayAd = false;//默认赋值 false ,当没有网络时,也不会 NullPoint 8 | 9 | public LoginCheckBean(boolean isPlayAd) { 10 | this.isPlayAd = isPlayAd; 11 | } 12 | public LoginCheckBean() { 13 | } 14 | 15 | 16 | public boolean isPlayAd() { 17 | return isPlayAd; 18 | } 19 | 20 | public void setPlayAd(boolean playAd) { 21 | isPlayAd = playAd; 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/myApplication/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.myApplication; 2 | 3 | import android.app.Application; 4 | 5 | /** 6 | * Created by john on 2016/5/8. 7 | */ 8 | public class MyApplication extends Application { 9 | public static String getToken() { 10 | return token; 11 | } 12 | 13 | public static void setToken(String token) { 14 | MyApplication.token = token; 15 | } 16 | 17 | private static String token = ""; 18 | 19 | 20 | 21 | 22 | @Override 23 | public void onCreate() { 24 | super.onCreate(); 25 | 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/presenter/LoginPresenterImpl.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.presenter; 2 | import com.zone.forgithubproject.contract.LoginContract; 3 | 4 | /** 5 | * Created by john on 2017/03/26 6 | */ 7 | 8 | public class LoginPresenterImpl implements LoginContract.Presenter{ 9 | 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/presenter/MainPresenterImpl.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.presenter; 2 | import com.zone.forgithubproject.contract.MainContract; 3 | 4 | /** 5 | * Created by john on 2017/03/26 6 | */ 7 | 8 | public class MainPresenterImpl implements MainContract.Presenter{ 9 | 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/service/RetrofitService.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.service; 2 | 3 | import okhttp3.ResponseBody; 4 | import retrofit2.http.GET; 5 | import retrofit2.http.POST; 6 | import retrofit2.http.Url; 7 | import rx.Observable; 8 | 9 | /** 10 | * Created by john on 2016/9/7. 11 | */ 12 | public interface RetrofitService { 13 | @GET 14 | Observable downLoadFile(@Url String fileUrl); 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/service/RetrofitServiceInstance.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.service; 2 | 3 | 4 | import android.content.Context; 5 | import android.text.TextUtils; 6 | import com.zone.forgithubproject.myApplication.MyApplication; 7 | import com.zone.forgithubproject.utils.L; 8 | import java.io.IOException; 9 | import java.util.concurrent.TimeUnit; 10 | import okhttp3.Authenticator; 11 | import okhttp3.Interceptor; 12 | import okhttp3.OkHttpClient; 13 | import okhttp3.Request; 14 | import okhttp3.Response; 15 | import okhttp3.Route; 16 | import okhttp3.logging.HttpLoggingInterceptor; 17 | import retrofit2.Retrofit; 18 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 19 | import retrofit2.converter.gson.GsonConverterFactory; 20 | 21 | 22 | /** 23 | * Created by john on 2016/9/10. 24 | */ 25 | public class RetrofitServiceInstance { 26 | private static RetrofitService instance = null; 27 | public static RetrofitService getInstance() { 28 | 29 | Authenticator authenticator = new Authenticator() {//当服务器返回的状态码为401时,会自动执行里面的代码,也就实现了自动刷新token 30 | @Override 31 | public Request authenticate(Route route, Response response) throws IOException { 32 | L.d("==========> 重新刷新了token"); 33 | return response.request().newBuilder() 34 | .addHeader("token","") 35 | .build(); 36 | } 37 | }; 38 | Interceptor tokenInterceptor = new Interceptor() {//全局拦截器,往请求头部添加token字段,就实现了全局token 39 | @Override 40 | public Response intercept(Chain chain) throws IOException { 41 | Request originalRequest = chain.request(); 42 | Request tokenRequest = null; 43 | MyApplication application = new MyApplication(); 44 | if (TextUtils.isEmpty(MyApplication.getToken())) { 45 | L.d("MyApplication 的token值为空!!!!!!!!!!!!!"); 46 | return chain.proceed(originalRequest); 47 | } 48 | tokenRequest = originalRequest.newBuilder() 49 | .header("token",MyApplication.getToken()) 50 | .build(); 51 | 52 | return chain.proceed(tokenRequest); 53 | } 54 | }; 55 | HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {//log拦截器,打印所有的log 56 | @Override 57 | public void log(String message) { 58 | L.d(message); 59 | } 60 | }); 61 | OkHttpClient client = new OkHttpClient.Builder() 62 | .connectTimeout(15, TimeUnit.SECONDS) 63 | .retryOnConnectionFailure(true) 64 | .addNetworkInterceptor(tokenInterceptor) 65 | .addInterceptor(loggingInterceptor) 66 | .authenticator(authenticator) 67 | .build(); 68 | if (instance == null) { 69 | synchronized (RetrofitService.class) { 70 | if (instance == null) { 71 | Retrofit retrofit = new Retrofit.Builder() //生成实例 72 | .baseUrl("http://115.159.212.89:3000/") //基础url,会拼接NetService中的参数 73 | .client(client) 74 | .addConverterFactory(GsonConverterFactory.create()) //使用Gson解析 75 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) //加入RxJava的适配器 76 | .build(); 77 | 78 | instance = retrofit.create(RetrofitService.class); 79 | } 80 | } 81 | } 82 | return instance; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/utils/L.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.utils; 2 | 3 | import android.util.Log; 4 | 5 | /** 6 | * Created by john on 2016/7/25. 7 | */ 8 | public class L { 9 | private L() 10 | { 11 | /* cannot be instantiated */ 12 | throw new UnsupportedOperationException("cannot be instantiated"); 13 | } 14 | 15 | public static boolean isDebug = true;// 是否需要打印bug,可以在application的onCreate函数里面初始化 16 | private static final String TAG = "zoneLog"; 17 | 18 | // 下面四个是默认tag的函数 19 | public static void i(String msg) 20 | { 21 | if (!isDebug) return; 22 | StackTraceElement targetStackTraceElement = getTargetStackTraceElement(); 23 | Log.i(TAG, "(" + targetStackTraceElement.getFileName() + ":" 24 | + targetStackTraceElement.getLineNumber() + ")"); 25 | Log.i(TAG, msg); 26 | } 27 | 28 | public static void d(String msg) 29 | { 30 | if (!isDebug) return; 31 | StackTraceElement targetStackTraceElement = getTargetStackTraceElement(); 32 | Log.d(TAG, "(" + targetStackTraceElement.getFileName() + ":" 33 | + targetStackTraceElement.getLineNumber() + ")"); 34 | Log.d(TAG, msg); 35 | } 36 | 37 | public static void e(String msg) 38 | { 39 | if (!isDebug) return; 40 | StackTraceElement targetStackTraceElement = getTargetStackTraceElement(); 41 | Log.e(TAG, "(" + targetStackTraceElement.getFileName() + ":" 42 | + targetStackTraceElement.getLineNumber() + ")"); 43 | Log.e(TAG, msg); 44 | } 45 | 46 | public static void v(String msg) 47 | { 48 | if (!isDebug) return; 49 | StackTraceElement targetStackTraceElement = getTargetStackTraceElement(); 50 | Log.v(TAG, "(" + targetStackTraceElement.getFileName() + ":" 51 | + targetStackTraceElement.getLineNumber() + ")"); 52 | Log.v(TAG, msg); 53 | } 54 | 55 | // 下面是传入自定义tag的函数 56 | public static void i(String tag, String msg) 57 | { 58 | if (!isDebug) return; 59 | StackTraceElement targetStackTraceElement = getTargetStackTraceElement(); 60 | Log.i(tag, "(" + targetStackTraceElement.getFileName() + ":" 61 | + targetStackTraceElement.getLineNumber() + ")"); 62 | Log.i(tag, msg); 63 | } 64 | 65 | public static void d(String tag, String msg) 66 | { 67 | if (!isDebug) return; 68 | StackTraceElement targetStackTraceElement = getTargetStackTraceElement(); 69 | Log.d(tag, "(" + targetStackTraceElement.getFileName() + ":" 70 | + targetStackTraceElement.getLineNumber() + ")"); 71 | Log.d(tag, msg); 72 | } 73 | 74 | public static void e(String tag, String msg) 75 | { 76 | if (!isDebug) return; 77 | StackTraceElement targetStackTraceElement = getTargetStackTraceElement(); 78 | Log.e(tag, "(" + targetStackTraceElement.getFileName() + ":" 79 | + targetStackTraceElement.getLineNumber() + ")"); 80 | Log.e(tag, msg); 81 | } 82 | 83 | public static void v(String tag, String msg) 84 | { 85 | if (!isDebug) return; 86 | StackTraceElement targetStackTraceElement = getTargetStackTraceElement(); 87 | Log.v(tag, "(" + targetStackTraceElement.getFileName() + ":" 88 | + targetStackTraceElement.getLineNumber() + ")"); 89 | Log.v(tag, msg); 90 | } 91 | 92 | private static StackTraceElement getTargetStackTraceElement() { 93 | // find the target invoked method 94 | StackTraceElement targetStackTrace = null; 95 | boolean shouldTrace = false; 96 | StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 97 | for (StackTraceElement stackTraceElement : stackTrace) { 98 | boolean isLogMethod = stackTraceElement.getClassName().equals(L.class.getName()); 99 | if (shouldTrace && !isLogMethod) { 100 | targetStackTrace = stackTraceElement; 101 | break; 102 | } 103 | shouldTrace = isLogMethod; 104 | } 105 | return targetStackTrace; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/utils/NetUtils.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.utils; 2 | 3 | /** 4 | * Created by john on 2016/7/25. 5 | */ 6 | import android.app.Activity; 7 | import android.content.ComponentName; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.net.ConnectivityManager; 11 | import android.net.NetworkInfo; 12 | 13 | /** 14 | * 跟网络相关的工具类 15 | * 16 | * 17 | * 18 | */ 19 | public class NetUtils 20 | { 21 | private NetUtils() 22 | { 23 | /* cannot be instantiated */ 24 | throw new UnsupportedOperationException("cannot be instantiated"); 25 | } 26 | 27 | /** 28 | * 判断网络是否连接 29 | * 30 | * @param context 31 | * @return 32 | */ 33 | public static boolean isConnected(Context context) 34 | { 35 | if (context == null) { 36 | L.d("传进来的context实例为空!"); 37 | } 38 | ConnectivityManager connectivity = (ConnectivityManager) context 39 | .getSystemService(Context.CONNECTIVITY_SERVICE); 40 | 41 | if (null != connectivity) 42 | { 43 | 44 | NetworkInfo info = connectivity.getActiveNetworkInfo(); 45 | if (null != info && info.isConnected()) 46 | { 47 | if (info.getState() == NetworkInfo.State.CONNECTED) 48 | { 49 | return true; 50 | } 51 | } 52 | } 53 | return false; 54 | } 55 | 56 | /** 57 | * 判断是否是wifi连接 58 | */ 59 | public static boolean isWifi(Context context) 60 | { 61 | ConnectivityManager cm = (ConnectivityManager) context 62 | .getSystemService(Context.CONNECTIVITY_SERVICE); 63 | 64 | if (cm == null) 65 | return false; 66 | return cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI; 67 | 68 | } 69 | 70 | /** 71 | * 打开网络设置界面 72 | */ 73 | public static void openSetting(Activity activity) 74 | { 75 | Intent intent = new Intent("/"); 76 | ComponentName cm = new ComponentName("com.android.settings", 77 | "com.android.settings.WirelessSettings"); 78 | intent.setComponent(cm); 79 | intent.setAction("android.intent.action.VIEW"); 80 | activity.startActivityForResult(intent, 0); 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/utils/Rx/RxHelper.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.utils.Rx; 2 | 3 | import com.zone.forgithubproject.base.BaseBean; 4 | import com.zone.forgithubproject.utils.L; 5 | import rx.Observable; 6 | import rx.Subscriber; 7 | import rx.android.schedulers.AndroidSchedulers; 8 | import rx.functions.Func1; 9 | import rx.schedulers.Schedulers; 10 | 11 | /** 12 | * Created by john on 2016/9/8. 13 | */ 14 | public class RxHelper { 15 | 16 | public static Observable.Transformer, T> handleResult() { 17 | return new Observable.Transformer, T>() { 18 | @Override 19 | public Observable call(Observable> baseBeanObservable) { 20 | return baseBeanObservable.flatMap(new Func1, Observable>() { 21 | @Override 22 | public Observable call(BaseBean result) { 23 | if (result.success()) {//判断返回的code是否为200,如果是则取到结果往下传 24 | L.d("====>code==200"); 25 | L.d("====>message=="+result.message); 26 | return createData(result.data); 27 | // return Observable.concat().first(); 28 | } else { 29 | L.d("====code==>"+result.code); 30 | // return Observable.error(new Exception(result.message)); 31 | return createError(result.message); 32 | } 33 | } 34 | }).subscribeOn(Schedulers.io()) 35 | .observeOn(AndroidSchedulers.mainThread()); 36 | } 37 | }; 38 | } 39 | 40 | 41 | 42 | 43 | private static Observable createData(final T data) { 44 | return Observable.create(new Observable.OnSubscribe() { 45 | @Override 46 | public void call(Subscriber subscriber) { 47 | try { 48 | subscriber.onNext(data); 49 | subscriber.onCompleted(); 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | subscriber.onError(e); 53 | } 54 | } 55 | }); 56 | } 57 | private static Observable createError(final String data) { 58 | return Observable.create(new Observable.OnSubscribe() { 59 | @Override 60 | public void call(Subscriber subscriber) { 61 | try { 62 | subscriber.onError(new Exception(data)); 63 | subscriber.onCompleted(); 64 | } catch (Exception e) { 65 | e.printStackTrace(); 66 | subscriber.onError(e); 67 | } 68 | } 69 | }); 70 | } 71 | private static Observable createData2(final T data) { 72 | return Observable.create(new Observable.OnSubscribe() { 73 | @Override 74 | public void call(Subscriber subscriber) { 75 | try { 76 | subscriber.onNext(data); 77 | subscriber.onCompleted(); 78 | } catch (Exception e) { 79 | e.printStackTrace(); 80 | subscriber.onError(e); 81 | } 82 | } 83 | }); 84 | } 85 | } 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/utils/Rx/RxSubscribe.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.utils.Rx; 2 | 3 | import android.content.Context; 4 | import com.zone.forgithubproject.utils.L; 5 | import com.zone.forgithubproject.utils.NetUtils; 6 | import rx.Subscriber; 7 | 8 | /** 9 | * Created by john on 2016/9/8. 10 | */ 11 | public abstract class RxSubscribe extends Subscriber { 12 | private Context mContext; 13 | 14 | public RxSubscribe(Context context) { 15 | mContext = context; 16 | } 17 | public RxSubscribe() {} 18 | 19 | 20 | 21 | protected abstract void _onNext(T t); 22 | 23 | protected abstract void _onError(String message); 24 | 25 | 26 | 27 | @Override 28 | public void onNext(T t) { 29 | _onNext(t); 30 | } 31 | 32 | @Override 33 | public void onError(Throwable e) { 34 | e.printStackTrace(); 35 | 36 | try { 37 | if (!NetUtils.isConnected(mContext)) { 38 | L.d("RxSubscribe====>网络不可用!zone"); 39 | _onError("网络错误,请检查网络!"); 40 | } 41 | } catch (Exception e1) { 42 | e1.printStackTrace(); 43 | } 44 | _onError(e.getMessage()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/utils/SPUtils.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.utils; 2 | 3 | /** 4 | * Created by john on 2016/7/25. 5 | */ 6 | 7 | import android.content.Context; 8 | import android.content.SharedPreferences; 9 | 10 | import java.lang.reflect.InvocationTargetException; 11 | import java.lang.reflect.Method; 12 | import java.util.Map; 13 | 14 | public class SPUtils 15 | { 16 | /** 17 | * 保存在手机里面的文件名 18 | */ 19 | public static final String FILE_NAME = "share_data"; 20 | 21 | /** 22 | * 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法 23 | * 24 | * @param context 25 | * @param key 26 | * @param object 27 | */ 28 | public static void put(Context context, String key, Object object) 29 | { 30 | 31 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, 32 | Context.MODE_PRIVATE); 33 | SharedPreferences.Editor editor = sp.edit(); 34 | 35 | if (object instanceof String) 36 | { 37 | editor.putString(key, (String) object); 38 | } else if (object instanceof Integer) 39 | { 40 | editor.putInt(key, (Integer) object); 41 | } else if (object instanceof Boolean) 42 | { 43 | editor.putBoolean(key, (Boolean) object); 44 | } else if (object instanceof Float) 45 | { 46 | editor.putFloat(key, (Float) object); 47 | } else if (object instanceof Long) 48 | { 49 | editor.putLong(key, (Long) object); 50 | } else 51 | { 52 | editor.putString(key, object.toString()); 53 | } 54 | 55 | SharedPreferencesCompat.apply(editor); 56 | } 57 | 58 | /** 59 | * 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值 60 | * 61 | * @param context 62 | * @param key 63 | * @param defaultObject 64 | * @return 65 | */ 66 | public static Object get(Context context, String key, Object defaultObject) 67 | { 68 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, 69 | Context.MODE_PRIVATE); 70 | 71 | if (defaultObject instanceof String) 72 | { 73 | return sp.getString(key, (String) defaultObject); 74 | } else if (defaultObject instanceof Integer) 75 | { 76 | return sp.getInt(key, (Integer) defaultObject); 77 | } else if (defaultObject instanceof Boolean) 78 | { 79 | return sp.getBoolean(key, (Boolean) defaultObject); 80 | } else if (defaultObject instanceof Float) 81 | { 82 | return sp.getFloat(key, (Float) defaultObject); 83 | } else if (defaultObject instanceof Long) 84 | { 85 | return sp.getLong(key, (Long) defaultObject); 86 | } 87 | 88 | return null; 89 | } 90 | 91 | /** 92 | * 移除某个key值已经对应的值 93 | * @param context 94 | * @param key 95 | */ 96 | public static void remove(Context context, String key) 97 | { 98 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, 99 | Context.MODE_PRIVATE); 100 | SharedPreferences.Editor editor = sp.edit(); 101 | editor.remove(key); 102 | SharedPreferencesCompat.apply(editor); 103 | } 104 | 105 | /** 106 | * 清除所有数据 107 | * @param context 108 | */ 109 | public static void clear(Context context) 110 | { 111 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, 112 | Context.MODE_PRIVATE); 113 | SharedPreferences.Editor editor = sp.edit(); 114 | editor.clear(); 115 | SharedPreferencesCompat.apply(editor); 116 | } 117 | 118 | /** 119 | * 查询某个key是否已经存在 120 | * @param context 121 | * @param key 122 | * @return 123 | */ 124 | public static boolean contains(Context context, String key) 125 | { 126 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, 127 | Context.MODE_PRIVATE); 128 | return sp.contains(key); 129 | } 130 | 131 | /** 132 | * 返回所有的键值对 133 | * 134 | * @param context 135 | * @return 136 | */ 137 | public static Map getAll(Context context) 138 | { 139 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, 140 | Context.MODE_PRIVATE); 141 | return sp.getAll(); 142 | } 143 | 144 | /** 145 | * 创建一个解决SharedPreferencesCompat.apply方法的一个兼容类 146 | * 147 | * @author zhy 148 | * 149 | */ 150 | private static class SharedPreferencesCompat 151 | { 152 | private static final Method sApplyMethod = findApplyMethod(); 153 | 154 | /** 155 | * 反射查找apply的方法 156 | * 157 | * @return 158 | */ 159 | @SuppressWarnings({ "unchecked", "rawtypes" }) 160 | private static Method findApplyMethod() 161 | { 162 | try 163 | { 164 | Class clz = SharedPreferences.Editor.class; 165 | return clz.getMethod("apply"); 166 | } catch (NoSuchMethodException e) 167 | { 168 | } 169 | 170 | return null; 171 | } 172 | 173 | /** 174 | * 如果找到则使用apply执行,否则使用commit 175 | * 176 | * @param editor 177 | */ 178 | public static void apply(SharedPreferences.Editor editor) 179 | { 180 | try 181 | { 182 | if (sApplyMethod != null) 183 | { 184 | sApplyMethod.invoke(editor); 185 | return; 186 | } 187 | } catch (IllegalArgumentException e) 188 | { 189 | } catch (IllegalAccessException e) 190 | { 191 | } catch (InvocationTargetException e) 192 | { 193 | } 194 | editor.commit(); 195 | } 196 | } 197 | 198 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/utils/T.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.utils; 2 | 3 | /** 4 | * Created by john on 2016/7/25. 5 | */ 6 | import android.content.Context; 7 | import android.support.design.widget.Snackbar; 8 | import android.view.View; 9 | import android.widget.Toast; 10 | 11 | /** 12 | * Toast统一管理类 13 | * 14 | */ 15 | public class T 16 | { 17 | 18 | private T() 19 | { 20 | /* cannot be instantiated */ 21 | throw new UnsupportedOperationException("cannot be instantiated"); 22 | } 23 | 24 | public static boolean isShow = true; 25 | 26 | 27 | public static void showShortSnackBar(View view,String message) { 28 | Snackbar.make(view,message,Snackbar.LENGTH_SHORT).show(); 29 | } 30 | public static void showLongSnackBar(View view,String message) { 31 | Snackbar.make(view,message,Snackbar.LENGTH_LONG).show(); 32 | } 33 | 34 | /** 35 | * 短时间显示Toast 36 | * 37 | * @param context 38 | * @param message 39 | */ 40 | public static void showShort(Context context, CharSequence message) 41 | { 42 | if (isShow) 43 | Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); 44 | } 45 | 46 | /** 47 | * 短时间显示Toast 48 | * 49 | * @param context 50 | * @param message 51 | */ 52 | public static void showShort(Context context, int message) 53 | { 54 | if (isShow) 55 | Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); 56 | } 57 | 58 | /** 59 | * 长时间显示Toast 60 | * 61 | * @param context 62 | * @param message 63 | */ 64 | public static void showLong(Context context, CharSequence message) 65 | { 66 | if (isShow) 67 | Toast.makeText(context, message, Toast.LENGTH_LONG).show(); 68 | } 69 | 70 | /** 71 | * 长时间显示Toast 72 | * 73 | * @param context 74 | * @param message 75 | */ 76 | public static void showLong(Context context, int message) 77 | { 78 | if (isShow) 79 | Toast.makeText(context, message, Toast.LENGTH_LONG).show(); 80 | } 81 | 82 | /** 83 | * 自定义显示Toast时间 84 | * 85 | * @param context 86 | * @param message 87 | * @param duration 88 | */ 89 | public static void show(Context context, CharSequence message, int duration) 90 | { 91 | if (isShow) 92 | Toast.makeText(context, message, duration).show(); 93 | } 94 | 95 | /** 96 | * 自定义显示Toast时间 97 | * 98 | * @param context 99 | * @param message 100 | * @param duration 101 | */ 102 | public static void show(Context context, int message, int duration) 103 | { 104 | if (isShow) 105 | Toast.makeText(context, message, duration).show(); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/com/zone/forgithubproject/weidge/ProgressWebView.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject.weidge; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.util.AttributeSet; 6 | import android.webkit.GeolocationPermissions; 7 | import android.webkit.WebView; 8 | import android.widget.ProgressBar; 9 | 10 | import com.zone.forgithubproject.utils.L; 11 | 12 | /** 13 | * Created by john on 2016/11/3. 14 | */ 15 | @SuppressWarnings("deprecation") 16 | public class ProgressWebView extends WebView { 17 | 18 | private ProgressBar progressbar; 19 | 20 | public ProgressWebView(Context context, AttributeSet attrs) { 21 | super(context, attrs); 22 | progressbar = new ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal); 23 | progressbar.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, 12, 0, 0)); 24 | // progressbar.setBackgroundColor(getResources().getColor(R.color.colorBlackFont)); 25 | // progressbar.setDrawingCacheBackgroundColor(getResources().getColor(R.color.colorBlackFont)); 26 | addView(progressbar); 27 | // setWebViewClient(new WebViewClient(){}); 28 | setWebChromeClient(new WebChromeClient()); 29 | } 30 | 31 | public class WebChromeClient extends android.webkit.WebChromeClient { 32 | @Override 33 | public void onReceivedIcon(WebView view, Bitmap icon) { 34 | 35 | super.onReceivedIcon(view, icon); 36 | 37 | } 38 | 39 | @Override 40 | public void onGeolocationPermissionsShowPrompt(String origin,GeolocationPermissions.Callback callback) { 41 | callback.invoke(origin, true, false); 42 | super.onGeolocationPermissionsShowPrompt(origin, callback); 43 | } 44 | @Override 45 | public void onProgressChanged(WebView view, int newProgress) { 46 | if (newProgress == 100) { 47 | progressbar.setVisibility(GONE); 48 | } else { 49 | if (progressbar.getVisibility() == GONE) 50 | progressbar.setVisibility(VISIBLE); 51 | progressbar.setProgress(newProgress); 52 | L.d("====>"+String.valueOf(newProgress)); 53 | } 54 | super.onProgressChanged(view, newProgress); 55 | } 56 | } 57 | 58 | @Override 59 | protected void onScrollChanged(int l, int t, int oldl, int oldt) { 60 | LayoutParams lp = (LayoutParams) progressbar.getLayoutParams(); 61 | lp.x = l; 62 | lp.y = t; 63 | progressbar.setLayoutParams(lp); 64 | super.onScrollChanged(l, t, oldl, oldt); 65 | } 66 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/jyz_png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/app/src/main/res/drawable/jyz_png.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash_jyz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/app/src/main/res/drawable/splash_jyz.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash_skip.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_ad.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 25 | 26 | 37 | 38 | 47 | 48 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_webview.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/head.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #f7f7f7 5 | #12b6f4 6 | #000000 7 | 8 | #FF4081 9 | 10 | 11 | 12 | #ecf0f3 13 | #f5f5f9 14 | #f5f5f5 15 | #f3f6f8 16 | #f7f7f7 17 | #fcfcfc 18 | #d7d7d7 19 | #ff3e3e 20 | #353535 21 | #f5f5f9 22 | #f0f0f4 23 | 24 | #999999 25 | #333333 26 | #11aff1 27 | #ffffff 28 | #000000 29 | #47c11d 30 | #3cb2ff 31 | #12b7f5 32 | #9612b7f5 33 | #393a3f 34 | 35 | #dddddd 36 | #c3c3c3 37 | #00ff15 38 | 39 | 40 | 41 | #95EE6B 42 | #5FD3B3 43 | #FFA573 44 | #F66F89 45 | 46 | #FFCD73 47 | #FFE073 48 | #FFB473 49 | #FFD540 50 | 51 | #81c2d6 52 | #8192d6 53 | #d9b3e6 54 | #dcf7a1 55 | #7BD7FA 56 | #83fcec 57 | 58 | #bfbfbf 59 | #373b3e 60 | #323639 61 | #f8f8f8 62 | 63 | #a5acad 64 | #f4f9f9 65 | #72899c 66 | #385772 67 | 68 | #81cff9 69 | #adbeff 70 | #c3b5f6 71 | #d5aef6 72 | #e6a6f5 73 | #72899c 74 | #72899c 75 | #72899c 76 | #72899c 77 | #72899c 78 | #72899c 79 | #72899c 80 | 81 | 82 | 83 | #ffbe77 84 | #e4ebee 85 | #f4f9f9 86 | 87 | #eeeeee 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ForGitHubProject 3 | 4 | -------------------------------------------------------------------------------- /app/src/test/java/com/zone/forgithubproject/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.zone.forgithubproject; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | classpath 'com.antfortune.freeline:gradle:0.8.7' 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | jcenter() 19 | } 20 | } 21 | 22 | task clean(type: Delete) { 23 | delete rootProject.buildDir 24 | } 25 | -------------------------------------------------------------------------------- /freeline.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | from __future__ import print_function 4 | import sys 5 | from argparse import ArgumentParser 6 | 7 | from freeline_core.dispatcher import Dispatcher 8 | from freeline_core.init import init 9 | 10 | 11 | class Freeline(object): 12 | def __init__(self): 13 | self.dispatcher = Dispatcher() 14 | 15 | def call(self, args=None): 16 | if 'init' in args and args.init: 17 | print('init freeline project...') 18 | init() 19 | exit() 20 | 21 | self.dispatcher.call_command(args) 22 | 23 | 24 | def get_parser(): 25 | parser = ArgumentParser() 26 | parser.add_argument('-v', '--version', action='store_true', help='show version') 27 | parser.add_argument('-f', '--cleanBuild', action='store_true', help='force to execute a clean build') 28 | parser.add_argument('-w', '--wait', action='store_true', help='make application wait for debugger') 29 | parser.add_argument('-a', '--all', action='store_true', 30 | help="together with '-f', freeline will force to clean build all projects.") 31 | parser.add_argument('-c', '--clean', action='store_true', help='clean cache directory and workspace') 32 | parser.add_argument('-d', '--debug', action='store_true', help='show freeline debug output (NOT DEBUG APPLICATION)') 33 | # parser.add_argument('-i', '--init', action='store_true', help='init freeline project') 34 | parser.parse_args() 35 | return parser 36 | 37 | 38 | def main(): 39 | if sys.version_info > (3, 0): 40 | print('Freeline only support Python 2.7+ now. Please use the correct version of Python for freeline.') 41 | exit() 42 | 43 | parser = get_parser() 44 | args = parser.parse_args() 45 | freeline = Freeline() 46 | freeline.call(args=args) 47 | 48 | 49 | if __name__ == '__main__': 50 | main() 51 | -------------------------------------------------------------------------------- /freeline/freeline.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | from __future__ import print_function 4 | import sys 5 | from argparse import ArgumentParser 6 | 7 | from freeline_core.dispatcher import Dispatcher 8 | from freeline_core.init import init 9 | 10 | 11 | class Freeline(object): 12 | def __init__(self): 13 | self.dispatcher = Dispatcher() 14 | 15 | def call(self, args=None): 16 | if 'init' in args and args.init: 17 | print('init freeline project...') 18 | init() 19 | exit() 20 | 21 | self.dispatcher.call_command(args) 22 | 23 | 24 | def get_parser(): 25 | parser = ArgumentParser() 26 | parser.add_argument('-v', '--version', action='store_true', help='show version') 27 | parser.add_argument('-f', '--cleanBuild', action='store_true', help='force to execute a clean build') 28 | parser.add_argument('-w', '--wait', action='store_true', help='make application wait for debugger') 29 | parser.add_argument('-a', '--all', action='store_true', 30 | help="together with '-f', freeline will force to clean build all projects.") 31 | parser.add_argument('-c', '--clean', action='store_true', help='clean cache directory and workspace') 32 | parser.add_argument('-d', '--debug', action='store_true', help='show freeline debug output (NOT DEBUG APPLICATION)') 33 | # parser.add_argument('-i', '--init', action='store_true', help='init freeline project') 34 | parser.parse_args() 35 | return parser 36 | 37 | 38 | def main(): 39 | if sys.version_info > (3, 0): 40 | print('Freeline only support Python 2.7+ now. Please use the correct version of Python for freeline.') 41 | exit() 42 | 43 | parser = get_parser() 44 | args = parser.parse_args() 45 | freeline = Freeline() 46 | freeline.call(args=args) 47 | 48 | 49 | if __name__ == '__main__': 50 | main() 51 | -------------------------------------------------------------------------------- /freeline/freeline_core/.idea/dictionaries/huangyong.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /freeline/freeline_core/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | -------------------------------------------------------------------------------- /freeline/freeline_core/.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /freeline/freeline_core/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | -------------------------------------------------------------------------------- /freeline/freeline_core/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /freeline/freeline_core/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /freeline/freeline_core/__init__.py: -------------------------------------------------------------------------------- 1 | import build_commands 2 | import builder 3 | import command 4 | import dispatcher 5 | import exceptions 6 | import task 7 | import tracing 8 | import utils 9 | -------------------------------------------------------------------------------- /freeline/freeline_core/build_commands.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | from command import AbstractBuildCommand, MacroCommand 3 | 4 | 5 | class CleanBuildCommand(AbstractBuildCommand): 6 | def __init__(self, builder): 7 | AbstractBuildCommand.__init__(self, builder, command_name='clean_build') 8 | self._setup() 9 | 10 | def execute(self): 11 | map(lambda command: command.execute(), self.command_list) 12 | 13 | def _setup(self): 14 | self.add_command(CheckBulidEnvironmentCommand(self._builder)) 15 | self.add_command(FindDependenciesOfTasksCommand(self._builder)) 16 | self.add_command(GenerateSortedBuildTasksCommand(self._builder)) 17 | self.add_command(UpdateApkCreatedTimeCommand(self._builder)) 18 | self.add_command(ExecuteCleanBuildCommand(self._builder)) 19 | 20 | 21 | class IncrementalBuildCommand(AbstractBuildCommand): 22 | def __init__(self, builder): 23 | AbstractBuildCommand.__init__(self, builder, command_name='incremental_build') 24 | self._setup() 25 | 26 | def execute(self): 27 | map(lambda command: command.execute(), self.command_list) 28 | 29 | def _setup(self): 30 | self.add_command(CheckBulidEnvironmentCommand(self._builder)) 31 | self.add_command(GenerateSortedBuildTasksCommand(self._builder)) 32 | self.add_command(ExecuteIncrementalBuildCommand(self._builder)) 33 | 34 | 35 | class CheckBulidEnvironmentCommand(AbstractBuildCommand): 36 | def __init__(self, builder): 37 | AbstractBuildCommand.__init__(self, builder, command_name='check_build_environment') 38 | 39 | def execute(self): 40 | self._builder.check_build_environment() 41 | 42 | 43 | class FindDependenciesOfTasksCommand(AbstractBuildCommand): 44 | def __init__(self, builder): 45 | AbstractBuildCommand.__init__(self, builder, command_name='find_dependencies_of_tasks') 46 | 47 | def execute(self): 48 | self._builder.find_dependencies() 49 | 50 | 51 | class GenerateSortedBuildTasksCommand(AbstractBuildCommand): 52 | def __init__(self, builder): 53 | AbstractBuildCommand.__init__(self, builder, command_name='generate_build_tasks') 54 | 55 | def execute(self): 56 | self._builder.generate_sorted_build_tasks() 57 | 58 | 59 | class UpdateApkCreatedTimeCommand(AbstractBuildCommand): 60 | def __init__(self, builder): 61 | AbstractBuildCommand.__init__(self, builder, command_name='update_apk_created_time') 62 | 63 | def execute(self): 64 | self._builder.update_apk_created_time() 65 | 66 | 67 | class ExecuteCleanBuildCommand(AbstractBuildCommand): 68 | def __init__(self, builder): 69 | AbstractBuildCommand.__init__(self, builder, command_name='execute_clean_build') 70 | 71 | def execute(self): 72 | self._builder.clean_build() 73 | 74 | 75 | class ExecuteIncrementalBuildCommand(AbstractBuildCommand): 76 | def __init__(self, builder): 77 | AbstractBuildCommand.__init__(self, builder, command_name='execute_incremental_build') 78 | 79 | def execute(self): 80 | self._builder.incremental_build() 81 | 82 | 83 | class CompileCommand(MacroCommand): 84 | def __init__(self, name, invoker): 85 | MacroCommand.__init__(self, name) 86 | self._invoker = invoker 87 | self._setup() 88 | 89 | def _setup(self): 90 | self.add_command(IncAaptCommand(self.command_name, self._invoker)) 91 | self.add_command(IncJavacCommand(self.command_name, self._invoker)) 92 | self.add_command(IncDexCommand(self.command_name, self._invoker)) 93 | 94 | 95 | class IncAaptCommand(MacroCommand): 96 | def __init__(self, pro, invoker): 97 | MacroCommand.__init__(self, '{}_inc_res_compile'.format(pro)) 98 | self._invoker = invoker 99 | 100 | def execute(self): 101 | self._invoker.run_aapt_task() 102 | 103 | 104 | class IncJavacCommand(MacroCommand): 105 | def __init__(self, pro, invoker): 106 | MacroCommand.__init__(self, '{}_inc_javac_compile'.format(pro)) 107 | self._invoker = invoker 108 | 109 | def execute(self): 110 | self._invoker.run_javac_task() 111 | 112 | 113 | class IncDexCommand(MacroCommand): 114 | def __init__(self, pro, invoker): 115 | MacroCommand.__init__(self, '{}_inc_dex_compile'.format(pro)) 116 | self._invoker = invoker 117 | 118 | def execute(self): 119 | self._invoker.run_dex_task() 120 | 121 | 122 | class SyncCommand(MacroCommand): 123 | def __init__(self, sync_client, command_name): 124 | MacroCommand.__init__(self, command_name=command_name) 125 | self._sync_client = sync_client 126 | -------------------------------------------------------------------------------- /freeline/freeline_core/command.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | from __future__ import print_function 3 | from utils import generate_random_string 4 | from logger import Logger 5 | 6 | 7 | class AbstractCommand(object): 8 | TPL_COMMAND_DEBUG_MSG = '[{}] {}' 9 | 10 | def __init__(self, command_name=None): 11 | self.command_name = generate_random_string() if not command_name else command_name 12 | 13 | def execute(self): 14 | raise NotImplementedError 15 | 16 | def debug(self, message): 17 | Logger.debug(AbstractBuildCommand.TPL_COMMAND_DEBUG_MSG.format(self.command_name, message)) 18 | 19 | def __repr__(self): 20 | return self.command_name 21 | 22 | __str__ = __repr__ 23 | 24 | 25 | class MacroCommand(AbstractCommand): 26 | def __init__(self, command_name=None): 27 | AbstractCommand.__init__(self, command_name=command_name) 28 | self.command_list = [] 29 | 30 | def add_command(self, command): 31 | if isinstance(command, AbstractCommand): 32 | self.command_list.append(command) 33 | 34 | def remove_command(self, command): 35 | self.command_list.remove(command) 36 | 37 | def execute(self): 38 | map(lambda command: command.execute(), self.command_list) 39 | 40 | 41 | class AbstractBuildCommand(MacroCommand): 42 | def __init__(self, builder, command_name=None): 43 | MacroCommand.__init__(self, command_name=command_name) 44 | self._builder = builder 45 | 46 | 47 | class AbstractCleanBuildCommand(AbstractBuildCommand): 48 | def __init__(self, builder, command_name=None): 49 | AbstractBuildCommand.__init__(self, builder, command_name=command_name) 50 | 51 | def execute(self): 52 | raise NotImplementedError 53 | 54 | 55 | class AbstractIncrementalBuildCommand(AbstractBuildCommand): 56 | def __init__(self, builder, command_name=None): 57 | AbstractBuildCommand.__init__(self, builder, command_name=command_name) 58 | 59 | def execute(self): 60 | raise NotImplementedError 61 | -------------------------------------------------------------------------------- /freeline/freeline_core/cursor.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | 3 | from terminal import Terminal 4 | 5 | 6 | class Cursor(object): 7 | def __init__(self, term=None): 8 | self.term = Terminal() if term is None else term 9 | self._stream = self.term.stream 10 | self._saved = False 11 | 12 | def write(self, s): 13 | self._stream.write(s) 14 | 15 | def save(self): 16 | self.write(self.term.save) 17 | self._saved = True 18 | 19 | def restore(self): 20 | if self._saved: 21 | self.write(self.term.restore) 22 | 23 | def flush(self): 24 | self._stream.flush() 25 | 26 | def newline(self): 27 | self.write(self.term.move_down) 28 | self.write(self.term.clear_bol) 29 | 30 | def clear_lines(self, num_lines=0): 31 | for i in range(num_lines): 32 | self.write(self.term.clear_eol) 33 | self.write(self.term.move_down) 34 | for i in range(num_lines): 35 | self.write(self.term.move_up) 36 | -------------------------------------------------------------------------------- /freeline/freeline_core/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | 3 | 4 | class NoConfigFoundException(Exception): 5 | def __init__(self, path): 6 | Exception.__init__(self, '{} not found, please execute gradlew checkBeforeCleanBuild first.'.format(path)) 7 | 8 | 9 | class EnvironmentException(Exception): 10 | def __init__(self, message): 11 | Exception.__init__(self, message) 12 | 13 | 14 | class FreelineException(Exception): 15 | def __init__(self, message, cause): 16 | Exception.__init__(self, message) 17 | self.cause = cause 18 | 19 | 20 | class NoDeviceFoundException(FreelineException): 21 | def __init__(self, message, cause): 22 | FreelineException.__init__(self, message, cause) 23 | 24 | 25 | class CheckSyncStateException(FreelineException): 26 | def __init__(self, message, cause): 27 | FreelineException.__init__(self, message, cause) 28 | 29 | 30 | class NoInstallationException(FreelineException): 31 | def __init__(self, message, cause): 32 | FreelineException.__init__(self, message, cause) 33 | 34 | 35 | class FileMissedException(FreelineException): 36 | def __init__(self, message, cause): 37 | FreelineException.__init__(self, message, cause) 38 | 39 | 40 | class UsbConnectionException(FreelineException): 41 | def __init__(self, message, cause): 42 | FreelineException.__init__(self, message, cause) 43 | -------------------------------------------------------------------------------- /freeline/freeline_core/freeline_build.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | from __future__ import print_function 3 | from command import AbstractCommand 4 | from exceptions import NoConfigFoundException 5 | from logger import Logger 6 | 7 | 8 | class FreelineBuildCommand(AbstractCommand): 9 | def __init__(self, config, task_engine=None): 10 | AbstractCommand.__init__(self, command_name='freeline_build_command') 11 | self._config = config 12 | self._task_engine = task_engine 13 | self._project_type = None 14 | self._dispatch_policy = None 15 | self._builder = None 16 | self._scan_command = None 17 | self._build_command = None 18 | self._setup() 19 | 20 | def execute(self): 21 | file_changed_dict = self._scan_command.execute() 22 | 23 | if self._dispatch_policy.is_need_clean_build(self._config, file_changed_dict): 24 | self._setup_clean_builder(file_changed_dict) 25 | from build_commands import CleanBuildCommand 26 | self._build_command = CleanBuildCommand(self._builder) 27 | else: 28 | # only flush changed list when your project need a incremental build. 29 | Logger.debug('file changed list:') 30 | Logger.debug(file_changed_dict) 31 | self._setup_inc_builder(file_changed_dict) 32 | from build_commands import IncrementalBuildCommand 33 | self._build_command = IncrementalBuildCommand(self._builder) 34 | 35 | self._build_command.execute() 36 | 37 | def _setup(self): 38 | if not self._config: 39 | raise NoConfigFoundException 40 | 41 | if 'project_type' in self._config: 42 | self._project_type = self._config['project_type'] 43 | if self._project_type == 'gradle': 44 | from gradle_tools import GradleScanChangedFilesCommand, GradleDispatchPolicy 45 | self._scan_command = GradleScanChangedFilesCommand(self._config) 46 | self._dispatch_policy = GradleDispatchPolicy() 47 | 48 | def _setup_clean_builder(self, file_changed_dict): 49 | if self._project_type == 'gradle': 50 | from gradle_clean_build import GradleCleanBuilder 51 | project_info = self._scan_command.project_info 52 | self._builder = GradleCleanBuilder(self._config, self._task_engine, project_info=project_info) 53 | 54 | def _setup_inc_builder(self, file_changed_dict): 55 | if self._project_type == 'gradle': 56 | project_info = self._scan_command.project_info 57 | from gradle_inc_build import GradleIncBuilder 58 | self._builder = GradleIncBuilder(file_changed_dict, self._config, self._task_engine, 59 | project_info=project_info) 60 | 61 | 62 | class DispatchPolicy(object): 63 | """ 64 | file_changed_dict: 65 | 66 | 'projects': { 67 | bundle1: { 68 | 'js': [], 69 | 'assets': [], 70 | 'res': [], 71 | 'src': [], 72 | 'manifest': [], 73 | 'pom': [] 74 | }, 75 | bundle2: { 76 | 'js': [], 77 | 'assets': [], 78 | 'res': [], 79 | 'src': [], 80 | 'manifest': [], 81 | 'pom': [] 82 | }, 83 | ... 84 | }, 85 | 86 | 'build_info': { 87 | 'last_clean_build_time': int, 88 | 'root_pom_changed': bool 89 | } 90 | """ 91 | 92 | def is_need_clean_build(self, config, file_changed_dict): 93 | raise NotImplementedError 94 | 95 | 96 | class ScanChangedFilesCommand(AbstractCommand): 97 | def __init__(self): 98 | AbstractCommand.__init__(self, command_name='scan_changed_files_command') 99 | 100 | def execute(self): 101 | raise NotImplementedError 102 | -------------------------------------------------------------------------------- /freeline/freeline_core/gradle_clean_build.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | from __future__ import print_function 3 | 4 | import os 5 | 6 | from android_tools import InstallApkTask, CleanAllCacheTask 7 | from builder import CleanBuilder 8 | from gradle_tools import GenerateFileStatTask, BuildBaseResourceTask, get_project_info, GenerateAptFilesStatTask, \ 9 | get_gradle_executable 10 | from task import CleanBuildTask, Task 11 | from utils import cexec, load_json_cache, write_json_cache 12 | from logger import Logger 13 | 14 | 15 | class GradleCleanBuilder(CleanBuilder): 16 | def __init__(self, config, task_engine, project_info=None, wait_for_debugger=False): 17 | CleanBuilder.__init__(self, config, task_engine, builder_name='gradle_clean_builder') 18 | self._root_task = None 19 | self._project_info = project_info 20 | self._wait_for_debugger = wait_for_debugger 21 | 22 | def check_build_environment(self): 23 | CleanBuilder.check_build_environment(self) 24 | if self._project_info is None: 25 | project_info_cache_path = os.path.join(self._config['build_cache_dir'], 'project_info_cache.json') 26 | if os.path.exists(project_info_cache_path): 27 | self._project_info = load_json_cache(project_info_cache_path) 28 | else: 29 | self._project_info = get_project_info(self._config) 30 | 31 | def find_dependencies(self): 32 | pass 33 | 34 | def generate_sorted_build_tasks(self): 35 | # tasks' order: 36 | # 1. generate file stat / check before clean build 37 | # 2. clean build 38 | # 3. install / clean cache 39 | # 4. build base res / generate project info cache 40 | build_task = GradleCleanBuildTask(self._config) 41 | install_task = InstallApkTask(self._adb, self._config, wait_for_debugger=self._wait_for_debugger) 42 | clean_all_cache_task = CleanAllCacheTask(self._config['build_cache_dir'], ignore=[ 43 | 'stat_cache.json', 'stat_cache_md5.json', 'apktime', 'jar_dependencies.json', 'resources_dependencies.json', 44 | 'public_keeper.xml', 'assets_dependencies.json', 'freeline_annotation_info.json']) 45 | build_base_resource_task = BuildBaseResourceTask(self._config, self._project_info) 46 | generate_stat_task = GenerateFileStatTask(self._config) 47 | append_stat_task = GenerateFileStatTask(self._config, is_append=True) 48 | read_project_info_task = GradleReadProjectInfoTask(self._config) 49 | generate_project_info_task = GradleGenerateProjectInfoTask(self._config) 50 | generate_apt_file_stat_task = GenerateAptFilesStatTask() 51 | 52 | # generate_stat_task.add_child_task(read_project_info_task) 53 | build_task.add_child_task(clean_all_cache_task) 54 | build_task.add_child_task(install_task) 55 | clean_all_cache_task.add_child_task(build_base_resource_task) 56 | clean_all_cache_task.add_child_task(generate_project_info_task) 57 | clean_all_cache_task.add_child_task(append_stat_task) 58 | clean_all_cache_task.add_child_task(generate_apt_file_stat_task) 59 | read_project_info_task.add_child_task(build_task) 60 | self._root_task = [generate_stat_task, read_project_info_task] 61 | 62 | def clean_build(self): 63 | self._task_engine.add_root_task(self._root_task) 64 | self._task_engine.start() 65 | 66 | 67 | class GradleReadProjectInfoTask(Task): 68 | def __init__(self, config): 69 | Task.__init__(self, 'read_project_info_task') 70 | self._config = config 71 | 72 | def execute(self): 73 | command = '{} -q checkBeforeCleanBuild'.format(get_gradle_executable(self._config)) 74 | output, err, code = cexec(command.split(' '), callback=None) 75 | if code != 0: 76 | from exceptions import FreelineException 77 | raise FreelineException('freeline failed when read project info with script: {}'.format(command), 78 | '{}\n{}'.format(output, err)) 79 | 80 | 81 | class GradleGenerateProjectInfoTask(Task): 82 | def __init__(self, config): 83 | Task.__init__(self, 'generate_project_info_task') 84 | 85 | def execute(self): 86 | # reload project info 87 | from dispatcher import read_freeline_config 88 | config = read_freeline_config() 89 | 90 | write_json_cache(os.path.join(config['build_cache_dir'], 'project_info_cache.json'), 91 | get_project_info(config)) 92 | 93 | 94 | class GradleCleanBuildTask(CleanBuildTask): 95 | def __init__(self, config): 96 | CleanBuildTask.__init__(self, 'gradle_clean_build_task', config) 97 | 98 | def execute(self): 99 | # reload config 100 | from dispatcher import read_freeline_config 101 | self._config = read_freeline_config() 102 | 103 | cwd = self._config['build_script_work_directory'].strip() 104 | if not cwd or not os.path.isdir(cwd): 105 | cwd = None 106 | 107 | command = self._config['build_script'] 108 | command += ' -P freelineBuild=true' 109 | if 'auto_dependency' in self._config and not self._config['auto_dependency']: 110 | command += ' -PdisableAutoDependency=true' 111 | if Logger.debuggable: 112 | command += ' --stacktrace' 113 | self.debug(command) 114 | self.debug("Gradle build task is running, please wait a minute...") 115 | output, err, code = cexec(command.split(' '), callback=None, cwd=cwd) 116 | if code != 0: 117 | from exceptions import FreelineException 118 | raise FreelineException('build failed with script: {}'.format(command), '{}\n{}'.format(output, err)) 119 | -------------------------------------------------------------------------------- /freeline/freeline_core/hackwindows.py: -------------------------------------------------------------------------------- 1 | TIOCGWINSZ = 1074295912 2 | 3 | 4 | def fcntl(fd, op, arg=0): 5 | return 0 6 | 7 | 8 | def ioctl(fd, op, arg=0, mutable_flag=True): 9 | if mutable_flag: 10 | return 0 11 | else: 12 | return "" 13 | 14 | 15 | def flock(fd, op): 16 | return 17 | 18 | 19 | def lockf(fd, operation, length=0, start=0, whence=0): 20 | return 21 | -------------------------------------------------------------------------------- /freeline/freeline_core/init.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | from __future__ import print_function 3 | import os 4 | 5 | from exceptions import FreelineException 6 | from utils import is_windows_system, cexec, copy, get_file_content 7 | 8 | is_windows = is_windows_system() 9 | 10 | 11 | def init(): 12 | project_dir = os.getcwd() 13 | symlink('freeline', project_dir, 'freeline.py') 14 | 15 | if is_windows: 16 | symlink('freeline', project_dir, 'freeline_core') 17 | 18 | from gradle_tools import get_all_modules 19 | modules = get_all_modules(project_dir) 20 | for m in modules: 21 | if is_main_project(m['path']): 22 | main_module = m 23 | break 24 | 25 | if not main_module: 26 | raise FreelineException('main module not found', 'set main module first') 27 | 28 | print('find main module: ' + main_module['name']) 29 | args = [] 30 | if is_windows: 31 | args.append('gradlew.bat') 32 | else: 33 | args.append('./gradlew') 34 | args.append(':{}:checkBeforeCleanBuild'.format(main_module['name'])) 35 | print('freeline is reading project info, please wait a moment...') 36 | output, err, code = cexec(args, cwd=project_dir) 37 | if code != 0: 38 | raise FreelineException('freeline failed when read project info with script: {}'.format(args), 39 | '{}\n{}'.format(output, err)) 40 | print('freeline init success') 41 | 42 | 43 | def is_main_project(module): 44 | config_path = os.path.join(module, 'build.gradle') 45 | if os.path.exists(config_path): 46 | content = get_file_content(config_path) 47 | if "apply plugin: 'com.antfortune.freeline'" in content: 48 | return True 49 | return False 50 | 51 | 52 | def symlink(base_dir, target_dir, fn): 53 | base_path = os.path.join(base_dir, fn) 54 | target_path = os.path.join(target_dir, fn) 55 | 56 | if not os.path.exists(base_path): 57 | raise FreelineException('file missing: {}'.format(base_path), ' Maybe you should sync freeline repo') 58 | 59 | if os.path.exists(target_path): 60 | os.remove(target_path) 61 | 62 | if is_windows_system(): 63 | copy(base_path, target_path) 64 | else: 65 | os.symlink(base_path, target_path) 66 | -------------------------------------------------------------------------------- /freeline/freeline_core/logger.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | from __future__ import print_function 3 | 4 | import Queue 5 | import logging 6 | import os 7 | import threading 8 | import time 9 | import traceback 10 | 11 | from utils import print_json, is_windows_system 12 | 13 | FAILURE = -1 14 | READY = 0 15 | WAITING = 1 16 | WORKING = 2 17 | SUCCESS = 3 18 | 19 | 20 | class LoggerWorker(threading.Thread): 21 | def __init__(self, logger, stop_event): 22 | threading.Thread.__init__(self) 23 | self.setDaemon(True) 24 | 25 | if not isinstance(logger, Logger): 26 | raise Exception('LoggerWorker should be set up with freeline.logger.Logger') 27 | 28 | self._logger = logger 29 | self._stop_event = stop_event 30 | 31 | def run(self): 32 | while not self._stop_event.isSet(): 33 | if self._logger.debuggable: 34 | message = Logger.debug_messages_queue.get() 35 | Logger.print_debug_message(message) 36 | else: 37 | self._logger.update() 38 | time.sleep(self._logger.interval) 39 | 40 | # if in debug mode, clean the debug messages queue 41 | if self._logger.debuggable: 42 | Logger.flush_debug_messages() 43 | else: 44 | self._logger.update() 45 | 46 | 47 | class Logger(object): 48 | # TODO: check screen height before log messages 49 | debuggable = False 50 | debug_messages_queue = Queue.Queue() 51 | temp_backup_queue = Queue.Queue() 52 | info_message_array = [] 53 | TPL_DEBUG_MESSAGE = '[DEBUG] {}' 54 | 55 | def __init__(self, debuggable=False, interval=0.1, unit="s"): 56 | self.debuggable = debuggable 57 | self.interval = interval 58 | self.unit = unit 59 | 60 | if not is_windows_system(): 61 | from cursor import Cursor 62 | from terminal import Terminal 63 | self.cursor = Cursor(Terminal()) 64 | 65 | self.sorted_tasks = [] 66 | 67 | self.tpl_running_task = '[+][{}] {} in {}{}\n' 68 | self.tpl_waiting_task = '[+][{}] {}\n' 69 | self.tpl_finished_task = '[-][{}] {} in {}{}\n' 70 | # self.tpl_faied_task = '[-]{}:{} in {}{}\n' 71 | logging.basicConfig(level=logging.DEBUG) 72 | 73 | def set_sorted_tasks(self, sorted_tasks): 74 | self.sorted_tasks = sorted_tasks 75 | 76 | def draw(self): 77 | # if len(self.sorted_tasks) > 0: 78 | self.cursor.restore() 79 | self._draw() 80 | self.cursor.flush() 81 | 82 | def clear_space(self): 83 | self.cursor.restore() 84 | self.cursor.clear_lines(self._calculate_lines_num()) 85 | self.cursor.save() 86 | 87 | def update(self): 88 | self.clear_space() 89 | self.draw() 90 | 91 | def reset(self): 92 | if not self.debuggable: 93 | self.clear_space() 94 | self.sorted_tasks = [] 95 | Logger.info_message_array = [] 96 | 97 | @staticmethod 98 | def info(message): 99 | Logger.info_message_array.append(message) 100 | 101 | @staticmethod 102 | def debug(message): 103 | Logger.debug_messages_queue.put(message) 104 | Logger.temp_backup_queue.put(message) 105 | 106 | @staticmethod 107 | def warn(message): 108 | pass 109 | 110 | @staticmethod 111 | def error(message): 112 | pass 113 | 114 | @staticmethod 115 | def print_debug_message(message): 116 | if isinstance(message, dict) or isinstance(message, list): 117 | print_json(message) 118 | else: 119 | print(Logger.TPL_DEBUG_MESSAGE.format(message)) 120 | 121 | @staticmethod 122 | def flush_debug_messages(): 123 | while not Logger.debug_messages_queue.empty(): 124 | message = Logger.debug_messages_queue.get() 125 | Logger.print_debug_message(message) 126 | 127 | @staticmethod 128 | def write_error_log(exception=None, extra=None): 129 | import json 130 | try: 131 | log_path = get_error_log_path() 132 | with open(log_path, 'w') as fp: 133 | while not Logger.temp_backup_queue.empty(): 134 | message = Logger.temp_backup_queue.get(timeout=0.5) 135 | if isinstance(message, dict) or isinstance(message, list): 136 | fp.write(json.dumps(message, indent=4, separators=(',', ': '))) 137 | else: 138 | fp.write(message) 139 | fp.write('\n') 140 | 141 | # write extra info 142 | if exception: 143 | fp.write(exception) 144 | return log_path 145 | except Exception as e: 146 | print(traceback.format_exc()) 147 | print(e.message) 148 | return None 149 | 150 | def _calculate_lines_num(self): 151 | lines_count = 0 152 | for task in self.sorted_tasks: 153 | if task.can_show_log: 154 | lines_count += 1 155 | return lines_count + len(Logger.info_message_array) + 1 156 | 157 | def _draw(self): 158 | # map(lambda task: self.cursor.write(self._get_formatted_message(task)), self.sorted_tasks) 159 | map(lambda message: self.cursor.write(message + '\n'), Logger.info_message_array) 160 | map(lambda task: self.cursor.write(self._get_formatted_message(task)), 161 | filter(lambda task: task.can_show_log(), self.sorted_tasks)) 162 | 163 | def _get_formatted_message(self, task): 164 | return { 165 | FAILURE: self.tpl_finished_task.format(task.name, 'failed.', task.cost_time, self.unit), 166 | READY: self.tpl_running_task.format(task.name, 'not start.', 'N/A', self.unit), 167 | WAITING: self.tpl_waiting_task.format(task.name, 'waiting...'), 168 | WORKING: self.tpl_running_task.format(task.name, task.running_message, 169 | round(time.time() - task.run_start_time, 1), self.unit), 170 | SUCCESS: self.tpl_finished_task.format(task.name, task.finished_message, task.cost_time, self.unit) 171 | }.get(task.status, 'NULL') 172 | 173 | 174 | def get_error_log_path(): 175 | return os.path.join(get_error_log_dir(), time.strftime('%y-%m-%d %H-%M-%S') + '.log') 176 | 177 | 178 | def get_error_log_dir(): 179 | dir_path = os.path.join(os.path.expanduser('~'), '.freeline', 'logs') 180 | if not os.path.exists(dir_path): 181 | os.makedirs(dir_path) 182 | return dir_path 183 | -------------------------------------------------------------------------------- /freeline/freeline_core/task.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | from __future__ import print_function 3 | import threading 4 | import time 5 | import traceback 6 | 7 | from exceptions import FreelineException 8 | 9 | from logger import Logger, FAILURE, READY, WAITING, WORKING, SUCCESS 10 | 11 | 12 | class Task(object): 13 | def __init__(self, name): 14 | self.name = name 15 | self.parent_tasks = [] 16 | self.child_tasks = [] 17 | 18 | self.status = READY # -1: failed; 0: not start; 1: waiting; 2: working; 3: success; 19 | self.start_time = 0 20 | self.run_start_time = 0 21 | self.cost_time = 0 22 | self.running_message = 'running...' 23 | self.finished_message = 'finished.' 24 | self.condition = threading.Condition() 25 | self.interrupted_exception = None 26 | 27 | def __repr__(self): 28 | return "[{}]".format(self.name) 29 | 30 | def add_parent_task(self, task): 31 | if task not in self.parent_tasks: 32 | self.parent_tasks.append(task) 33 | task.add_child_task(self) 34 | 35 | def add_child_task(self, task): 36 | if task not in self.child_tasks: 37 | self.child_tasks.append(task) 38 | task.add_parent_task(self) 39 | 40 | def is_all_parent_finished(self): 41 | for task in self.parent_tasks: 42 | if task.status != SUCCESS and task.status != FAILURE: 43 | return False 44 | return True 45 | 46 | def wait(self): 47 | self.condition.acquire() 48 | self.condition.wait() 49 | self.condition.release() 50 | 51 | def notify(self): 52 | self.condition.acquire() 53 | self.condition.notify() 54 | self.condition.release() 55 | 56 | def execute(self): 57 | raise NotImplementedError 58 | 59 | def can_show_log(self): 60 | return self.status == SUCCESS or self.status == WORKING or self.status == FAILURE 61 | 62 | def debug(self, message): 63 | Logger.debug('[{}] {}'.format(self.name, message)) 64 | 65 | 66 | class CleanBuildTask(Task): 67 | def __init__(self, name, config): 68 | Task.__init__(self, name) 69 | self._config = config 70 | 71 | def execute(self): 72 | raise NotImplementedError 73 | 74 | 75 | class IncrementalBuildTask(Task): 76 | def __init__(self, name): 77 | Task.__init__(self, name) 78 | 79 | def execute(self): 80 | raise NotImplementedError 81 | 82 | 83 | class SyncTask(Task): 84 | def __init__(self, client, name): 85 | Task.__init__(self, name) 86 | self._client = client 87 | 88 | def execute(self): 89 | raise NotImplementedError 90 | 91 | 92 | class ExecutableTask(object): 93 | def __init__(self, task, engine): 94 | self.task = task 95 | self.engine = engine 96 | self._tpl_debug_message = '[{}] {}' 97 | 98 | def __repr__(self): 99 | return "".format(self.task.name) 100 | 101 | def debug(self, message): 102 | Logger.debug(self._tpl_debug_message.format(self.task.name, message)) 103 | 104 | def execute(self): 105 | # self.debug('{} start to execute...'.format(self.task.name)) 106 | self.task.start_time = time.time() 107 | self.task.status = WAITING 108 | while not self.task.is_all_parent_finished(): 109 | # self.debug('{} waiting...'.format(self.task.name)) 110 | self.task.wait() 111 | self.task.run_start_time = time.time() 112 | self.task.status = WORKING 113 | self.debug('{} start to run after waiting {}s'.format(self.task.name, 114 | round(self.task.run_start_time - self.task.start_time, 115 | 1))) 116 | # check if task need to interrupt before being executing 117 | if self.task.interrupted_exception is not None: 118 | self.task.status = FAILURE 119 | self._pass_interrupted_exception() 120 | return 121 | 122 | try: 123 | self.task.execute() 124 | self.task.status = SUCCESS 125 | except FreelineException as e: 126 | self.task.interrupted_exception = e 127 | self.task.status = FAILURE 128 | except: 129 | self.task.interrupted_exception = FreelineException('unexpected exception within task', 130 | traceback.format_exc()) 131 | self.task.status = FAILURE 132 | 133 | self.task.cost_time = round(time.time() - self.task.run_start_time, 1) 134 | self.debug('{} finish in {}s'.format(self.task.name, round(self.task.cost_time, 1))) 135 | 136 | # check if task need to interrupt after being executing 137 | if self.task.interrupted_exception is not None: 138 | self._pass_interrupted_exception() 139 | return 140 | 141 | for child_task in self.task.child_tasks: 142 | child_task.notify() 143 | 144 | self._check_engine_finished() 145 | 146 | def _pass_interrupted_exception(self): 147 | for child_task in self.engine.get_running_tasks(): 148 | child_task.interrupted_exception = self.task.interrupted_exception 149 | child_task.notify() 150 | 151 | if self.engine.is_all_tasks_finished(): 152 | self.engine.interrupt(self.task.interrupted_exception) 153 | self.engine.finish() 154 | 155 | def _check_engine_finished(self): 156 | if self.engine.is_all_tasks_finished(): 157 | self.engine.finish() 158 | 159 | 160 | def find_root_tasks(task_list): 161 | return filter(lambda task: len(task.parent_tasks) == 0, task_list) 162 | 163 | 164 | def find_last_tasks(task_list): 165 | return filter(lambda task: len(task.child_tasks) == 0, task_list) 166 | -------------------------------------------------------------------------------- /freeline/freeline_core/tracing.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from logger import Logger 4 | 5 | 6 | class Tracing(object): 7 | def __init__(self, description): 8 | self.__name = "tracing" 9 | self.__description = description 10 | self.__start_time = 0 11 | 12 | def __enter__(self): 13 | self.__start_time = time.time() 14 | 15 | def __exit__(self, exc_type, exc_val, exc_tb): 16 | if exc_tb: 17 | return False 18 | else: 19 | self.debug(time.time() - self.__start_time) 20 | return True 21 | 22 | def debug(self, execute_time): 23 | Logger.debug('[{}] {}: {}ms'.format(self.__name, self.__description, execute_time * 1000)) 24 | -------------------------------------------------------------------------------- /freeline/freeline_core/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | from __future__ import print_function 3 | import os 4 | import string 5 | import random 6 | import platform 7 | import re 8 | import hashlib 9 | import json 10 | import shutil 11 | from hashlib import md5 12 | from subprocess import Popen, PIPE 13 | 14 | import errno 15 | 16 | try: 17 | import xml.etree.cElementTree as ET 18 | except ImportError: 19 | import xml.etree.ElementTree as ET 20 | 21 | 22 | def cexec(args, callback=None, add_path=None, cwd=None): 23 | env = None 24 | if add_path: 25 | import copy 26 | env = copy.copy(os.environ) 27 | env['PATH'] = add_path + os.path.pathsep + env['PATH'] 28 | p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=env, cwd=cwd) 29 | output, err = p.communicate() 30 | code = p.returncode 31 | 32 | if code != 0 and callback: 33 | callback(args, code, output, err) 34 | else: 35 | return output, err, code 36 | 37 | 38 | def curl(url, body=None): 39 | code = 0 40 | err = None 41 | result = None 42 | try: 43 | import urllib2 44 | result = urllib2.urlopen(url, data=body).read().decode('utf-8').strip() 45 | except Exception as e: 46 | code = -1 47 | err = e 48 | return result, err, code 49 | 50 | 51 | def generate_random_string(length=6): 52 | return ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(length)) 53 | 54 | 55 | def is_windows_system(): 56 | return 'Windows' in platform.system() 57 | 58 | 59 | def is_linux_system(): 60 | return 'Linux' in platform.system() 61 | 62 | 63 | def copy(src, dst): 64 | try: 65 | shutil.copytree(src, dst) 66 | except OSError as e: 67 | if e.errno == errno.ENOTDIR: 68 | shutil.copy(src, dst) 69 | else: 70 | print('Directory not copied. Error: {}'.format(e.message)) 71 | 72 | 73 | def get_md5(fpath): 74 | m = md5() 75 | target_file = open(fpath, 'rb') 76 | m.update(target_file.read()) 77 | target_file.close() 78 | return m.hexdigest() 79 | 80 | 81 | def md5string(param): 82 | m = hashlib.md5() 83 | m.update(param.encode('utf-8')) 84 | return m.hexdigest() 85 | 86 | 87 | base = [str(x) for x in range(10)] + [chr(x) for x in range(ord('A'), ord('A') + 6)] 88 | 89 | 90 | def dec2hex(string_num): 91 | num = int(string_num) 92 | mid = [] 93 | while True: 94 | if num == 0: 95 | break 96 | num, rem = divmod(num, 16) 97 | mid.append(base[rem]) 98 | return ''.join([str(t) for t in mid[::-1]]) 99 | 100 | 101 | def is_exe(path): 102 | return os.path.isfile(path) and os.access(path, os.X_OK) 103 | 104 | 105 | def print_json(json_obj): 106 | print(json.dumps(json_obj, indent=4, separators=(',', ': '))) 107 | 108 | 109 | def get_file_content(path): 110 | if not path or not os.path.isfile(path): 111 | return '' 112 | import codecs 113 | with codecs.open(path, encoding='utf-8') as f: 114 | return f.read() 115 | 116 | 117 | def write_file_content(target_path, content): 118 | with open(target_path, 'w') as fp: 119 | if isinstance(content, int): 120 | content = unicode(content) 121 | fp.write(content.encode('utf-8')) 122 | 123 | 124 | def load_json_cache(fpath): 125 | cache = {} 126 | if not os.path.exists(fpath): 127 | pass 128 | if os.path.isfile(fpath): 129 | try: 130 | with open(fpath, 'r') as fp: 131 | cache = json.load(fp) 132 | except Exception: 133 | pass 134 | return cache 135 | 136 | 137 | def write_json_cache(fpath, cache): 138 | try: 139 | with open(fpath, 'w') as fp: 140 | json.dump(cache, fp) 141 | except Exception: 142 | pass 143 | 144 | 145 | def calculate_typed_file_count(dir_path, ext): 146 | i = 0 147 | for dirpath, dirnames, files in os.walk(dir_path): 148 | for fn in files: 149 | if fn.endswith(ext): 150 | i += 1 151 | return i 152 | 153 | 154 | def remove_namespace(path): 155 | content = get_file_content(path) 156 | xmlstring = re.sub('xmlns="[^"]+"', '', content, count=1) 157 | return xmlstring.encode('utf-8') # to avoid UnicodeEncodeError 158 | 159 | 160 | def merge_xml(filenames): 161 | return ET.tostring(XMLCombiner(filenames).combine().getroot()) 162 | 163 | 164 | def get_text_by_tag(root, tag): 165 | element = root.find(tag) 166 | return element.text if element is not None else '' 167 | 168 | 169 | class HashableDict(dict): 170 | def __hash__(self): 171 | return hash(tuple(sorted(self.items()))) 172 | 173 | 174 | class XMLCombiner(object): 175 | def __init__(self, filenames): 176 | assert len(filenames) > 0, 'No filenames!' 177 | self.roots = [ET.parse(f).getroot() for f in filenames] 178 | 179 | def combine(self): 180 | for r in self.roots[1:]: 181 | # combine each element with the first one, and update that 182 | self.combine_element(self.roots[0], r) 183 | # return the string representation 184 | return ET.ElementTree(self.roots[0]) 185 | 186 | def combine_element(self, one, other): 187 | mapping = {(el.tag, HashableDict(el.attrib)): el for el in one} 188 | for el in other: 189 | if len(el) == 0: 190 | try: 191 | # Update the text 192 | mapping[(el.tag, HashableDict(el.attrib))].text = el.text 193 | except KeyError: 194 | # An element with this name is not in the mapping 195 | mapping[(el.tag, HashableDict(el.attrib))] = el 196 | # Add it 197 | one.append(el) 198 | else: 199 | try: 200 | # Recursively process the element, and update it in the same way 201 | self.combine_element(mapping[(el.tag, HashableDict(el.attrib))], el) 202 | except KeyError: 203 | mapping[(el.tag, HashableDict(el.attrib))] = el 204 | one.append(el) 205 | -------------------------------------------------------------------------------- /freeline/freeline_core/version.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | import subprocess 3 | import os 4 | 5 | VERSION_FORMATTER = '{}({})' 6 | FREELINE_VERSION = 'v0.8.7' 7 | 8 | 9 | def get_freeline_version(): 10 | if is_git_dir(): 11 | return VERSION_FORMATTER.format(FREELINE_VERSION, get_git_short_version()) 12 | else: 13 | return FREELINE_VERSION 14 | 15 | 16 | def get_git_short_version(): 17 | # note: get git version 18 | # http://stackoverflow.com/questions/14989858/get-the-current-git-hash-in-a-python-script 19 | return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']) 20 | 21 | 22 | def is_git_dir(): 23 | project_root_path = os.path.abspath(os.path.join(os.path.realpath(__file__), os.pardir)) 24 | git_dir_path = os.path.join(project_root_path, '.git') 25 | if os.path.exists(git_dir_path) and os.path.isdir(git_dir_path): 26 | return True 27 | return False 28 | -------------------------------------------------------------------------------- /freeline/release-tools/DexMerge.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/freeline/release-tools/DexMerge.jar -------------------------------------------------------------------------------- /freeline/release-tools/FreelineAapt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/freeline/release-tools/FreelineAapt -------------------------------------------------------------------------------- /freeline/release-tools/FreelineAapt.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/freeline/release-tools/FreelineAapt.exe -------------------------------------------------------------------------------- /freeline/release-tools/FreelineAapt_: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/freeline/release-tools/FreelineAapt_ -------------------------------------------------------------------------------- /freeline/release-tools/databinding-cli.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/freeline/release-tools/databinding-cli.jar -------------------------------------------------------------------------------- /freeline/release-tools/databinding-cli8.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/freeline/release-tools/databinding-cli8.jar -------------------------------------------------------------------------------- /freeline/release-tools/dexmerger: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (C) 2012 The Android Open Source Project 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # Set up prog to be the path of this script, including following symlinks, 18 | # and set up progdir to be the fully-qualified pathname of its directory. 19 | prog="$0" 20 | while [ -h "${prog}" ]; do 21 | newProg=`/bin/ls -ld "${prog}"` 22 | newProg=`expr "${newProg}" : ".* -> \(.*\)$"` 23 | if expr "x${newProg}" : 'x/' >/dev/null; then 24 | prog="${newProg}" 25 | else 26 | progdir=`dirname "${prog}"` 27 | prog="${progdir}/${newProg}" 28 | fi 29 | done 30 | oldwd=`pwd` 31 | progdir=`dirname "${prog}"` 32 | cd "${progdir}" 33 | progdir=`pwd` 34 | prog="${progdir}"/`basename "${prog}"` 35 | cd "${oldwd}" 36 | 37 | jarfile=dx.jar 38 | libdir="$progdir" 39 | 40 | if [ ! -r "$libdir/$jarfile" ]; then 41 | # set dx.jar location for the SDK case 42 | libdir=`dirname "$progdir"`/platform-tools/lib 43 | fi 44 | 45 | 46 | if [ ! -r "$libdir/$jarfile" ]; then 47 | # set dx.jar location for the Android tree case 48 | libdir=`dirname "$progdir"`/framework 49 | fi 50 | 51 | if [ ! -r "$libdir/$jarfile" ]; then 52 | echo `basename "$prog"`": can't find $jarfile" 53 | exit 1 54 | fi 55 | 56 | # By default, give dexmerger a max heap size of 1 gig. This can be overridden 57 | # by using a "-J" option (see below). 58 | defaultMx="-Xmx1024M" 59 | 60 | # The following will extract any initial parameters of the form 61 | # "-J" from the command line and pass them to the Java 62 | # invocation (instead of to dexmerger). This makes it possible for you to add 63 | # a command-line parameter such as "-JXmx256M" in your scripts, for 64 | # example. "java" (with no args) and "java -X" give a summary of 65 | # available options. 66 | 67 | javaOpts="" 68 | 69 | while expr "x$1" : 'x-J' >/dev/null; do 70 | opt=`expr "x$1" : 'x-J\(.*\)'` 71 | javaOpts="${javaOpts} -${opt}" 72 | if expr "x${opt}" : "xXmx[0-9]" >/dev/null; then 73 | defaultMx="no" 74 | fi 75 | shift 76 | done 77 | 78 | if [ "${defaultMx}" != "no" ]; then 79 | javaOpts="${javaOpts} ${defaultMx}" 80 | fi 81 | 82 | if [ "$OSTYPE" = "cygwin" ]; then 83 | # For Cygwin, convert the jarfile path into native Windows style. 84 | jarpath=`cygpath -w "$libdir/$jarfile"` 85 | else 86 | jarpath="$libdir/$jarfile" 87 | fi 88 | 89 | exec java $javaOpts -cp "$jarpath" com.android.dx.merge.DexMerger "$@" 90 | -------------------------------------------------------------------------------- /freeline/release-tools/dx: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (C) 2007 The Android Open Source Project 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # Set up prog to be the path of this script, including following symlinks, 18 | # and set up progdir to be the fully-qualified pathname of its directory. 19 | prog="$0" 20 | while [ -h "${prog}" ]; do 21 | newProg=`/bin/ls -ld "${prog}"` 22 | newProg=`expr "${newProg}" : ".* -> \(.*\)$"` 23 | if expr "x${newProg}" : 'x/' >/dev/null; then 24 | prog="${newProg}" 25 | else 26 | progdir=`dirname "${prog}"` 27 | prog="${progdir}/${newProg}" 28 | fi 29 | done 30 | oldwd=`pwd` 31 | progdir=`dirname "${prog}"` 32 | cd "${progdir}" 33 | progdir=`pwd` 34 | prog="${progdir}"/`basename "${prog}"` 35 | cd "${oldwd}" 36 | 37 | jarfile=dx.jar 38 | libdir="freeline/release-tools" 39 | 40 | if [ ! -r "$libdir/$jarfile" ]; then 41 | # set dx.jar location for the SDK case 42 | libdir="$libdir/lib" 43 | fi 44 | 45 | 46 | if [ ! -r "$libdir/$jarfile" ]; then 47 | # set dx.jar location for the Android tree case 48 | libdir=`dirname "$progdir"`/framework 49 | fi 50 | 51 | if [ ! -r "$libdir/$jarfile" ]; then 52 | echo `basename "$prog"`": can't find $jarfile" 53 | exit 1 54 | fi 55 | 56 | # By default, give dx a max heap size of 1 gig. This can be overridden 57 | # by using a "-J" option (see below). 58 | defaultMx="-Xmx1024M" 59 | 60 | # The following will extract any initial parameters of the form 61 | # "-J" from the command line and pass them to the Java 62 | # invocation (instead of to dx). This makes it possible for you to add 63 | # a command-line parameter such as "-JXmx256M" in your scripts, for 64 | # example. "java" (with no args) and "java -X" give a summary of 65 | # available options. 66 | 67 | javaOpts="" 68 | 69 | while expr "x$1" : 'x-J' >/dev/null; do 70 | opt=`expr "x$1" : 'x-J\(.*\)'` 71 | javaOpts="${javaOpts} -${opt}" 72 | if expr "x${opt}" : "xXmx[0-9]" >/dev/null; then 73 | defaultMx="no" 74 | fi 75 | shift 76 | done 77 | 78 | if [ "${defaultMx}" != "no" ]; then 79 | javaOpts="${javaOpts} ${defaultMx}" 80 | fi 81 | 82 | if [ "$OSTYPE" = "cygwin" ]; then 83 | # For Cygwin, convert the jarfile path into native Windows style. 84 | jarpath=`cygpath -w "$libdir/$jarfile"` 85 | else 86 | jarpath="$libdir/$jarfile" 87 | fi 88 | 89 | exec java $javaOpts -jar "$jarpath" "$@" 90 | -------------------------------------------------------------------------------- /freeline/release-tools/dx.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM Copyright (C) 2007 The Android Open Source Project 3 | REM 4 | REM Licensed under the Apache License, Version 2.0 (the "License"); 5 | REM you may not use this file except in compliance with the License. 6 | REM You may obtain a copy of the License at 7 | REM 8 | REM http://www.apache.org/licenses/LICENSE-2.0 9 | REM 10 | REM Unless required by applicable law or agreed to in writing, software 11 | REM distributed under the License is distributed on an "AS IS" BASIS, 12 | REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | REM See the License for the specific language governing permissions and 14 | REM limitations under the License. 15 | 16 | REM don't modify the caller's environment 17 | setlocal 18 | 19 | REM Locate dx.jar in the directory where dx.bat was found and start it. 20 | 21 | REM Set up prog to be the path of this script, including following symlinks, 22 | REM and set up progdir to be the fully-qualified pathname of its directory. 23 | set prog=%~f0 24 | 25 | rem Check we have a valid Java.exe in the path. 26 | set java_exe= 27 | if exist "%~dp0..\tools\lib\find_java.bat" call "%~dp0..\tools\lib\find_java.bat" 28 | if exist "%~dp0..\..\tools\lib\find_java.bat" call "%~dp0..\..\tools\lib\find_java.bat" 29 | if not defined java_exe goto :EOF 30 | 31 | set jarfile=dx.jar 32 | set "frameworkdir=%~dp0" 33 | rem frameworkdir must not end with a dir sep. 34 | set "frameworkdir=%frameworkdir:~0,-1%" 35 | 36 | if exist "%frameworkdir%\%jarfile%" goto JarFileOk 37 | set "frameworkdir=%~dp0lib" 38 | 39 | if exist "%frameworkdir%\%jarfile%" goto JarFileOk 40 | set "frameworkdir=%~dp0..\framework" 41 | 42 | :JarFileOk 43 | 44 | set "jarpath=%frameworkdir%\%jarfile%" 45 | 46 | set javaOpts= 47 | set args= 48 | 49 | REM By default, give dx a max heap size of 1 gig and a stack size of 1meg. 50 | rem This can be overridden by using "-JXmx..." and "-JXss..." options below. 51 | set defaultXmx=-Xmx1024M 52 | set defaultXss=-Xss1m 53 | 54 | REM Capture all arguments that are not -J options. 55 | REM Note that when reading the input arguments with %1, the cmd.exe 56 | REM automagically converts --name=value arguments into 2 arguments "--name" 57 | REM followed by "value". Dx has been changed to know how to deal with that. 58 | set params= 59 | 60 | :firstArg 61 | if [%1]==[] goto endArgs 62 | set a=%~1 63 | 64 | if [%defaultXmx%]==[] goto notXmx 65 | if %a:~0,5% NEQ -JXmx goto notXmx 66 | set defaultXmx= 67 | :notXmx 68 | 69 | if [%defaultXss%]==[] goto notXss 70 | if %a:~0,5% NEQ -JXss goto notXss 71 | set defaultXss= 72 | :notXss 73 | 74 | if %a:~0,2% NEQ -J goto notJ 75 | set javaOpts=%javaOpts% -%a:~2% 76 | shift /1 77 | goto firstArg 78 | 79 | :notJ 80 | set params=%params% %1 81 | shift /1 82 | goto firstArg 83 | 84 | :endArgs 85 | 86 | set javaOpts=%javaOpts% %defaultXmx% %defaultXss% 87 | call "%java_exe%" %javaOpts% -Djava.ext.dirs="%frameworkdir%" -jar "%jarpath%" %params% 88 | 89 | -------------------------------------------------------------------------------- /freeline/release-tools/dx.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/freeline/release-tools/dx.jar -------------------------------------------------------------------------------- /freeline/release-tools/jasmin: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (C) 2007 The Android Open Source Project 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # Set up prog to be the path of this script, including following symlinks, 18 | # and set up progdir to be the fully-qualified pathname of its directory. 19 | prog="$0" 20 | while [ -h "${prog}" ]; do 21 | newProg=`/bin/ls -ld "${prog}"` 22 | newProg=`expr "${newProg}" : ".* -> \(.*\)$"` 23 | if expr "x${newProg}" : 'x/' >/dev/null; then 24 | prog="${newProg}" 25 | else 26 | progdir=`dirname "${prog}"` 27 | prog="${progdir}/${newProg}" 28 | fi 29 | done 30 | oldwd=`pwd` 31 | progdir=`dirname "${prog}"` 32 | cd "${progdir}" 33 | progdir=`pwd` 34 | prog="${progdir}"/`basename "${prog}"` 35 | cd "${oldwd}" 36 | 37 | libdir=`dirname $progdir`/etc 38 | 39 | exec java -jar $libdir/jasmin.jar "$@" 40 | -------------------------------------------------------------------------------- /freeline/release-tools/jasmin.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/freeline/release-tools/jasmin.jar -------------------------------------------------------------------------------- /freeline/release-tools/lib64/libc++.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/freeline/release-tools/lib64/libc++.so -------------------------------------------------------------------------------- /freeline/release-tools/mainDexClasses: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (C) 2013 The Android Open Source Project 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | function makeTempJar () 18 | { 19 | local tempDir=/tmp 20 | if [ ! -e "${tempDir}" ]; then 21 | tempDir=. 22 | fi 23 | local tempfile="${tempDir}/mainDexClasses-$$.tmp.jar" 24 | if [ -e "${tempfile}" ]; then 25 | echo "Failed to create temporary file" >2 26 | exit 6 27 | fi 28 | echo "${tempfile}" 29 | } 30 | 31 | function cleanTmp () 32 | { 33 | if [ -e "${tmpOut}" ] ; then 34 | rm "${tmpOut}" 35 | fi 36 | } 37 | 38 | 39 | # Set up prog to be the path of this script, including following symlinks, 40 | # and set up progdir to be the fully-qualified pathname of its directory. 41 | prog="$0" 42 | 43 | while [ -h "${prog}" ]; do 44 | newProg=`/bin/ls -ld "${prog}"` 45 | newProg=`expr "${newProg}" : ".* -> \(.*\)$"` 46 | if expr "x${newProg}" : 'x/' >/dev/null; then 47 | prog="${newProg}" 48 | else 49 | progdir=`dirname "${prog}"` 50 | prog="${progdir}/${newProg}" 51 | fi 52 | done 53 | oldwd=`pwd` 54 | progdir=`dirname "${prog}"` 55 | cd "${progdir}" 56 | progdir=`pwd` 57 | prog="${progdir}"/`basename "${prog}"` 58 | cd "${oldwd}" 59 | 60 | baserules="${progdir}"/mainDexClasses.rules 61 | if [ ! -r ${baserules} ]; then 62 | echo `basename "$prog"`": can't find mainDexClasses.rules" 1>&2 63 | exit 1 64 | fi 65 | 66 | jarfile=dx.jar 67 | libdir="$progdir" 68 | 69 | if [ ! -r "$libdir/$jarfile" ]; then 70 | # set dx.jar location for the SDK case 71 | libdir="$libdir/lib" 72 | fi 73 | 74 | 75 | if [ ! -r "$libdir/$jarfile" ]; then 76 | # set dx.jar location for the Android tree case 77 | libdir=`dirname "$progdir"`/framework 78 | fi 79 | 80 | if [ ! -r "$libdir/$jarfile" ]; then 81 | echo `basename "$prog"`": can't find $jarfile" 1>&2 82 | exit 1 83 | fi 84 | 85 | proguardExec="proguard.sh" 86 | proguard=${PROGUARD_HOME}/bin/${proguardExec} 87 | 88 | if [ ! -r "${proguard}" ]; then 89 | # set proguard location for the SDK case 90 | proguardBaseDir=`dirname "$progdir"` 91 | # "${progdir}"/../.. 92 | proguardBaseDir=`dirname "$proguardBaseDir"` 93 | proguard="${proguardBaseDir}"/tools/proguard/bin/${proguardExec} 94 | fi 95 | 96 | if [ ! -r "${proguard}" ]; then 97 | # set proguard location for the Android tree case 98 | proguardBaseDir=`dirname "$proguardBaseDir"` 99 | # "${progdir}"/../../../.. 100 | proguardBaseDir=`dirname "$proguardBaseDir"` 101 | proguard="${proguardBaseDir}"/external/proguard/bin/${proguardExec} 102 | fi 103 | 104 | if [ ! -r "${proguard}" ]; then 105 | proguard="`which proguard`" 106 | fi 107 | 108 | if [ -z "${proguard}" -o ! -r "${proguard}" ]; then 109 | proguard="`which ${proguardExec}`" 110 | fi 111 | 112 | if [ -z "${proguard}" -o ! -r "${proguard}" ]; then 113 | echo `basename "$prog"`": can't find ${proguardExec}" 1>&2 114 | exit 1 115 | fi 116 | 117 | shrinkedAndroidJar="${SHRINKED_ANDROID_JAR}" 118 | if [ -z "${shrinkedAndroidJar}" ]; then 119 | shrinkedAndroidJar=shrinkedAndroid.jar 120 | fi 121 | 122 | if [ ! -r "${shrinkedAndroidJar}" ]; then 123 | shrinkedAndroidJar=${libdir}/${shrinkedAndroidJar} 124 | fi 125 | 126 | if [ ! -r "${shrinkedAndroidJar}" ]; then 127 | echo `basename "$prog"`": can't find shrinkedAndroid.jar" 1>&2 128 | exit 1 129 | fi 130 | 131 | if [ "$OSTYPE" = "cygwin" ]; then 132 | # For Cygwin, convert the jarfile path into native Windows style. 133 | jarpath=`cygpath -w "$libdir/$jarfile"` 134 | proguard=`cygpath -w "${proguard}"` 135 | shrinkedAndroidJar=`cygpath -w "${shrinkedAndroidJar}"` 136 | else 137 | jarpath="$libdir/$jarfile" 138 | fi 139 | 140 | if expr "x$1" : 'x--output' >/dev/null; then 141 | exec 1>$2 142 | shift 2 143 | fi 144 | 145 | if [ $# -ne 1 ]; then 146 | echo "Usage : $0 [--output ] " 1>&2 147 | exit 2 148 | fi 149 | 150 | tmpOut=`makeTempJar` 151 | 152 | trap cleanTmp 0 153 | 154 | ${proguard} -injars ${@} -dontwarn -forceprocessing -outjars ${tmpOut} \ 155 | -libraryjars "${shrinkedAndroidJar}" -dontoptimize -dontobfuscate -dontpreverify \ 156 | -include "${baserules}" 1>/dev/null || exit 10 157 | 158 | java -cp "$jarpath" com.android.multidex.ClassReferenceListBuilder "${tmpOut}" ${@} || exit 11 159 | -------------------------------------------------------------------------------- /freeline/release-tools/mainDexClasses.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM Copyright (C) 2013 The Android Open Source Project 3 | REM 4 | REM Licensed under the Apache License, Version 2.0 (the "License"); 5 | REM you may not use this file except in compliance with the License. 6 | REM You may obtain a copy of the License at 7 | REM 8 | REM http://www.apache.org/licenses/LICENSE-2.0 9 | REM 10 | REM Unless required by applicable law or agreed to in writing, software 11 | REM distributed under the License is distributed on an "AS IS" BASIS, 12 | REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | REM See the License for the specific language governing permissions and 14 | REM limitations under the License. 15 | 16 | REM don't modify the caller's environment 17 | setlocal 18 | 19 | rem Check we have a valid Java.exe in the path. 20 | set java_exe= 21 | if exist "%~dp0..\tools\lib\find_java.bat" call "%~dp0..\tools\lib\find_java.bat" 22 | if exist "%~dp0..\..\tools\lib\find_java.bat" call "%~dp0..\..\tools\lib\find_java.bat" 23 | if not defined java_exe goto :EOF 24 | 25 | set baserules="%~dp0\mainDexClasses.rules" 26 | 27 | REM Locate dx.jar in the directory where dx.bat was found. 28 | set jarfile=dx.jar 29 | set "frameworkdir=%~dp0" 30 | rem frameworkdir must not end with a dir sep. 31 | set "frameworkdir=%frameworkdir:~0,-1%" 32 | if exist "%frameworkdir%\%jarfile%" goto JarFileOk 33 | set "frameworkdir=%~dp0lib" 34 | 35 | if exist "%frameworkdir%\%jarfile%" goto JarFileOk 36 | set "frameworkdir=%~dp0..\framework" 37 | :JarFileOk 38 | 39 | set "jarpath=%frameworkdir%\%jarfile%" 40 | 41 | set "shrinkedAndroidJar=%SHRINKED_ANDROID_JAR% 42 | if exist "%shrinkedAndroidJar%" goto shrinkedAndroidOk 43 | set "shrinkedAndroidJar=shrinkedAndroid.jar" 44 | 45 | if exist "%shrinkedAndroidJar%" goto shrinkedAndroidOk 46 | set "shrinkedAndroidJar=%frameworkdir%\%shrinkedAndroidJar%" 47 | 48 | :shrinkedAndroidOk 49 | set "proguardExec=proguard.bat" 50 | set "proguard=%PROGUARD_HOME%\bin\%proguardExec%" 51 | 52 | if exist "%proguard%" goto proguardOk 53 | REM set proguard location for the SDK case 54 | set "PROGUARD_HOME=%~dp0\..\..\tools\proguard" 55 | set "proguard=%PROGUARD_HOME%\bin\%proguardExec%" 56 | 57 | if exist "%proguard%" goto proguardOk 58 | REM set proguard location for the Android tree case 59 | set "PROGUARD_HOME=%~dp0\..\..\..\..\external\proguard" 60 | set "proguard=%PROGUARD_HOME%\bin\%proguardExec%" 61 | 62 | :proguardOk 63 | REM Capture all arguments. 64 | REM Note that when reading the input arguments with %1, the cmd.exe 65 | REM automagically converts --name=value arguments into 2 arguments "--name" 66 | REM followed by "value". Dx has been changed to know how to deal with that. 67 | set params= 68 | 69 | set output= 70 | 71 | :firstArg 72 | if [%1]==[] goto endArgs 73 | 74 | if %1 NEQ --output goto notOut 75 | set "output=%2" 76 | shift 77 | shift 78 | goto firstArg 79 | 80 | :notOut 81 | if defined params goto usage 82 | set params=%1 83 | shift 84 | goto firstArg 85 | 86 | :endArgs 87 | if defined params ( goto makeTmpJar ) else ( goto usage ) 88 | 89 | :makeTmpJar 90 | set "tmpJar=%TMP%\mainDexClasses-%RANDOM%.tmp.jar" 91 | if exist "%tmpJar%" goto makeTmpJar 92 | echo "" > "%tmpJar%" 93 | set "exitStatus=0" 94 | 95 | 96 | call "%proguard%" -injars %params% -dontwarn -forceprocessing -outjars "%tmpJar%" -libraryjars "%shrinkedAndroidJar%" -dontoptimize -dontobfuscate -dontpreverify -include "%baserules%" 1>nul 97 | 98 | if DEFINED output goto redirect 99 | call "%java_exe%" -Djava.ext.dirs="%frameworkdir%" com.android.multidex.ClassReferenceListBuilder "%tmpJar%" "%params%" 100 | goto afterClassReferenceListBuilder 101 | :redirect 102 | call "%java_exe%" -Djava.ext.dirs="%frameworkdir%" com.android.multidex.ClassReferenceListBuilder "%tmpJar%" "%params%" 1>"%output%" 103 | :afterClassReferenceListBuilder 104 | 105 | del %tmpJar% 106 | exit /b 107 | 108 | :usage 109 | echo "Usage : %0 [--output ] " 110 | exit /b 1 111 | -------------------------------------------------------------------------------- /freeline/release-tools/mainDexClasses.rules: -------------------------------------------------------------------------------- 1 | -keep public class * extends android.app.Instrumentation { 2 | (); 3 | } 4 | -keep public class * extends android.app.Application { 5 | (); 6 | } 7 | -keep public class * extends android.app.Activity { 8 | (); 9 | } 10 | -keep public class * extends android.app.Service { 11 | (); 12 | } 13 | -keep public class * extends android.content.ContentProvider { 14 | (); 15 | } 16 | -keep public class * extends android.content.BroadcastReceiver { 17 | (); 18 | } 19 | -keep public class * extends android.app.backup.BackupAgent { 20 | (); 21 | } 22 | -keep class android.support.multidex.** { 23 | *; 24 | } -------------------------------------------------------------------------------- /freeline/release-tools/manifest.txt: -------------------------------------------------------------------------------- 1 | Main-Class: com.android.dx.command.Main 2 | -------------------------------------------------------------------------------- /freeline_core/.idea/dictionaries/huangyong.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /freeline_core/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | -------------------------------------------------------------------------------- /freeline_core/.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /freeline_core/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | -------------------------------------------------------------------------------- /freeline_core/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /freeline_core/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /freeline_core/__init__.py: -------------------------------------------------------------------------------- 1 | import build_commands 2 | import builder 3 | import command 4 | import dispatcher 5 | import exceptions 6 | import task 7 | import tracing 8 | import utils 9 | -------------------------------------------------------------------------------- /freeline_core/build_commands.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | from command import AbstractBuildCommand, MacroCommand 3 | 4 | 5 | class CleanBuildCommand(AbstractBuildCommand): 6 | def __init__(self, builder): 7 | AbstractBuildCommand.__init__(self, builder, command_name='clean_build') 8 | self._setup() 9 | 10 | def execute(self): 11 | map(lambda command: command.execute(), self.command_list) 12 | 13 | def _setup(self): 14 | self.add_command(CheckBulidEnvironmentCommand(self._builder)) 15 | self.add_command(FindDependenciesOfTasksCommand(self._builder)) 16 | self.add_command(GenerateSortedBuildTasksCommand(self._builder)) 17 | self.add_command(UpdateApkCreatedTimeCommand(self._builder)) 18 | self.add_command(ExecuteCleanBuildCommand(self._builder)) 19 | 20 | 21 | class IncrementalBuildCommand(AbstractBuildCommand): 22 | def __init__(self, builder): 23 | AbstractBuildCommand.__init__(self, builder, command_name='incremental_build') 24 | self._setup() 25 | 26 | def execute(self): 27 | map(lambda command: command.execute(), self.command_list) 28 | 29 | def _setup(self): 30 | self.add_command(CheckBulidEnvironmentCommand(self._builder)) 31 | self.add_command(GenerateSortedBuildTasksCommand(self._builder)) 32 | self.add_command(ExecuteIncrementalBuildCommand(self._builder)) 33 | 34 | 35 | class CheckBulidEnvironmentCommand(AbstractBuildCommand): 36 | def __init__(self, builder): 37 | AbstractBuildCommand.__init__(self, builder, command_name='check_build_environment') 38 | 39 | def execute(self): 40 | self._builder.check_build_environment() 41 | 42 | 43 | class FindDependenciesOfTasksCommand(AbstractBuildCommand): 44 | def __init__(self, builder): 45 | AbstractBuildCommand.__init__(self, builder, command_name='find_dependencies_of_tasks') 46 | 47 | def execute(self): 48 | self._builder.find_dependencies() 49 | 50 | 51 | class GenerateSortedBuildTasksCommand(AbstractBuildCommand): 52 | def __init__(self, builder): 53 | AbstractBuildCommand.__init__(self, builder, command_name='generate_build_tasks') 54 | 55 | def execute(self): 56 | self._builder.generate_sorted_build_tasks() 57 | 58 | 59 | class UpdateApkCreatedTimeCommand(AbstractBuildCommand): 60 | def __init__(self, builder): 61 | AbstractBuildCommand.__init__(self, builder, command_name='update_apk_created_time') 62 | 63 | def execute(self): 64 | self._builder.update_apk_created_time() 65 | 66 | 67 | class ExecuteCleanBuildCommand(AbstractBuildCommand): 68 | def __init__(self, builder): 69 | AbstractBuildCommand.__init__(self, builder, command_name='execute_clean_build') 70 | 71 | def execute(self): 72 | self._builder.clean_build() 73 | 74 | 75 | class ExecuteIncrementalBuildCommand(AbstractBuildCommand): 76 | def __init__(self, builder): 77 | AbstractBuildCommand.__init__(self, builder, command_name='execute_incremental_build') 78 | 79 | def execute(self): 80 | self._builder.incremental_build() 81 | 82 | 83 | class CompileCommand(MacroCommand): 84 | def __init__(self, name, invoker): 85 | MacroCommand.__init__(self, name) 86 | self._invoker = invoker 87 | self._setup() 88 | 89 | def _setup(self): 90 | self.add_command(IncAaptCommand(self.command_name, self._invoker)) 91 | self.add_command(IncJavacCommand(self.command_name, self._invoker)) 92 | self.add_command(IncDexCommand(self.command_name, self._invoker)) 93 | 94 | 95 | class IncAaptCommand(MacroCommand): 96 | def __init__(self, pro, invoker): 97 | MacroCommand.__init__(self, '{}_inc_res_compile'.format(pro)) 98 | self._invoker = invoker 99 | 100 | def execute(self): 101 | self._invoker.run_aapt_task() 102 | 103 | 104 | class IncJavacCommand(MacroCommand): 105 | def __init__(self, pro, invoker): 106 | MacroCommand.__init__(self, '{}_inc_javac_compile'.format(pro)) 107 | self._invoker = invoker 108 | 109 | def execute(self): 110 | self._invoker.run_javac_task() 111 | 112 | 113 | class IncDexCommand(MacroCommand): 114 | def __init__(self, pro, invoker): 115 | MacroCommand.__init__(self, '{}_inc_dex_compile'.format(pro)) 116 | self._invoker = invoker 117 | 118 | def execute(self): 119 | self._invoker.run_dex_task() 120 | 121 | 122 | class SyncCommand(MacroCommand): 123 | def __init__(self, sync_client, command_name): 124 | MacroCommand.__init__(self, command_name=command_name) 125 | self._sync_client = sync_client 126 | -------------------------------------------------------------------------------- /freeline_core/builder.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | import os 3 | 4 | from logger import Logger 5 | from utils import generate_random_string, is_exe, remove_namespace, is_windows_system, is_linux_system 6 | 7 | try: 8 | import xml.etree.cElementTree as ET 9 | except ImportError: 10 | import xml.etree.ElementTree as ET 11 | 12 | 13 | class Builder(object): 14 | TPL_BUILDER_DEBUG_MSG = "[{}] {}" 15 | 16 | def __init__(self, config, task_engine, builder_name=None): 17 | self.builder_name = 'builder' + generate_random_string() if not builder_name else builder_name 18 | self._config = config 19 | self._task_engine = task_engine 20 | 21 | def debug(self, message): 22 | Logger.debug(Builder.TPL_BUILDER_DEBUG_MSG.format(self.builder_name, message)) 23 | 24 | @staticmethod 25 | def get_android_sdk_dir(config): 26 | if 'sdk_directory' in config and os.path.exists(config['sdk_directory']): 27 | return config['sdk_directory'] 28 | 29 | sdk_dir = os.getenv('ANDROID_HOME') 30 | if sdk_dir and os.path.isdir(sdk_dir): 31 | return sdk_dir 32 | 33 | sdk_dir = os.getenv('ANDROID_SDK') 34 | if sdk_dir and os.path.isdir(sdk_dir): 35 | return sdk_dir 36 | 37 | Logger.debug('[ERROR] config[sdk_directory]、ANDROID_HOME、ANDROID_SDK not found, ' 38 | 'Build.get_android_sdk_dir() return None.') 39 | return None 40 | 41 | @staticmethod 42 | def get_adb(config): 43 | sdk_dir = Builder.get_android_sdk_dir(config) 44 | adb_exe_name = os.name == 'nt' and 'adb.exe' or 'adb' 45 | if os.path.isdir(sdk_dir) and is_exe(os.path.join(sdk_dir, 'platform-tools', adb_exe_name)): 46 | return os.path.join(sdk_dir, 'platform-tools', adb_exe_name) 47 | Logger.debug('[ERROR] Builder.get_adb() return None.') 48 | return None 49 | 50 | @staticmethod 51 | def get_maven_home_dir(): 52 | path = os.getenv('M2_HOME') 53 | if not path: 54 | path = os.getenv('MAVEN_HOME') 55 | return path 56 | 57 | @staticmethod 58 | def get_mvn(): 59 | mvn_exe_name = os.name == 'nt' and 'mvn.bat' or 'mvn' 60 | path = Builder.get_maven_home_dir() 61 | if os.path.isdir(path) and os.path.isdir(os.path.join(path, 'bin')): 62 | mvn_exe_path = os.path.join(path, 'bin', mvn_exe_name) 63 | return mvn_exe_path if is_exe(mvn_exe_path) else None 64 | 65 | @staticmethod 66 | def get_maven_cache_dir(): 67 | path = Builder.get_maven_home_dir() 68 | if os.path.isdir(path): 69 | settings_path = os.path.join(path, 'conf', 'settings.xml') 70 | if os.path.exists(settings_path): 71 | tree = ET.ElementTree(ET.fromstring(remove_namespace(settings_path))) 72 | local_repo_node = tree.find('localRepository') 73 | if local_repo_node is not None: 74 | return local_repo_node.text 75 | return None 76 | 77 | @staticmethod 78 | def get_aapt(): 79 | aapt = os.path.join('freeline', 'release-tools', 'FreelineAapt') 80 | if is_windows_system(): 81 | aapt = os.path.join('freeline', 'release-tools', 'FreelineAapt.exe') 82 | if is_linux_system(): 83 | aapt = os.path.join('freeline', 'release-tools', 'FreelineAapt_') 84 | return aapt if os.path.exists(aapt) else None 85 | 86 | @staticmethod 87 | def get_javac(config=None): 88 | path = os.getenv('JAVA_HOME') 89 | if config is not None and 'java_home' in config: 90 | path = config['java_home'] 91 | exec_name = 'javac.exe' if is_windows_system() else 'javac' 92 | if path and is_exe(os.path.join(path, 'bin', exec_name)): 93 | return os.path.join(path, 'bin', exec_name) 94 | Logger.debug('[ERROR] Builder.get_javac() return None.') 95 | return None 96 | 97 | @staticmethod 98 | def get_java(config=None): 99 | path = os.getenv('JAVA_HOME') 100 | if config is not None and 'java_home' in config: 101 | path = config['java_home'] 102 | exec_name = 'java.exe' if is_windows_system() else 'java' 103 | if path and is_exe(os.path.join(path, 'bin', exec_name)): 104 | return os.path.join(path, 'bin', exec_name) 105 | Logger.debug('[ERROR] Builder.get_java() return None.') 106 | return None 107 | 108 | @staticmethod 109 | def get_dx(config): 110 | if is_windows_system(): 111 | if 'build_tools_directory' in config and os.path.exists(config['build_tools_directory']): 112 | path = os.path.join(config['build_tools_directory'], 'dx.bat') 113 | if is_exe(path): 114 | return path 115 | else: 116 | return os.path.join('freeline', 'release-tools', 'dx') 117 | 118 | @staticmethod 119 | def get_databinding_cli(config): 120 | dbcli = os.path.join('freeline', 'release-tools', 'databinding-cli.jar') 121 | if 'use_jdk8' in config: 122 | if config['use_jdk8']: 123 | dbcli = os.path.join('freeline', 'release-tools', 'databinding-cli8.jar') 124 | return dbcli 125 | 126 | 127 | class CleanBuilder(Builder): 128 | def __init__(self, config, task_engine, builder_name=None): 129 | Builder.__init__(self, config, task_engine, builder_name=builder_name) 130 | self._adb = None 131 | self._is_art = False 132 | 133 | def check_build_environment(self): 134 | from android_tools import get_device_sdk_version_by_adb 135 | self._adb = Builder.get_adb(self._config) 136 | self._is_art = get_device_sdk_version_by_adb(self._adb) >= 20 137 | 138 | def find_dependencies(self): 139 | raise NotImplementedError 140 | 141 | def generate_sorted_build_tasks(self): 142 | raise NotImplementedError 143 | 144 | def update_apk_created_time(self): 145 | from android_tools import get_apktime_path 146 | from sync_client import update_clean_build_created_flag 147 | update_clean_build_created_flag(get_apktime_path(self._config)) 148 | 149 | def clean_build(self): 150 | raise NotImplementedError 151 | 152 | 153 | class IncrementalBuilder(Builder): 154 | def __init__(self, changed_files, config, task_engine, builder_name=None): 155 | Builder.__init__(self, config, task_engine, builder_name=builder_name) 156 | self._changed_files = changed_files 157 | 158 | def check_build_environment(self): 159 | raise NotImplementedError 160 | -------------------------------------------------------------------------------- /freeline_core/command.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | from __future__ import print_function 3 | from utils import generate_random_string 4 | from logger import Logger 5 | 6 | 7 | class AbstractCommand(object): 8 | TPL_COMMAND_DEBUG_MSG = '[{}] {}' 9 | 10 | def __init__(self, command_name=None): 11 | self.command_name = generate_random_string() if not command_name else command_name 12 | 13 | def execute(self): 14 | raise NotImplementedError 15 | 16 | def debug(self, message): 17 | Logger.debug(AbstractBuildCommand.TPL_COMMAND_DEBUG_MSG.format(self.command_name, message)) 18 | 19 | def __repr__(self): 20 | return self.command_name 21 | 22 | __str__ = __repr__ 23 | 24 | 25 | class MacroCommand(AbstractCommand): 26 | def __init__(self, command_name=None): 27 | AbstractCommand.__init__(self, command_name=command_name) 28 | self.command_list = [] 29 | 30 | def add_command(self, command): 31 | if isinstance(command, AbstractCommand): 32 | self.command_list.append(command) 33 | 34 | def remove_command(self, command): 35 | self.command_list.remove(command) 36 | 37 | def execute(self): 38 | map(lambda command: command.execute(), self.command_list) 39 | 40 | 41 | class AbstractBuildCommand(MacroCommand): 42 | def __init__(self, builder, command_name=None): 43 | MacroCommand.__init__(self, command_name=command_name) 44 | self._builder = builder 45 | 46 | 47 | class AbstractCleanBuildCommand(AbstractBuildCommand): 48 | def __init__(self, builder, command_name=None): 49 | AbstractBuildCommand.__init__(self, builder, command_name=command_name) 50 | 51 | def execute(self): 52 | raise NotImplementedError 53 | 54 | 55 | class AbstractIncrementalBuildCommand(AbstractBuildCommand): 56 | def __init__(self, builder, command_name=None): 57 | AbstractBuildCommand.__init__(self, builder, command_name=command_name) 58 | 59 | def execute(self): 60 | raise NotImplementedError 61 | -------------------------------------------------------------------------------- /freeline_core/cursor.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | 3 | from terminal import Terminal 4 | 5 | 6 | class Cursor(object): 7 | def __init__(self, term=None): 8 | self.term = Terminal() if term is None else term 9 | self._stream = self.term.stream 10 | self._saved = False 11 | 12 | def write(self, s): 13 | self._stream.write(s) 14 | 15 | def save(self): 16 | self.write(self.term.save) 17 | self._saved = True 18 | 19 | def restore(self): 20 | if self._saved: 21 | self.write(self.term.restore) 22 | 23 | def flush(self): 24 | self._stream.flush() 25 | 26 | def newline(self): 27 | self.write(self.term.move_down) 28 | self.write(self.term.clear_bol) 29 | 30 | def clear_lines(self, num_lines=0): 31 | for i in range(num_lines): 32 | self.write(self.term.clear_eol) 33 | self.write(self.term.move_down) 34 | for i in range(num_lines): 35 | self.write(self.term.move_up) 36 | -------------------------------------------------------------------------------- /freeline_core/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | 3 | 4 | class NoConfigFoundException(Exception): 5 | def __init__(self, path): 6 | Exception.__init__(self, '{} not found, please execute gradlew checkBeforeCleanBuild first.'.format(path)) 7 | 8 | 9 | class EnvironmentException(Exception): 10 | def __init__(self, message): 11 | Exception.__init__(self, message) 12 | 13 | 14 | class FreelineException(Exception): 15 | def __init__(self, message, cause): 16 | Exception.__init__(self, message) 17 | self.cause = cause 18 | 19 | 20 | class NoDeviceFoundException(FreelineException): 21 | def __init__(self, message, cause): 22 | FreelineException.__init__(self, message, cause) 23 | 24 | 25 | class CheckSyncStateException(FreelineException): 26 | def __init__(self, message, cause): 27 | FreelineException.__init__(self, message, cause) 28 | 29 | 30 | class NoInstallationException(FreelineException): 31 | def __init__(self, message, cause): 32 | FreelineException.__init__(self, message, cause) 33 | 34 | 35 | class FileMissedException(FreelineException): 36 | def __init__(self, message, cause): 37 | FreelineException.__init__(self, message, cause) 38 | 39 | 40 | class UsbConnectionException(FreelineException): 41 | def __init__(self, message, cause): 42 | FreelineException.__init__(self, message, cause) 43 | -------------------------------------------------------------------------------- /freeline_core/freeline_build.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | from __future__ import print_function 3 | from command import AbstractCommand 4 | from exceptions import NoConfigFoundException 5 | from logger import Logger 6 | 7 | 8 | class FreelineBuildCommand(AbstractCommand): 9 | def __init__(self, config, task_engine=None): 10 | AbstractCommand.__init__(self, command_name='freeline_build_command') 11 | self._config = config 12 | self._task_engine = task_engine 13 | self._project_type = None 14 | self._dispatch_policy = None 15 | self._builder = None 16 | self._scan_command = None 17 | self._build_command = None 18 | self._setup() 19 | 20 | def execute(self): 21 | file_changed_dict = self._scan_command.execute() 22 | 23 | if self._dispatch_policy.is_need_clean_build(self._config, file_changed_dict): 24 | self._setup_clean_builder(file_changed_dict) 25 | from build_commands import CleanBuildCommand 26 | self._build_command = CleanBuildCommand(self._builder) 27 | else: 28 | # only flush changed list when your project need a incremental build. 29 | Logger.debug('file changed list:') 30 | Logger.debug(file_changed_dict) 31 | self._setup_inc_builder(file_changed_dict) 32 | from build_commands import IncrementalBuildCommand 33 | self._build_command = IncrementalBuildCommand(self._builder) 34 | 35 | self._build_command.execute() 36 | 37 | def _setup(self): 38 | if not self._config: 39 | raise NoConfigFoundException 40 | 41 | if 'project_type' in self._config: 42 | self._project_type = self._config['project_type'] 43 | if self._project_type == 'gradle': 44 | from gradle_tools import GradleScanChangedFilesCommand, GradleDispatchPolicy 45 | self._scan_command = GradleScanChangedFilesCommand(self._config) 46 | self._dispatch_policy = GradleDispatchPolicy() 47 | 48 | def _setup_clean_builder(self, file_changed_dict): 49 | if self._project_type == 'gradle': 50 | from gradle_clean_build import GradleCleanBuilder 51 | project_info = self._scan_command.project_info 52 | self._builder = GradleCleanBuilder(self._config, self._task_engine, project_info=project_info) 53 | 54 | def _setup_inc_builder(self, file_changed_dict): 55 | if self._project_type == 'gradle': 56 | project_info = self._scan_command.project_info 57 | from gradle_inc_build import GradleIncBuilder 58 | self._builder = GradleIncBuilder(file_changed_dict, self._config, self._task_engine, 59 | project_info=project_info) 60 | 61 | 62 | class DispatchPolicy(object): 63 | """ 64 | file_changed_dict: 65 | 66 | 'projects': { 67 | bundle1: { 68 | 'js': [], 69 | 'assets': [], 70 | 'res': [], 71 | 'src': [], 72 | 'manifest': [], 73 | 'pom': [] 74 | }, 75 | bundle2: { 76 | 'js': [], 77 | 'assets': [], 78 | 'res': [], 79 | 'src': [], 80 | 'manifest': [], 81 | 'pom': [] 82 | }, 83 | ... 84 | }, 85 | 86 | 'build_info': { 87 | 'last_clean_build_time': int, 88 | 'root_pom_changed': bool 89 | } 90 | """ 91 | 92 | def is_need_clean_build(self, config, file_changed_dict): 93 | raise NotImplementedError 94 | 95 | 96 | class ScanChangedFilesCommand(AbstractCommand): 97 | def __init__(self): 98 | AbstractCommand.__init__(self, command_name='scan_changed_files_command') 99 | 100 | def execute(self): 101 | raise NotImplementedError 102 | -------------------------------------------------------------------------------- /freeline_core/gradle_clean_build.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | from __future__ import print_function 3 | 4 | import os 5 | 6 | from android_tools import InstallApkTask, CleanAllCacheTask 7 | from builder import CleanBuilder 8 | from gradle_tools import GenerateFileStatTask, BuildBaseResourceTask, get_project_info, GenerateAptFilesStatTask, \ 9 | get_gradle_executable 10 | from task import CleanBuildTask, Task 11 | from utils import cexec, load_json_cache, write_json_cache 12 | from logger import Logger 13 | 14 | 15 | class GradleCleanBuilder(CleanBuilder): 16 | def __init__(self, config, task_engine, project_info=None, wait_for_debugger=False): 17 | CleanBuilder.__init__(self, config, task_engine, builder_name='gradle_clean_builder') 18 | self._root_task = None 19 | self._project_info = project_info 20 | self._wait_for_debugger = wait_for_debugger 21 | 22 | def check_build_environment(self): 23 | CleanBuilder.check_build_environment(self) 24 | if self._project_info is None: 25 | project_info_cache_path = os.path.join(self._config['build_cache_dir'], 'project_info_cache.json') 26 | if os.path.exists(project_info_cache_path): 27 | self._project_info = load_json_cache(project_info_cache_path) 28 | else: 29 | self._project_info = get_project_info(self._config) 30 | 31 | def find_dependencies(self): 32 | pass 33 | 34 | def generate_sorted_build_tasks(self): 35 | # tasks' order: 36 | # 1. generate file stat / check before clean build 37 | # 2. clean build 38 | # 3. install / clean cache 39 | # 4. build base res / generate project info cache 40 | build_task = GradleCleanBuildTask(self._config) 41 | install_task = InstallApkTask(self._adb, self._config, wait_for_debugger=self._wait_for_debugger) 42 | clean_all_cache_task = CleanAllCacheTask(self._config['build_cache_dir'], ignore=[ 43 | 'stat_cache.json', 'stat_cache_md5.json', 'apktime', 'jar_dependencies.json', 'resources_dependencies.json', 44 | 'public_keeper.xml', 'assets_dependencies.json', 'freeline_annotation_info.json']) 45 | build_base_resource_task = BuildBaseResourceTask(self._config, self._project_info) 46 | generate_stat_task = GenerateFileStatTask(self._config) 47 | append_stat_task = GenerateFileStatTask(self._config, is_append=True) 48 | read_project_info_task = GradleReadProjectInfoTask(self._config) 49 | generate_project_info_task = GradleGenerateProjectInfoTask(self._config) 50 | generate_apt_file_stat_task = GenerateAptFilesStatTask() 51 | 52 | # generate_stat_task.add_child_task(read_project_info_task) 53 | build_task.add_child_task(clean_all_cache_task) 54 | build_task.add_child_task(install_task) 55 | clean_all_cache_task.add_child_task(build_base_resource_task) 56 | clean_all_cache_task.add_child_task(generate_project_info_task) 57 | clean_all_cache_task.add_child_task(append_stat_task) 58 | clean_all_cache_task.add_child_task(generate_apt_file_stat_task) 59 | read_project_info_task.add_child_task(build_task) 60 | self._root_task = [generate_stat_task, read_project_info_task] 61 | 62 | def clean_build(self): 63 | self._task_engine.add_root_task(self._root_task) 64 | self._task_engine.start() 65 | 66 | 67 | class GradleReadProjectInfoTask(Task): 68 | def __init__(self, config): 69 | Task.__init__(self, 'read_project_info_task') 70 | self._config = config 71 | 72 | def execute(self): 73 | command = '{} -q checkBeforeCleanBuild'.format(get_gradle_executable(self._config)) 74 | output, err, code = cexec(command.split(' '), callback=None) 75 | if code != 0: 76 | from exceptions import FreelineException 77 | raise FreelineException('freeline failed when read project info with script: {}'.format(command), 78 | '{}\n{}'.format(output, err)) 79 | 80 | 81 | class GradleGenerateProjectInfoTask(Task): 82 | def __init__(self, config): 83 | Task.__init__(self, 'generate_project_info_task') 84 | 85 | def execute(self): 86 | # reload project info 87 | from dispatcher import read_freeline_config 88 | config = read_freeline_config() 89 | 90 | write_json_cache(os.path.join(config['build_cache_dir'], 'project_info_cache.json'), 91 | get_project_info(config)) 92 | 93 | 94 | class GradleCleanBuildTask(CleanBuildTask): 95 | def __init__(self, config): 96 | CleanBuildTask.__init__(self, 'gradle_clean_build_task', config) 97 | 98 | def execute(self): 99 | # reload config 100 | from dispatcher import read_freeline_config 101 | self._config = read_freeline_config() 102 | 103 | cwd = self._config['build_script_work_directory'].strip() 104 | if not cwd or not os.path.isdir(cwd): 105 | cwd = None 106 | 107 | command = self._config['build_script'] 108 | command += ' -P freelineBuild=true' 109 | if 'auto_dependency' in self._config and not self._config['auto_dependency']: 110 | command += ' -PdisableAutoDependency=true' 111 | if Logger.debuggable: 112 | command += ' --stacktrace' 113 | self.debug(command) 114 | self.debug("Gradle build task is running, please wait a minute...") 115 | output, err, code = cexec(command.split(' '), callback=None, cwd=cwd) 116 | if code != 0: 117 | from exceptions import FreelineException 118 | raise FreelineException('build failed with script: {}'.format(command), '{}\n{}'.format(output, err)) 119 | -------------------------------------------------------------------------------- /freeline_core/hackwindows.py: -------------------------------------------------------------------------------- 1 | TIOCGWINSZ = 1074295912 2 | 3 | 4 | def fcntl(fd, op, arg=0): 5 | return 0 6 | 7 | 8 | def ioctl(fd, op, arg=0, mutable_flag=True): 9 | if mutable_flag: 10 | return 0 11 | else: 12 | return "" 13 | 14 | 15 | def flock(fd, op): 16 | return 17 | 18 | 19 | def lockf(fd, operation, length=0, start=0, whence=0): 20 | return 21 | -------------------------------------------------------------------------------- /freeline_core/init.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | from __future__ import print_function 3 | import os 4 | 5 | from exceptions import FreelineException 6 | from utils import is_windows_system, cexec, copy, get_file_content 7 | 8 | is_windows = is_windows_system() 9 | 10 | 11 | def init(): 12 | project_dir = os.getcwd() 13 | symlink('freeline', project_dir, 'freeline.py') 14 | 15 | if is_windows: 16 | symlink('freeline', project_dir, 'freeline_core') 17 | 18 | from gradle_tools import get_all_modules 19 | modules = get_all_modules(project_dir) 20 | for m in modules: 21 | if is_main_project(m['path']): 22 | main_module = m 23 | break 24 | 25 | if not main_module: 26 | raise FreelineException('main module not found', 'set main module first') 27 | 28 | print('find main module: ' + main_module['name']) 29 | args = [] 30 | if is_windows: 31 | args.append('gradlew.bat') 32 | else: 33 | args.append('./gradlew') 34 | args.append(':{}:checkBeforeCleanBuild'.format(main_module['name'])) 35 | print('freeline is reading project info, please wait a moment...') 36 | output, err, code = cexec(args, cwd=project_dir) 37 | if code != 0: 38 | raise FreelineException('freeline failed when read project info with script: {}'.format(args), 39 | '{}\n{}'.format(output, err)) 40 | print('freeline init success') 41 | 42 | 43 | def is_main_project(module): 44 | config_path = os.path.join(module, 'build.gradle') 45 | if os.path.exists(config_path): 46 | content = get_file_content(config_path) 47 | if "apply plugin: 'com.antfortune.freeline'" in content: 48 | return True 49 | return False 50 | 51 | 52 | def symlink(base_dir, target_dir, fn): 53 | base_path = os.path.join(base_dir, fn) 54 | target_path = os.path.join(target_dir, fn) 55 | 56 | if not os.path.exists(base_path): 57 | raise FreelineException('file missing: {}'.format(base_path), ' Maybe you should sync freeline repo') 58 | 59 | if os.path.exists(target_path): 60 | os.remove(target_path) 61 | 62 | if is_windows_system(): 63 | copy(base_path, target_path) 64 | else: 65 | os.symlink(base_path, target_path) 66 | -------------------------------------------------------------------------------- /freeline_core/logger.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | from __future__ import print_function 3 | 4 | import Queue 5 | import logging 6 | import os 7 | import threading 8 | import time 9 | import traceback 10 | 11 | from utils import print_json, is_windows_system 12 | 13 | FAILURE = -1 14 | READY = 0 15 | WAITING = 1 16 | WORKING = 2 17 | SUCCESS = 3 18 | 19 | 20 | class LoggerWorker(threading.Thread): 21 | def __init__(self, logger, stop_event): 22 | threading.Thread.__init__(self) 23 | self.setDaemon(True) 24 | 25 | if not isinstance(logger, Logger): 26 | raise Exception('LoggerWorker should be set up with freeline.logger.Logger') 27 | 28 | self._logger = logger 29 | self._stop_event = stop_event 30 | 31 | def run(self): 32 | while not self._stop_event.isSet(): 33 | if self._logger.debuggable: 34 | message = Logger.debug_messages_queue.get() 35 | Logger.print_debug_message(message) 36 | else: 37 | self._logger.update() 38 | time.sleep(self._logger.interval) 39 | 40 | # if in debug mode, clean the debug messages queue 41 | if self._logger.debuggable: 42 | Logger.flush_debug_messages() 43 | else: 44 | self._logger.update() 45 | 46 | 47 | class Logger(object): 48 | # TODO: check screen height before log messages 49 | debuggable = False 50 | debug_messages_queue = Queue.Queue() 51 | temp_backup_queue = Queue.Queue() 52 | info_message_array = [] 53 | TPL_DEBUG_MESSAGE = '[DEBUG] {}' 54 | 55 | def __init__(self, debuggable=False, interval=0.1, unit="s"): 56 | self.debuggable = debuggable 57 | self.interval = interval 58 | self.unit = unit 59 | 60 | if not is_windows_system(): 61 | from cursor import Cursor 62 | from terminal import Terminal 63 | self.cursor = Cursor(Terminal()) 64 | 65 | self.sorted_tasks = [] 66 | 67 | self.tpl_running_task = '[+][{}] {} in {}{}\n' 68 | self.tpl_waiting_task = '[+][{}] {}\n' 69 | self.tpl_finished_task = '[-][{}] {} in {}{}\n' 70 | # self.tpl_faied_task = '[-]{}:{} in {}{}\n' 71 | logging.basicConfig(level=logging.DEBUG) 72 | 73 | def set_sorted_tasks(self, sorted_tasks): 74 | self.sorted_tasks = sorted_tasks 75 | 76 | def draw(self): 77 | # if len(self.sorted_tasks) > 0: 78 | self.cursor.restore() 79 | self._draw() 80 | self.cursor.flush() 81 | 82 | def clear_space(self): 83 | self.cursor.restore() 84 | self.cursor.clear_lines(self._calculate_lines_num()) 85 | self.cursor.save() 86 | 87 | def update(self): 88 | self.clear_space() 89 | self.draw() 90 | 91 | def reset(self): 92 | if not self.debuggable: 93 | self.clear_space() 94 | self.sorted_tasks = [] 95 | Logger.info_message_array = [] 96 | 97 | @staticmethod 98 | def info(message): 99 | Logger.info_message_array.append(message) 100 | 101 | @staticmethod 102 | def debug(message): 103 | Logger.debug_messages_queue.put(message) 104 | Logger.temp_backup_queue.put(message) 105 | 106 | @staticmethod 107 | def warn(message): 108 | pass 109 | 110 | @staticmethod 111 | def error(message): 112 | pass 113 | 114 | @staticmethod 115 | def print_debug_message(message): 116 | if isinstance(message, dict) or isinstance(message, list): 117 | print_json(message) 118 | else: 119 | print(Logger.TPL_DEBUG_MESSAGE.format(message)) 120 | 121 | @staticmethod 122 | def flush_debug_messages(): 123 | while not Logger.debug_messages_queue.empty(): 124 | message = Logger.debug_messages_queue.get() 125 | Logger.print_debug_message(message) 126 | 127 | @staticmethod 128 | def write_error_log(exception=None, extra=None): 129 | import json 130 | try: 131 | log_path = get_error_log_path() 132 | with open(log_path, 'w') as fp: 133 | while not Logger.temp_backup_queue.empty(): 134 | message = Logger.temp_backup_queue.get(timeout=0.5) 135 | if isinstance(message, dict) or isinstance(message, list): 136 | fp.write(json.dumps(message, indent=4, separators=(',', ': '))) 137 | else: 138 | fp.write(message) 139 | fp.write('\n') 140 | 141 | # write extra info 142 | if exception: 143 | fp.write(exception) 144 | return log_path 145 | except Exception as e: 146 | print(traceback.format_exc()) 147 | print(e.message) 148 | return None 149 | 150 | def _calculate_lines_num(self): 151 | lines_count = 0 152 | for task in self.sorted_tasks: 153 | if task.can_show_log: 154 | lines_count += 1 155 | return lines_count + len(Logger.info_message_array) + 1 156 | 157 | def _draw(self): 158 | # map(lambda task: self.cursor.write(self._get_formatted_message(task)), self.sorted_tasks) 159 | map(lambda message: self.cursor.write(message + '\n'), Logger.info_message_array) 160 | map(lambda task: self.cursor.write(self._get_formatted_message(task)), 161 | filter(lambda task: task.can_show_log(), self.sorted_tasks)) 162 | 163 | def _get_formatted_message(self, task): 164 | return { 165 | FAILURE: self.tpl_finished_task.format(task.name, 'failed.', task.cost_time, self.unit), 166 | READY: self.tpl_running_task.format(task.name, 'not start.', 'N/A', self.unit), 167 | WAITING: self.tpl_waiting_task.format(task.name, 'waiting...'), 168 | WORKING: self.tpl_running_task.format(task.name, task.running_message, 169 | round(time.time() - task.run_start_time, 1), self.unit), 170 | SUCCESS: self.tpl_finished_task.format(task.name, task.finished_message, task.cost_time, self.unit) 171 | }.get(task.status, 'NULL') 172 | 173 | 174 | def get_error_log_path(): 175 | return os.path.join(get_error_log_dir(), time.strftime('%y-%m-%d %H-%M-%S') + '.log') 176 | 177 | 178 | def get_error_log_dir(): 179 | dir_path = os.path.join(os.path.expanduser('~'), '.freeline', 'logs') 180 | if not os.path.exists(dir_path): 181 | os.makedirs(dir_path) 182 | return dir_path 183 | -------------------------------------------------------------------------------- /freeline_core/task.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | from __future__ import print_function 3 | import threading 4 | import time 5 | import traceback 6 | 7 | from exceptions import FreelineException 8 | 9 | from logger import Logger, FAILURE, READY, WAITING, WORKING, SUCCESS 10 | 11 | 12 | class Task(object): 13 | def __init__(self, name): 14 | self.name = name 15 | self.parent_tasks = [] 16 | self.child_tasks = [] 17 | 18 | self.status = READY # -1: failed; 0: not start; 1: waiting; 2: working; 3: success; 19 | self.start_time = 0 20 | self.run_start_time = 0 21 | self.cost_time = 0 22 | self.running_message = 'running...' 23 | self.finished_message = 'finished.' 24 | self.condition = threading.Condition() 25 | self.interrupted_exception = None 26 | 27 | def __repr__(self): 28 | return "[{}]".format(self.name) 29 | 30 | def add_parent_task(self, task): 31 | if task not in self.parent_tasks: 32 | self.parent_tasks.append(task) 33 | task.add_child_task(self) 34 | 35 | def add_child_task(self, task): 36 | if task not in self.child_tasks: 37 | self.child_tasks.append(task) 38 | task.add_parent_task(self) 39 | 40 | def is_all_parent_finished(self): 41 | for task in self.parent_tasks: 42 | if task.status != SUCCESS and task.status != FAILURE: 43 | return False 44 | return True 45 | 46 | def wait(self): 47 | self.condition.acquire() 48 | self.condition.wait() 49 | self.condition.release() 50 | 51 | def notify(self): 52 | self.condition.acquire() 53 | self.condition.notify() 54 | self.condition.release() 55 | 56 | def execute(self): 57 | raise NotImplementedError 58 | 59 | def can_show_log(self): 60 | return self.status == SUCCESS or self.status == WORKING or self.status == FAILURE 61 | 62 | def debug(self, message): 63 | Logger.debug('[{}] {}'.format(self.name, message)) 64 | 65 | 66 | class CleanBuildTask(Task): 67 | def __init__(self, name, config): 68 | Task.__init__(self, name) 69 | self._config = config 70 | 71 | def execute(self): 72 | raise NotImplementedError 73 | 74 | 75 | class IncrementalBuildTask(Task): 76 | def __init__(self, name): 77 | Task.__init__(self, name) 78 | 79 | def execute(self): 80 | raise NotImplementedError 81 | 82 | 83 | class SyncTask(Task): 84 | def __init__(self, client, name): 85 | Task.__init__(self, name) 86 | self._client = client 87 | 88 | def execute(self): 89 | raise NotImplementedError 90 | 91 | 92 | class ExecutableTask(object): 93 | def __init__(self, task, engine): 94 | self.task = task 95 | self.engine = engine 96 | self._tpl_debug_message = '[{}] {}' 97 | 98 | def __repr__(self): 99 | return "".format(self.task.name) 100 | 101 | def debug(self, message): 102 | Logger.debug(self._tpl_debug_message.format(self.task.name, message)) 103 | 104 | def execute(self): 105 | # self.debug('{} start to execute...'.format(self.task.name)) 106 | self.task.start_time = time.time() 107 | self.task.status = WAITING 108 | while not self.task.is_all_parent_finished(): 109 | # self.debug('{} waiting...'.format(self.task.name)) 110 | self.task.wait() 111 | self.task.run_start_time = time.time() 112 | self.task.status = WORKING 113 | self.debug('{} start to run after waiting {}s'.format(self.task.name, 114 | round(self.task.run_start_time - self.task.start_time, 115 | 1))) 116 | # check if task need to interrupt before being executing 117 | if self.task.interrupted_exception is not None: 118 | self.task.status = FAILURE 119 | self._pass_interrupted_exception() 120 | return 121 | 122 | try: 123 | self.task.execute() 124 | self.task.status = SUCCESS 125 | except FreelineException as e: 126 | self.task.interrupted_exception = e 127 | self.task.status = FAILURE 128 | except: 129 | self.task.interrupted_exception = FreelineException('unexpected exception within task', 130 | traceback.format_exc()) 131 | self.task.status = FAILURE 132 | 133 | self.task.cost_time = round(time.time() - self.task.run_start_time, 1) 134 | self.debug('{} finish in {}s'.format(self.task.name, round(self.task.cost_time, 1))) 135 | 136 | # check if task need to interrupt after being executing 137 | if self.task.interrupted_exception is not None: 138 | self._pass_interrupted_exception() 139 | return 140 | 141 | for child_task in self.task.child_tasks: 142 | child_task.notify() 143 | 144 | self._check_engine_finished() 145 | 146 | def _pass_interrupted_exception(self): 147 | for child_task in self.engine.get_running_tasks(): 148 | child_task.interrupted_exception = self.task.interrupted_exception 149 | child_task.notify() 150 | 151 | if self.engine.is_all_tasks_finished(): 152 | self.engine.interrupt(self.task.interrupted_exception) 153 | self.engine.finish() 154 | 155 | def _check_engine_finished(self): 156 | if self.engine.is_all_tasks_finished(): 157 | self.engine.finish() 158 | 159 | 160 | def find_root_tasks(task_list): 161 | return filter(lambda task: len(task.parent_tasks) == 0, task_list) 162 | 163 | 164 | def find_last_tasks(task_list): 165 | return filter(lambda task: len(task.child_tasks) == 0, task_list) 166 | -------------------------------------------------------------------------------- /freeline_core/tracing.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from logger import Logger 4 | 5 | 6 | class Tracing(object): 7 | def __init__(self, description): 8 | self.__name = "tracing" 9 | self.__description = description 10 | self.__start_time = 0 11 | 12 | def __enter__(self): 13 | self.__start_time = time.time() 14 | 15 | def __exit__(self, exc_type, exc_val, exc_tb): 16 | if exc_tb: 17 | return False 18 | else: 19 | self.debug(time.time() - self.__start_time) 20 | return True 21 | 22 | def debug(self, execute_time): 23 | Logger.debug('[{}] {}: {}ms'.format(self.__name, self.__description, execute_time * 1000)) 24 | -------------------------------------------------------------------------------- /freeline_core/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | from __future__ import print_function 3 | import os 4 | import string 5 | import random 6 | import platform 7 | import re 8 | import hashlib 9 | import json 10 | import shutil 11 | from hashlib import md5 12 | from subprocess import Popen, PIPE 13 | 14 | import errno 15 | 16 | try: 17 | import xml.etree.cElementTree as ET 18 | except ImportError: 19 | import xml.etree.ElementTree as ET 20 | 21 | 22 | def cexec(args, callback=None, add_path=None, cwd=None): 23 | env = None 24 | if add_path: 25 | import copy 26 | env = copy.copy(os.environ) 27 | env['PATH'] = add_path + os.path.pathsep + env['PATH'] 28 | p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=env, cwd=cwd) 29 | output, err = p.communicate() 30 | code = p.returncode 31 | 32 | if code != 0 and callback: 33 | callback(args, code, output, err) 34 | else: 35 | return output, err, code 36 | 37 | 38 | def curl(url, body=None): 39 | code = 0 40 | err = None 41 | result = None 42 | try: 43 | import urllib2 44 | result = urllib2.urlopen(url, data=body).read().decode('utf-8').strip() 45 | except Exception as e: 46 | code = -1 47 | err = e 48 | return result, err, code 49 | 50 | 51 | def generate_random_string(length=6): 52 | return ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(length)) 53 | 54 | 55 | def is_windows_system(): 56 | return 'Windows' in platform.system() 57 | 58 | 59 | def is_linux_system(): 60 | return 'Linux' in platform.system() 61 | 62 | 63 | def copy(src, dst): 64 | try: 65 | shutil.copytree(src, dst) 66 | except OSError as e: 67 | if e.errno == errno.ENOTDIR: 68 | shutil.copy(src, dst) 69 | else: 70 | print('Directory not copied. Error: {}'.format(e.message)) 71 | 72 | 73 | def get_md5(fpath): 74 | m = md5() 75 | target_file = open(fpath, 'rb') 76 | m.update(target_file.read()) 77 | target_file.close() 78 | return m.hexdigest() 79 | 80 | 81 | def md5string(param): 82 | m = hashlib.md5() 83 | m.update(param.encode('utf-8')) 84 | return m.hexdigest() 85 | 86 | 87 | base = [str(x) for x in range(10)] + [chr(x) for x in range(ord('A'), ord('A') + 6)] 88 | 89 | 90 | def dec2hex(string_num): 91 | num = int(string_num) 92 | mid = [] 93 | while True: 94 | if num == 0: 95 | break 96 | num, rem = divmod(num, 16) 97 | mid.append(base[rem]) 98 | return ''.join([str(t) for t in mid[::-1]]) 99 | 100 | 101 | def is_exe(path): 102 | return os.path.isfile(path) and os.access(path, os.X_OK) 103 | 104 | 105 | def print_json(json_obj): 106 | print(json.dumps(json_obj, indent=4, separators=(',', ': '))) 107 | 108 | 109 | def get_file_content(path): 110 | if not path or not os.path.isfile(path): 111 | return '' 112 | import codecs 113 | with codecs.open(path, encoding='utf-8') as f: 114 | return f.read() 115 | 116 | 117 | def write_file_content(target_path, content): 118 | with open(target_path, 'w') as fp: 119 | if isinstance(content, int): 120 | content = unicode(content) 121 | fp.write(content.encode('utf-8')) 122 | 123 | 124 | def load_json_cache(fpath): 125 | cache = {} 126 | if not os.path.exists(fpath): 127 | pass 128 | if os.path.isfile(fpath): 129 | try: 130 | with open(fpath, 'r') as fp: 131 | cache = json.load(fp) 132 | except Exception: 133 | pass 134 | return cache 135 | 136 | 137 | def write_json_cache(fpath, cache): 138 | try: 139 | with open(fpath, 'w') as fp: 140 | json.dump(cache, fp) 141 | except Exception: 142 | pass 143 | 144 | 145 | def calculate_typed_file_count(dir_path, ext): 146 | i = 0 147 | for dirpath, dirnames, files in os.walk(dir_path): 148 | for fn in files: 149 | if fn.endswith(ext): 150 | i += 1 151 | return i 152 | 153 | 154 | def remove_namespace(path): 155 | content = get_file_content(path) 156 | xmlstring = re.sub('xmlns="[^"]+"', '', content, count=1) 157 | return xmlstring.encode('utf-8') # to avoid UnicodeEncodeError 158 | 159 | 160 | def merge_xml(filenames): 161 | return ET.tostring(XMLCombiner(filenames).combine().getroot()) 162 | 163 | 164 | def get_text_by_tag(root, tag): 165 | element = root.find(tag) 166 | return element.text if element is not None else '' 167 | 168 | 169 | class HashableDict(dict): 170 | def __hash__(self): 171 | return hash(tuple(sorted(self.items()))) 172 | 173 | 174 | class XMLCombiner(object): 175 | def __init__(self, filenames): 176 | assert len(filenames) > 0, 'No filenames!' 177 | self.roots = [ET.parse(f).getroot() for f in filenames] 178 | 179 | def combine(self): 180 | for r in self.roots[1:]: 181 | # combine each element with the first one, and update that 182 | self.combine_element(self.roots[0], r) 183 | # return the string representation 184 | return ET.ElementTree(self.roots[0]) 185 | 186 | def combine_element(self, one, other): 187 | mapping = {(el.tag, HashableDict(el.attrib)): el for el in one} 188 | for el in other: 189 | if len(el) == 0: 190 | try: 191 | # Update the text 192 | mapping[(el.tag, HashableDict(el.attrib))].text = el.text 193 | except KeyError: 194 | # An element with this name is not in the mapping 195 | mapping[(el.tag, HashableDict(el.attrib))] = el 196 | # Add it 197 | one.append(el) 198 | else: 199 | try: 200 | # Recursively process the element, and update it in the same way 201 | self.combine_element(mapping[(el.tag, HashableDict(el.attrib))], el) 202 | except KeyError: 203 | mapping[(el.tag, HashableDict(el.attrib))] = el 204 | one.append(el) 205 | -------------------------------------------------------------------------------- /freeline_core/version.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf8 -*- 2 | import subprocess 3 | import os 4 | 5 | VERSION_FORMATTER = '{}({})' 6 | FREELINE_VERSION = 'v0.8.7' 7 | 8 | 9 | def get_freeline_version(): 10 | if is_git_dir(): 11 | return VERSION_FORMATTER.format(FREELINE_VERSION, get_git_short_version()) 12 | else: 13 | return FREELINE_VERSION 14 | 15 | 16 | def get_git_short_version(): 17 | # note: get git version 18 | # http://stackoverflow.com/questions/14989858/get-the-current-git-hash-in-a-python-script 19 | return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']) 20 | 21 | 22 | def is_git_dir(): 23 | project_root_path = os.path.abspath(os.path.join(os.path.realpath(__file__), os.pardir)) 24 | git_dir_path = os.path.join(project_root_path, '.git') 25 | if os.path.exists(git_dir_path) and os.path.isdir(git_dir_path): 26 | return True 27 | return False 28 | -------------------------------------------------------------------------------- /freeline_project_description.json: -------------------------------------------------------------------------------- 1 | { 2 | "android_gradle_plugin_version": "2.2.0", 3 | "apk_path": "F:\\ForGitHubProject\\app\\build\\outputs\\apk\\app-debug.apk", 4 | "auto_dependency": true, 5 | "build_cache_dir": "F:\\ForGitHubProject\\app\\build\\freeline", 6 | "build_directory": "F:\\ForGitHubProject\\app\\build", 7 | "build_script": "gradlew.bat :app:assembleDebug", 8 | "build_script_work_directory": "", 9 | "build_tools_directory": "Z:\\SDK\\build-tools\\24.0.3", 10 | "build_tools_version": "24.0.3", 11 | "check_sources_md5": [ 12 | 13 | ], 14 | "compile_sdk_directory": "Z:\\SDK\\platforms\\android-24", 15 | "compile_sdk_version": "android-24", 16 | "databinding": [ 17 | 18 | ], 19 | "databinding_modules": [ 20 | 21 | ], 22 | "debug_package": "com.zone.forgithubproject", 23 | "exclude_dep_res_paths": [ 24 | 25 | ], 26 | "extra_dep_res_paths": [ 27 | 28 | ], 29 | "freeline_cache_dir": "F:\\ForGitHubProject", 30 | "freeline_gradle_plugin_version": "0.8.7", 31 | "ignore_resource_ids": [ 32 | "avd_hide_password_1", 33 | "avd_hide_password_2", 34 | "avd_hide_password_3", 35 | "avd_show_password_1", 36 | "avd_show_password_2", 37 | "avd_show_password_3" 38 | ], 39 | "java_home": "C:\\Program Files\\Java\\jdk1.8.0_51", 40 | "launcher": "com.zone.forgithubproject.AdActivity", 41 | "main_assets_directory": [ 42 | "F:\\ForGitHubProject\\app\\src\\debug\\assets", 43 | "F:\\ForGitHubProject\\app\\src\\main\\assets" 44 | ], 45 | "main_jniLibs_directory": [ 46 | "F:\\ForGitHubProject\\app\\src\\debug\\jniLibs", 47 | "F:\\ForGitHubProject\\app\\src\\main\\jniLibs" 48 | ], 49 | "main_jni_directory": [ 50 | "F:\\ForGitHubProject\\app\\src\\debug\\jni", 51 | "F:\\ForGitHubProject\\app\\src\\main\\jni" 52 | ], 53 | "main_manifest_path": "F:\\ForGitHubProject\\app\\src\\main\\AndroidManifest.xml", 54 | "main_project_dir": "app", 55 | "main_project_name": "app", 56 | "main_r_path": "F:\\ForGitHubProject\\app\\build\\generated\\source\\r\\debug\\R.java", 57 | "main_res_directory": [ 58 | "F:\\ForGitHubProject\\app\\src\\debug\\res", 59 | "F:\\ForGitHubProject\\app\\src\\main\\res" 60 | ], 61 | "main_src_directory": [ 62 | "F:\\ForGitHubProject\\app\\src\\debug\\java", 63 | "F:\\ForGitHubProject\\app\\src\\main\\java" 64 | ], 65 | "module_dependencies": { 66 | "app": [ 67 | 68 | ] 69 | }, 70 | "modules": [ 71 | { 72 | "name": "app", 73 | "path": "F:\\ForGitHubProject\\app" 74 | } 75 | ], 76 | "package": "com.zone.forgithubproject", 77 | "product_flavor": "", 78 | "project_source_sets": { 79 | "app": { 80 | "main_assets_directory": [ 81 | "F:\\ForGitHubProject\\app\\src\\debug\\assets", 82 | "F:\\ForGitHubProject\\app\\src\\main\\assets" 83 | ], 84 | "main_jniLibs_directory": [ 85 | "F:\\ForGitHubProject\\app\\src\\debug\\jniLibs", 86 | "F:\\ForGitHubProject\\app\\src\\main\\jniLibs" 87 | ], 88 | "main_jni_directory": [ 89 | "F:\\ForGitHubProject\\app\\src\\debug\\jni", 90 | "F:\\ForGitHubProject\\app\\src\\main\\jni" 91 | ], 92 | "main_manifest_path": "F:\\ForGitHubProject\\app\\src\\main\\AndroidManifest.xml", 93 | "main_res_directory": [ 94 | "F:\\ForGitHubProject\\app\\src\\debug\\res", 95 | "F:\\ForGitHubProject\\app\\src\\main\\res" 96 | ], 97 | "main_src_directory": [ 98 | "F:\\ForGitHubProject\\app\\src\\debug\\java", 99 | "F:\\ForGitHubProject\\app\\src\\main\\java" 100 | ] 101 | } 102 | }, 103 | "project_type": "gradle", 104 | "root_dir": "F:\\ForGitHubProject", 105 | "sdk_directory": "Z:\\SDK", 106 | "use_jdk8": true, 107 | "use_system_gradle": false 108 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonezoen/ForGitHubProject/57fd36d6e82c2b35e3b3027749b9a09c5659d2f1/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------