From 7e7e20a13ea7cf2a9d11531f2958022f17d5ae5b Mon Sep 17 00:00:00 2001 From: Lennon <1136160757@qq.com> Date: Wed, 27 Nov 2019 15:16:08 +0800 Subject: [PATCH 01/24] =?UTF-8?q?fork=20XDroidMvp=20=E5=B9=B6=E4=BF=AE?= =?UTF-8?q?=E6=94=B9support=E4=BE=9D=E8=B5=96=E4=B8=BAAndroidX=E4=BE=9D?= =?UTF-8?q?=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Readme.md | 9 +- app/build.gradle | 8 +- .../xdroidmvp/demo/adapter/GanhuoAdapter.java | 2 +- .../xdroidmvp/demo/adapter/GirlAdapter.java | 2 +- .../xdroidmvp/demo/adapter/HomeAdapter.java | 2 +- .../xdroidmvp/demo/ui/AboutActivity.java | 2 +- .../xdroidmvp/demo/ui/MainActivity.java | 8 +- .../xdroidmvp/demo/ui/WebActivity.java | 4 +- app/src/main/res/layout/activity_main.xml | 6 +- app/src/main/res/layout/activity_web.xml | 4 +- app/src/main/res/layout/view_toolbar.xml | 2 +- build.gradle | 6 +- conf.gradle | 61 +- gradle.properties | 6 +- gradle/wrapper/gradle-wrapper.properties | 2 +- mvp/build.gradle | 4 +- .../xdroidmvp/base/SimpleRecAdapter.java | 2 +- .../xdroidmvp/base/XFragmentAdapter.java | 7 +- .../droidlover/xdroidmvp/cache/DiskCache.java | 8 +- .../xdroidmvp/cache/DiskLruCache.java | 1 + .../xdroidmvp/cache/MemoryCache.java | 6 +- .../xdroidmvp/imageloader/GlideLoader.java | 4 - .../xdroidmvp/mvp/LazyFragment.java | 4 +- .../droidlover/xdroidmvp/mvp/XActivity.java | 2 +- .../droidlover/xdroidmvp/mvp/XFragment.java | 2 +- .../droidlover/xdroidmvp/router/Router.java | 4 +- settings.gradle | 2 +- test/build.gradle | 4 +- .../xdroidmvp/test/base/BRecAdapter.java | 2 +- .../xdroidmvp/test/base/DRecAdapter.java | 2 +- .../xdroidmvp/test/router/StartActivity.java | 2 +- xrecycler/build.gradle | 43 ++ xrecycler/src/main/AndroidManifest.xml | 9 + .../xrecyclerview/LoadMoreUIHandler.java | 11 + .../xrecyclerview/RecyclerAdapter.java | 298 ++++++++ .../xrecyclerview/RecyclerItemCallback.java | 29 + .../xrecyclerview/SimpleLoadMoreFooter.java | 55 ++ .../xrecyclerview/XDataObserver.java | 47 ++ .../xrecyclerview/XHeadFootViewHolder.java | 18 + .../xrecyclerview/XRecyclerAdapter.java | 305 ++++++++ .../xrecyclerview/XRecyclerContentLayout.java | 165 +++++ .../xrecyclerview/XRecyclerView.java | 687 ++++++++++++++++++ .../divider/FlexibleDividerDecoration.java | 342 +++++++++ .../HorizontalDividerItemDecoration.java | 144 ++++ .../VerticalDividerItemDecoration.java | 143 ++++ .../layout/x_view_footer_load_more_simple.xml | 24 + .../layout/x_view_recycler_content_layout.xml | 15 + xrecycler/src/main/res/values/attrs.xml | 20 + xrecycler/src/main/res/values/colors.xml | 7 + xrecycler/src/main/res/values/strings.xml | 3 + 50 files changed, 2471 insertions(+), 74 deletions(-) create mode 100644 xrecycler/build.gradle create mode 100644 xrecycler/src/main/AndroidManifest.xml create mode 100644 xrecycler/src/main/java/cn/droidlover/xrecyclerview/LoadMoreUIHandler.java create mode 100644 xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerAdapter.java create mode 100644 xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerItemCallback.java create mode 100644 xrecycler/src/main/java/cn/droidlover/xrecyclerview/SimpleLoadMoreFooter.java create mode 100644 xrecycler/src/main/java/cn/droidlover/xrecyclerview/XDataObserver.java create mode 100644 xrecycler/src/main/java/cn/droidlover/xrecyclerview/XHeadFootViewHolder.java create mode 100644 xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerAdapter.java create mode 100644 xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerContentLayout.java create mode 100644 xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerView.java create mode 100644 xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/FlexibleDividerDecoration.java create mode 100644 xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/HorizontalDividerItemDecoration.java create mode 100644 xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/VerticalDividerItemDecoration.java create mode 100644 xrecycler/src/main/res/layout/x_view_footer_load_more_simple.xml create mode 100644 xrecycler/src/main/res/layout/x_view_recycler_content_layout.xml create mode 100644 xrecycler/src/main/res/values/attrs.xml create mode 100644 xrecycler/src/main/res/values/colors.xml create mode 100644 xrecycler/src/main/res/values/strings.xml diff --git a/Readme.md b/Readme.md index 8aeec97..31ed840 100644 --- a/Readme.md +++ b/Readme.md @@ -1,4 +1,4 @@ -#XDroidMvp 轻量级的Android MVP快速开发框架 +#XDroidMvp 轻量级的Android MVP快速开发框架(AndroidX适配版本) ## 概述 @@ -196,7 +196,10 @@ dependencies { * [ButterKnife](https://github.com/JakeWharton/butterknife)使用的是8.4.0版本,重点是 `@BindView`,可以去项目官网查看。 * [Rxlifecycle](https://github.com/trello/RxLifecycle)使用的是1.0版本,具体如何使用可以查看官网。 * [RxPermissions](https://github.com/tbruyelle/RxPermissions)使用的是0.9.1版本,具体如何使用可以查看官网。 -* [retrofit](https://github.com/square/retrofit),具体如何使用可以查看官网。 +* [retrofit](https://github.com/square/retrofit),具体如何使用可以查看官网。 +* [一定]()要修改当前项目的 gradle.properties添加 + android.useAndroidX=true + android.enableJetifier=true ## 更新日志 @@ -225,6 +228,8 @@ dependencies { **XDroid交流群**:153569290 +**XDroid MVP原版**:[XDroidMvp](https://github.com/limedroid/XDroidMvp) + **XDroid MVC版本**:[XDroid](https://github.com/limedroid/XDroid) 若您在使用过程中遇到任何问题,欢迎加入 **153569290** 群或者是邮件反馈,谢谢您的关注。**XDroidMvp**会持续维护,如果喜欢,记得star fork。 diff --git a/app/build.gradle b/app/build.gradle index 8882733..017d369 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion rootProject.ext.android.compileSdkVersion - + buildToolsVersion "29.0.2" defaultConfig { applicationId 'cn.droidlover.xdroidmvp.demo' minSdkVersion rootProject.ext.android.minSdkVersion @@ -21,8 +21,8 @@ android { dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) + api fileTree(dir: 'libs', include: ['*.jar']) annotationProcessor rootProject.ext.dependencies["butterknife-apt"] - implementation rootProject.ext.dependencies["avi-loading"] - implementation project(":mvp") + api rootProject.ext.dependencies["avi-loading"] + api project(":mvp") } diff --git a/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/GanhuoAdapter.java b/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/GanhuoAdapter.java index 2dd2ac9..d759023 100644 --- a/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/GanhuoAdapter.java +++ b/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/GanhuoAdapter.java @@ -1,7 +1,7 @@ package cn.droidlover.xdroidmvp.demo.adapter; import android.content.Context; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.view.View; import android.widget.TextView; diff --git a/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/GirlAdapter.java b/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/GirlAdapter.java index a51c03d..2bbebb5 100644 --- a/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/GirlAdapter.java +++ b/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/GirlAdapter.java @@ -1,7 +1,7 @@ package cn.droidlover.xdroidmvp.demo.adapter; import android.content.Context; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.view.View; import android.widget.ImageView; diff --git a/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/HomeAdapter.java b/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/HomeAdapter.java index 14398e6..21332a3 100644 --- a/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/HomeAdapter.java +++ b/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/HomeAdapter.java @@ -3,7 +3,7 @@ import android.content.Context; import android.graphics.Color; import android.net.Uri; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.view.View; import android.widget.ImageView; import android.widget.RelativeLayout; diff --git a/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/AboutActivity.java b/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/AboutActivity.java index c25b60b..67d041e 100644 --- a/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/AboutActivity.java +++ b/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/AboutActivity.java @@ -2,7 +2,7 @@ import android.app.Activity; import android.os.Bundle; -import android.support.v7.widget.Toolbar; +import androidx.appcompat.widget.Toolbar; import android.view.MenuItem; import android.view.View; diff --git a/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/MainActivity.java b/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/MainActivity.java index 304c222..8509baf 100644 --- a/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/MainActivity.java +++ b/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/MainActivity.java @@ -1,10 +1,9 @@ package cn.droidlover.xdroidmvp.demo.ui; import android.os.Bundle; -import android.support.design.widget.TabLayout; -import android.support.v4.app.Fragment; -import android.support.v4.view.ViewPager; -import android.support.v7.widget.Toolbar; +import androidx.appcompat.widget.Toolbar; +import androidx.fragment.app.Fragment; +import androidx.viewpager.widget.ViewPager; import android.view.MenuItem; import java.util.ArrayList; @@ -14,6 +13,7 @@ import cn.droidlover.xdroidmvp.base.XFragmentAdapter; import cn.droidlover.xdroidmvp.demo.R; import cn.droidlover.xdroidmvp.mvp.XActivity; +import com.google.android.material.tabs.TabLayout; /** * Created by wanglei on 2016/12/22. diff --git a/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/WebActivity.java b/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/WebActivity.java index 578ace7..7826264 100644 --- a/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/WebActivity.java +++ b/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/WebActivity.java @@ -2,8 +2,7 @@ import android.app.Activity; import android.os.Bundle; -import android.support.v4.widget.SwipeRefreshLayout; -import android.support.v7.widget.Toolbar; +import androidx.appcompat.widget.Toolbar; import android.view.KeyEvent; import android.view.MenuItem; import android.view.View; @@ -13,6 +12,7 @@ import android.webkit.WebView; import android.webkit.WebViewClient; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import butterknife.BindView; import cn.droidlover.xdroidmvp.demo.R; import cn.droidlover.xdroidmvp.demo.kit.AppKit; diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index daa8d1e..44c6300 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -10,7 +10,7 @@ android:layout_height="wrap_content" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> - - - - @@ -21,7 +21,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> - + diff --git a/app/src/main/res/layout/view_toolbar.xml b/app/src/main/res/layout/view_toolbar.xml index d416e75..e343e70 100644 --- a/app/src/main/res/layout/view_toolbar.xml +++ b/app/src/main/res/layout/view_toolbar.xml @@ -6,7 +6,7 @@ android:layout_height="wrap_content" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> - fragmentList = new ArrayList<>(); private String[] titles; diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskCache.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskCache.java index 203b6db..7593ca1 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskCache.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskCache.java @@ -58,7 +58,9 @@ public void put(String key, String value) { } public void put(String key, String value, long expireMills) { - if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) return; + if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) { + return; + } String name = getMd5Key(key); try { @@ -82,6 +84,7 @@ public void put(String key, Object value) { } + @Override public String get(String key) { try { String md5Key = getMd5Key(key); @@ -115,6 +118,7 @@ public String get(String key) { return null; } + @Override public void remove(String key) { try { cache.remove(getMd5Key(key)); @@ -123,6 +127,7 @@ public void remove(String key) { } } + @Override public boolean contains(String key) { try { DiskLruCache.Snapshot snapshot = cache.get(getMd5Key(key)); @@ -133,6 +138,7 @@ public boolean contains(String key) { return false; } + @Override public void clear() { try { cache.delete(); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskLruCache.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskLruCache.java index c096b9b..87b7acf 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskLruCache.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskLruCache.java @@ -688,6 +688,7 @@ public synchronized void flush() throws IOException { /** * Closes this cache. Stored values will remain on the filesystem. */ + @Override public synchronized void close() throws IOException { if (journalWriter == null) { return; // already closed diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/MemoryCache.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/MemoryCache.java index c2c0ef8..63f1298 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/MemoryCache.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/MemoryCache.java @@ -1,6 +1,6 @@ package cn.droidlover.xdroidmvp.cache; -import android.support.v4.util.LruCache; +import androidx.collection.LruCache; import android.text.TextUtils; /** @@ -33,7 +33,9 @@ public static MemoryCache getInstance() { @Override public synchronized void put(String key, Object value) { - if (TextUtils.isEmpty(key)) return; + if (TextUtils.isEmpty(key)) { + return; + } if (cache.get(key) != null) { cache.remove(key); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/GlideLoader.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/GlideLoader.java index 029e94c..778e75a 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/GlideLoader.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/GlideLoader.java @@ -5,9 +5,6 @@ import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.support.annotation.Nullable; -import android.support.v4.content.ContextCompat; -import android.support.v4.graphics.drawable.RoundedBitmapDrawable; -import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; import android.widget.ImageView; import com.bumptech.glide.Glide; @@ -19,7 +16,6 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; import com.bumptech.glide.request.RequestOptions; -import com.bumptech.glide.request.target.BitmapImageViewTarget; import com.bumptech.glide.request.target.SimpleTarget; import com.bumptech.glide.request.transition.DrawableCrossFadeFactory; import com.bumptech.glide.request.transition.Transition; diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java index b3961ab..f3c371b 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java @@ -4,13 +4,13 @@ import android.content.Context; import android.os.Bundle; import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import com.trello.rxlifecycle3.components.support.RxFragment; -import com.trello.rxlifecycle2.components.support.RxFragment; import java.lang.reflect.Field; diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java index faccc4d..cc3242a 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java @@ -7,12 +7,12 @@ import android.view.View; import com.tbruyelle.rxpermissions2.RxPermissions; -import com.trello.rxlifecycle2.components.support.RxAppCompatActivity; import butterknife.Unbinder; import cn.droidlover.xdroidmvp.XDroidConf; import cn.droidlover.xdroidmvp.event.BusProvider; import cn.droidlover.xdroidmvp.kit.KnifeKit; +import com.trello.rxlifecycle3.components.support.RxAppCompatActivity; /** * Created by wanglei on 2016/12/29. diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java index d3db641..d737450 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java @@ -9,12 +9,12 @@ import android.view.ViewGroup; import com.tbruyelle.rxpermissions2.RxPermissions; -import com.trello.rxlifecycle2.components.support.RxFragment; import butterknife.Unbinder; import cn.droidlover.xdroidmvp.XDroidConf; import cn.droidlover.xdroidmvp.event.BusProvider; import cn.droidlover.xdroidmvp.kit.KnifeKit; +import com.trello.rxlifecycle3.components.support.RxFragment; /** * Created by wanglei on 2016/12/29. diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/router/Router.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/router/Router.java index dd20f80..7fb53cb 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/router/Router.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/router/Router.java @@ -5,12 +5,12 @@ import android.os.Bundle; import android.os.Parcelable; import android.support.annotation.Nullable; -import android.support.v4.app.ActivityCompat; -import android.support.v4.app.ActivityOptionsCompat; import java.io.Serializable; import java.util.ArrayList; +import androidx.core.app.ActivityCompat; +import androidx.core.app.ActivityOptionsCompat; import cn.droidlover.xdroidmvp.XDroidConf; /** diff --git a/settings.gradle b/settings.gradle index 4329383..ace07e0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':mvp', ':test' +include ':app', ':mvp', ':test',':xrecycler' diff --git a/test/build.gradle b/test/build.gradle index f10b251..4383c2e 100644 --- a/test/build.gradle +++ b/test/build.gradle @@ -19,8 +19,8 @@ android { } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) + api fileTree(dir: 'libs', include: ['*.jar']) annotationProcessor rootProject.ext.dependencies["butterknife-apt"] - implementation project(":mvp") + api project(":mvp") } diff --git a/test/src/main/java/cn/droidlover/xdroidmvp/test/base/BRecAdapter.java b/test/src/main/java/cn/droidlover/xdroidmvp/test/base/BRecAdapter.java index a6810ae..aad4c6f 100644 --- a/test/src/main/java/cn/droidlover/xdroidmvp/test/base/BRecAdapter.java +++ b/test/src/main/java/cn/droidlover/xdroidmvp/test/base/BRecAdapter.java @@ -1,7 +1,7 @@ package cn.droidlover.xdroidmvp.test.base; import android.content.Context; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.view.View; import cn.droidlover.xdroidmvp.base.SimpleRecAdapter; diff --git a/test/src/main/java/cn/droidlover/xdroidmvp/test/base/DRecAdapter.java b/test/src/main/java/cn/droidlover/xdroidmvp/test/base/DRecAdapter.java index 65076ba..ca5a5aa 100644 --- a/test/src/main/java/cn/droidlover/xdroidmvp/test/base/DRecAdapter.java +++ b/test/src/main/java/cn/droidlover/xdroidmvp/test/base/DRecAdapter.java @@ -1,7 +1,7 @@ package cn.droidlover.xdroidmvp.test.base; import android.content.Context; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/test/src/main/java/cn/droidlover/xdroidmvp/test/router/StartActivity.java b/test/src/main/java/cn/droidlover/xdroidmvp/test/router/StartActivity.java index d3c60d2..22819e4 100644 --- a/test/src/main/java/cn/droidlover/xdroidmvp/test/router/StartActivity.java +++ b/test/src/main/java/cn/droidlover/xdroidmvp/test/router/StartActivity.java @@ -2,8 +2,8 @@ import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.ActivityOptionsCompat; +import androidx.core.app.ActivityOptionsCompat; import cn.droidlover.xdroidmvp.mvp.XActivity; import cn.droidlover.xdroidmvp.router.Router; diff --git a/xrecycler/build.gradle b/xrecycler/build.gradle new file mode 100644 index 0000000..54aae9c --- /dev/null +++ b/xrecycler/build.gradle @@ -0,0 +1,43 @@ +apply plugin: 'com.android.library' +apply plugin: 'com.github.dcendents.android-maven' + +group = 'com.github.limedroid' + +android { + compileSdkVersion rootProject.ext.android.compileSdkVersion + + defaultConfig { + minSdkVersion rootProject.ext.android.minSdkVersion + targetSdkVersion rootProject.ext.android.targetSdkVersion + versionCode rootProject.ext.android.versionCode + versionName rootProject.ext.android.versionName + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + api fileTree(dir: 'libs', include: ['*.jar']) + api rootProject.ext.dependencies["recyclerview-v7"] + api rootProject.ext.dependencies["annotations"] + api rootProject.ext.dependencies["XStateController"] + api rootProject.ext.dependencies["support-v4"] +} + + +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" +} + +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +artifacts { + archives sourcesJar +} diff --git a/xrecycler/src/main/AndroidManifest.xml b/xrecycler/src/main/AndroidManifest.xml new file mode 100644 index 0000000..78feb01 --- /dev/null +++ b/xrecycler/src/main/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/LoadMoreUIHandler.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/LoadMoreUIHandler.java new file mode 100644 index 0000000..bb624bd --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/LoadMoreUIHandler.java @@ -0,0 +1,11 @@ +package cn.droidlover.xrecyclerview; + +/** + * Created by wanglei on 2016/10/30. + */ + +public interface LoadMoreUIHandler { + void onLoading(); + + void onLoadFinish(boolean hasMore); +} diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerAdapter.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerAdapter.java new file mode 100644 index 0000000..e89c2e0 --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerAdapter.java @@ -0,0 +1,298 @@ +package cn.droidlover.xrecyclerview; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.view.ViewGroup; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Created by wanglei on 2016/10/30. + */ + +public abstract class RecyclerAdapter extends RecyclerView.Adapter { + + protected Context context; + protected List data = new ArrayList(); + private RecyclerItemCallback recItemClick; + + public RecyclerAdapter(Context context) { + this.context = context; + } + + /** + * 设置数据源 + * + * @param data + */ + public void setData(List data) { + this.data.clear(); + if (data != null) { + this.data.addAll(data); + } + notifyDataSetChanged(); + } + + /** + * 设置数据源 + * + * @param data + */ + public void setData(T[] data) { + if (data != null && data.length > 0) { + setData(Arrays.asList(data)); + } + } + + /** + * 添加数据 + * + * @param data + */ + public void addData(List data) { + int preSize = this.data.size(); + if (data != null && data.size() > 0) { + if (this.data == null) { + this.data = new ArrayList(); + } + this.data.addAll(data); + notifyItemRangeInserted(preSize, this.data.size()); + } + notifyDataSetChanged(); + } + + /** + * 添加数据 + * + * @param data + */ + public void addData(T[] data) { + addData(Arrays.asList(data)); + } + + + /** + * 删除元素 + * + * @param element + */ + public void removeElement(T element) { + if (data.contains(element)) { + int position = data.indexOf(element); + data.remove(element); + notifyItemRemoved(position); + notifyItemChanged(position); + } + } + + /** + * 删除元素 + * + * @param position + */ + public void removeElement(int position) { + if (data != null && data.size() > position) { + data.remove(position); + notifyItemRemoved(position); + notifyItemChanged(position); + } + } + + /** + * 删除元素 + * + * @param elements + */ + public void removeElements(List elements) { + if (data != null && elements != null && elements.size() > 0 + && data.size() >= elements.size()) { + + for (T element : elements) { + if (data.contains(element)) { + data.remove(element); + } + } + + notifyDataSetChanged(); + } + } + + /** + * 删除元素 + * + * @param elements + */ + public void removeElements(T[] elements) { + if (elements != null && elements.length > 0) { + removeElements(Arrays.asList(elements)); + } + } + + /** + * 更新元素 + * + * @param element + * @param position + */ + public void updateElement(T element, int position) { + if (position >= 0 && data.size() > position) { + data.remove(position); + data.add(position, element); + notifyItemChanged(position); + } + } + + /** + * 添加元素 + * + * @param element + */ + public void addElement(T element) { + if (element != null) { + if (this.data == null) { + this.data = new ArrayList(); + } + data.add(element); + notifyItemInserted(this.data.size()); + } + } + + public void addElement(int position, T element) { + if (element != null) { + if (this.data == null) { + this.data = new ArrayList(); + } + data.add(position, element); + notifyItemInserted(position); + } + } + + /** + * 清除数据源 + */ + public void clearData() { + if (this.data != null) { + this.data.clear(); + notifyDataSetChanged(); + } + } + + + /** + * 设置控件可见 + * + * @param view + */ + protected void setVisible(View view) { + view.setVisibility(View.VISIBLE); + } + + /** + * 设置控件不可见 + * + * @param view + */ + protected void setGone(View view) { + view.setVisibility(View.GONE); + } + + /** + * 设置控件不可见 + * + * @param view + */ + protected void setInvisible(View view) { + view.setVisibility(View.INVISIBLE); + } + + /** + * 设置控件可用 + * + * @param view + */ + protected void setEnable(View view) { + view.setEnabled(true); + } + + /** + * 设置控件不可用 + * + * @param view + */ + protected void setDisable(View view) { + view.setEnabled(false); + } + + /** + * 获取图片资源 + * + * @param resId + * @return + */ + protected Drawable getDrawable(int resId) { + return context.getResources().getDrawable(resId); + } + + /** + * 获取字符串资源 + * + * @param resId + * @return + */ + protected String getString(int resId) { + return context.getResources().getString(resId); + } + + /** + * 获取颜色资源 + * + * @param resId + * @return + */ + protected int getColor(int resId) { + return context.getResources().getColor(resId); + } + + /** + * 获取数据源 + * + * @return + */ + public List getDataSource() { + return data; + } + + + public void setRecItemClick(RecyclerItemCallback recItemClick) { + this.recItemClick = recItemClick; + } + + public RecyclerItemCallback getRecItemClick() { + return recItemClick; + } + + + @Override + public int getItemViewType(int position) { + return super.getItemViewType(position); + } + + @Override + public abstract F onCreateViewHolder(ViewGroup parent, int viewType); + + @Override + public abstract void onBindViewHolder(F holder, int position); + + @Override + public int getItemCount() { + return data == null || data.isEmpty() ? 0 : data.size(); + } + + +} + + diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerItemCallback.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerItemCallback.java new file mode 100644 index 0000000..6e25f9d --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerItemCallback.java @@ -0,0 +1,29 @@ +package cn.droidlover.xrecyclerview; + +/** + * Created by wanglei on 2016/10/30. + */ + +public abstract class RecyclerItemCallback { + /** + * 单击事件 + * + * @param position 位置 + * @param model 实体 + * @param tag 标签 + * @param holder 控件 + */ + public void onItemClick(int position, T model, int tag, F holder) { + } + + /** + * 长按事件 + * + * @param position 位置 + * @param model 实体 + * @param tag 标签 + * @param holder 控件 + */ + public void onItemLongClick(int position, T model, int tag, F holder) { + } +} diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/SimpleLoadMoreFooter.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/SimpleLoadMoreFooter.java new file mode 100644 index 0000000..6e4e4ce --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/SimpleLoadMoreFooter.java @@ -0,0 +1,55 @@ +package cn.droidlover.xrecyclerview; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; +import android.widget.TextView; + +/** + * Created by wanglei on 2016/10/30. + */ + +public class SimpleLoadMoreFooter extends RelativeLayout implements LoadMoreUIHandler { + + TextView tvMsg; + ProgressBar progressBar; + + public SimpleLoadMoreFooter(Context context) { + this(context, null); + } + + public SimpleLoadMoreFooter(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SimpleLoadMoreFooter(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setupView(); + } + + private void setupView() { + inflate(getContext(), R.layout.x_view_footer_load_more_simple, this); + tvMsg = (TextView) findViewById(R.id.tv_msg); + progressBar = (ProgressBar) findViewById(R.id.progressBar); + } + + @Override + public void onLoading() { + setVisibility(VISIBLE); + tvMsg.setText("加载中"); + progressBar.setVisibility(VISIBLE); + } + + @Override + public void onLoadFinish(boolean hasMore) { + if (hasMore) { + setVisibility(GONE); + } else { + setVisibility(VISIBLE); + tvMsg.setText("没有更多数据"); + progressBar.setVisibility(GONE); + } + } +} + diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XDataObserver.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XDataObserver.java new file mode 100644 index 0000000..64b916f --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XDataObserver.java @@ -0,0 +1,47 @@ +package cn.droidlover.xrecyclerview; + + +import androidx.recyclerview.widget.RecyclerView; + +/** + * Created by wanglei on 2016/10/30. + */ + +public class XDataObserver extends RecyclerView.AdapterDataObserver { + + private XRecyclerAdapter adapter; + + public XDataObserver(XRecyclerAdapter adapter) { + this.adapter = adapter; + } + + @Override + public void onChanged() { + super.onChanged(); + adapter.notifyDataSetChanged(); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount) { + super.onItemRangeChanged(positionStart, itemCount); + adapter.notifyItemRangeChanged(positionStart + adapter.getHeaderSize(), itemCount); + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + super.onItemRangeInserted(positionStart, itemCount); + adapter.notifyItemRangeInserted(positionStart + adapter.getHeaderSize(), itemCount); + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + super.onItemRangeMoved(fromPosition, toPosition, itemCount); + adapter.notifyItemRangeChanged(fromPosition + adapter.getHeaderSize(), itemCount + adapter.getHeaderSize() + toPosition); + } + + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + super.onItemRangeRemoved(positionStart, itemCount); + adapter.notifyItemRangeRemoved(positionStart + adapter.getHeaderSize(), itemCount); + } +} diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XHeadFootViewHolder.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XHeadFootViewHolder.java new file mode 100644 index 0000000..b952179 --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XHeadFootViewHolder.java @@ -0,0 +1,18 @@ +package cn.droidlover.xrecyclerview; + +import android.view.View; +import androidx.recyclerview.widget.RecyclerView; + +/** + * Created by wanglei on 2016/10/30. + */ + +public class XHeadFootViewHolder extends RecyclerView.ViewHolder { + + public XHeadFootViewHolder(View itemView) { + super(itemView); + itemView.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)); + itemView.setTag(this); + } +} + diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerAdapter.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerAdapter.java new file mode 100644 index 0000000..1b796cb --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerAdapter.java @@ -0,0 +1,305 @@ +package cn.droidlover.xrecyclerview; + +import android.view.View; +import android.view.ViewGroup; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by wanglei on 2016/10/30. + */ + +public class XRecyclerAdapter extends RecyclerView.Adapter implements Cloneable { + private static final int ITEM_TYPE_HEADER = Integer.MAX_VALUE + 1; //headerView + private static final int ITEM_TYPE_NORMAL = ITEM_TYPE_HEADER + 1 * 1000; //normalItem + private static final int ITEM_TYPE_FOOTER = ITEM_TYPE_HEADER + 2 * 1000; //footerView + + private RecyclerView.Adapter baseAdapter; + private List headerViewList = new ArrayList(); + private List footerViewList = new ArrayList(); + private Map adapterClassMap; + + private RecyclerView.AdapterDataObserver observer = new XDataObserver(this); + + public XRecyclerAdapter(RecyclerView.Adapter baseAdapter) { + this(baseAdapter, null, null); + } + + public XRecyclerAdapter(RecyclerView.Adapter adapter, List headerViewList, List footerViewList) { + if (headerViewList != null && headerViewList.size() > 0) { + this.headerViewList.addAll(headerViewList); + } + if (footerViewList != null && footerViewList.size() > 0) { + this.footerViewList.addAll(footerViewList); + } + if (adapterClassMap == null) { + adapterClassMap = new HashMap(); + } + setAdapter(adapter); + } + + private void setAdapter(RecyclerView.Adapter adapter) { + if (adapter == null) { + return; + } + + if (baseAdapter != null) { + baseAdapter.unregisterAdapterDataObserver(observer); + } + baseAdapter = adapter; + Class clazz = baseAdapter.getClass(); + if (!adapterClassMap.containsKey(clazz)) { + putClass(clazz); + } + baseAdapter.registerAdapterDataObserver(observer); + } + + private void putClass(Class clazz) { + adapterClassMap.put(clazz, Integer.valueOf(ITEM_TYPE_FOOTER + 100 * adapterClassMap.size())); + } + + private int getMapClassIntValue() { + return adapterClassMap.get(baseAdapter.getClass()).intValue(); + } + + /** + * 数据是否为空 + * + * @return + */ + private boolean isEmpty() { + return getDataCount() == 0; + } + + /** + * 添加headview + * + * @param pos + * @param view + * @return + */ + public boolean addHeadView(int pos, View view) { + if (view == null || headerViewList.contains(view)) { + return false; + } + + headerViewList.add(pos, view); + notifyItemInserted(headerViewList.indexOf(Integer.valueOf(pos))); + return true; + } + + /** + * 添加headView + * + * @param view + * @return + */ + public boolean addHeadView(View view) { + return addHeadView(getHeaderSize(), view); + } + + /** + * 添加footView + * + * @param pos + * @param view + * @return + */ + public boolean addFootView(int pos, View view) { + if (view == null || footerViewList.contains(view)) { + return false; + } + + footerViewList.add(pos, view); + notifyItemInserted(getHeaderSize() + getDataCount() + pos); + + return true; + } + + public boolean addFootView(View view) { + return addFootView(getFooterSize(), view); + } + + /** + * 删除headView + * + * @param view + * @return + */ + public boolean removeHeadView(View view) { + if (view == null && !headerViewList.contains(view)) { + return false; + } + + int pos = headerViewList.indexOf(view); + boolean result = headerViewList.remove(view); + + if (result) { + notifyItemRemoved(pos); + if (view.getParent() != null) { + ((ViewGroup) view.getParent()).removeView(view); + } + } + return result; + } + + public boolean removeAllHeadersView() { + headerViewList.clear(); + notifyDataSetChanged(); + return true; + } + + public boolean removeAllFootView() { + footerViewList.clear(); + notifyDataSetChanged(); + return true; + } + + /** + * 删除footView + * + * @param view + * @return + */ + public boolean removeFootView(View view) { + boolean result = false; + + if (!footerViewList.contains(view)) { + result = false; + } + + int pos = footerViewList.indexOf(view); + if (pos > -1) { + result = footerViewList.remove(view); + } + if (result) { + notifyItemRemoved(1 + pos + getHeaderSize() + getDataCount()); + } + return result; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + int index = 0; + if (viewType < ITEM_TYPE_HEADER + getHeaderSize()) { + index = viewType + ITEM_TYPE_HEADER; + return new XHeadFootViewHolder(headerViewList.get(index)); + } + if ((getDataCount() > 0 && viewType < ITEM_TYPE_NORMAL + getDataCount()) + || viewType <= ITEM_TYPE_NORMAL + getFooterSize()) { + index = viewType - ITEM_TYPE_NORMAL; + if (index < footerViewList.size()) { + return new XHeadFootViewHolder(footerViewList.get(index)); + } + } + index = viewType - getMapClassIntValue(); + return baseAdapter.onCreateViewHolder(parent, index); + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + int headViewSize = getHeaderSize(); + if (position >= headViewSize && (position < headViewSize + getDataCount())) { + baseAdapter.onBindViewHolder(holder, position - headViewSize); + } + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List payloads) { + int headViewSize = getHeaderSize(); + if (position >= headViewSize && (position < headViewSize + getDataCount())) { + baseAdapter.onBindViewHolder(holder, position - headViewSize, payloads); + } + } + + @Override + public int getItemCount() { + return getHeaderSize() + getDataCount() + getFooterSize(); + } + + @Override + public int getItemViewType(int position) { + if (getDataCount() > 0) { + if (position < getHeaderSize()) { + return ITEM_TYPE_HEADER + position; + } + if (position < getHeaderSize() + getDataCount()) { + return getMapClassIntValue() + baseAdapter.getItemViewType(position - getHeaderSize()); + } + return ITEM_TYPE_NORMAL + position - getHeaderSize() - getDataCount(); + } else { + if (getHeaderSize() > 0 && position < getHeaderSize()) { + return ITEM_TYPE_HEADER + position; + } + return ITEM_TYPE_NORMAL + position - getHeaderSize(); + } + + } + + /** + * 判断当前位置是否是header or footer + * + * @param position + * @return + */ + public boolean isHeaderOrFooter(int position) { + return position < getHeaderSize() || position >= getHeaderSize() + getDataCount(); + } + + public int getDataCount() { + return baseAdapter.getItemCount(); + } + + /** + * 获取headView的数量 + * + * @return + */ + public int getHeaderSize() { + return headerViewList != null ? headerViewList.size() : 0; + } + + /** + * 获取footerView的数量 + * + * @return + */ + public int getFooterSize() { + return footerViewList != null ? footerViewList.size() : 0; + } + + public RecyclerView.Adapter getAdapter() { + return baseAdapter; + } + + @Override + protected Object clone() throws CloneNotSupportedException { + XRecyclerAdapter adapter = new XRecyclerAdapter(getAdapter()); + adapter.headerViewList = headerViewList; + adapter.footerViewList = footerViewList; + return adapter; + } + + /** + * 获取headViews + * + * @return + */ + public List getHeaderViewList() { + return headerViewList; + } + + /** + * 获取footViews + * + * @return + */ + public List getFooterViewList() { + return footerViewList; + } + +} diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerContentLayout.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerContentLayout.java new file mode 100644 index 0000000..dfd059b --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerContentLayout.java @@ -0,0 +1,165 @@ +package cn.droidlover.xrecyclerview; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.util.AttributeSet; + +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import cn.droidlover.xstatecontroller.XStateController; + +/** + * Created by wanglei on 2016/10/30. + */ + +public class XRecyclerContentLayout extends XStateController implements XRecyclerView.StateCallback, SwipeRefreshLayout.OnRefreshListener { + + private int padding; + private int paddingLeft; + private int paddingRight; + private int paddingTop; + private int paddingBottom; + private int scrollbarStyle; + private int backgroundColor; + private boolean clipToPadding; + private boolean scrollbarNone = false; + + SwipeRefreshLayout swipeRefreshLayout; + XRecyclerView recyclerView; + + + public XRecyclerContentLayout(Context context) { + this(context, null); + } + + public XRecyclerContentLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public XRecyclerContentLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + setupAttrs(context, attrs); + inflateView(context); + } + + private void setupAttrs(Context context, AttributeSet attrs) { + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.XRecyclerContentLayout); + + backgroundColor = typedArray.getColor(R.styleable.XRecyclerContentLayout_recyclerBackgroundColor, Color.WHITE); + padding = (int) typedArray.getDimension(R.styleable.XRecyclerContentLayout_recyclerPadding, -1.0f); + paddingLeft = (int) typedArray.getDimension(R.styleable.XRecyclerContentLayout_recyclerPaddingLeft, 0.0f); + paddingRight = (int) typedArray.getDimension(R.styleable.XRecyclerContentLayout_recyclerPaddingRight, 0.0f); + paddingTop = (int) typedArray.getDimension(R.styleable.XRecyclerContentLayout_recyclerPaddingTop, 0.0f); + paddingBottom = (int) typedArray.getDimension(R.styleable.XRecyclerContentLayout_recyclerPaddingBottom, 0.0f); + scrollbarStyle = typedArray.getInt(R.styleable.XRecyclerContentLayout_recyclerScrollbarStyle, 2); + clipToPadding = typedArray.getBoolean(R.styleable.XRecyclerContentLayout_recyclerClipToPadding, false); + scrollbarNone = typedArray.getBoolean(R.styleable.XRecyclerContentLayout_recyclerScrollbarNone, false); + + typedArray.recycle(); + } + + private void inflateView(Context context) { + inflate(context, R.layout.x_view_recycler_content_layout, this); + } + + @Override + protected void onFinishInflate() { + swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout); + recyclerView = (XRecyclerView) findViewById(R.id.recyclerView); + + swipeRefreshLayout.setEnabled(false); + swipeRefreshLayout.setColorSchemeResources( + R.color.x_red, + R.color.x_blue, + R.color.x_yellow, + R.color.x_green + ); + swipeRefreshLayout.setOnRefreshListener(this); + if (padding != -1) { + recyclerView.setPadding(padding, padding, padding, padding); + } else { + recyclerView.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); + } + recyclerView.setClipToPadding(clipToPadding); + + if (scrollbarNone) { + recyclerView.setVerticalScrollBarEnabled(false); + recyclerView.setHorizontalScrollBarEnabled(false); + } else { + recyclerView.setScrollBarStyle(scrollbarStyle); + } + + recyclerView.setBackgroundColor(backgroundColor); + + recyclerView.stateCallback(this); + + super.onFinishInflate(); + } + + @Override + public void setDisplayState(int displayState) { + XRecyclerAdapter adapter = recyclerView.getAdapter(); + if (adapter != null && adapter.getItemCount() > 0) { + super.setDisplayState(STATE_CONTENT); + return; + } + super.setDisplayState(displayState); + } + + public void setDisplayState(int state, boolean isForce) { + if (isForce) { + super.setDisplayState(state); + return; + } + setDisplayState(state); + } + + @Override + public void showEmpty() { + setDisplayState(STATE_EMPTY, true); + } + + @Override + public void showError() { + setDisplayState(STATE_ERROR, true); + } + + @Override + public void showLoading() { + setDisplayState(STATE_LOADING, true); + } + + @Override + public void notifyEmpty() { + showEmpty(); + } + + @Override + public void notifyContent() { + showContent(); + } + + @Override + public void refreshState(boolean isRefresh) { + swipeRefreshLayout.setRefreshing(isRefresh); + } + + @Override + public void refreshEnabled(boolean isEnabled) { + swipeRefreshLayout.setEnabled(isEnabled); + } + + @Override + public void onRefresh() { + recyclerView.onRefresh(); + } + + public XRecyclerView getRecyclerView() { + return recyclerView; + } + + public SwipeRefreshLayout getSwipeRefreshLayout() { + return swipeRefreshLayout; + } +} diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerView.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerView.java new file mode 100644 index 0000000..fa5944b --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerView.java @@ -0,0 +1,687 @@ +package cn.droidlover.xrecyclerview; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; + +import java.util.Collections; +import java.util.List; + +import androidx.annotation.ColorRes; +import androidx.annotation.DimenRes; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.*; +import cn.droidlover.xrecyclerview.divider.HorizontalDividerItemDecoration; +import cn.droidlover.xrecyclerview.divider.VerticalDividerItemDecoration; + +/** + * Created by wanglei on 2016/10/30. + */ + +public class XRecyclerView extends RecyclerView { + + private float xFactor = 1.0f; + private float yFactor = 1.0f; + + LayoutManagerType layoutManagerType; //LayoutManager类型 + private int[] lastScrollPositions; //瀑布流位置存储 + private int[] firstScrollPositions; + + LoadMoreUIHandler loadMoreUIHandler; + View loadMoreView; //加载更多控件 + + private boolean loadMore = false; + private int totalPage = 1; + private int currentPage = 1; + private boolean isRefresh = false; + private boolean isRefreshEnabled = true; //是否可刷新 + private int lastVelocityY = 0; + + XRecyclerAdapter adapter; + + StateCallback stateCallback; + OnRefreshAndLoadMoreListener onRefreshAndLoadMoreListener; + + public static final int LOAD_MORE_ITEM_SLOP = 2; + + public XRecyclerView(Context context) { + this(context, null); + } + + public XRecyclerView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public XRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setUpView(); + } + + private void setUpView() { + addOnScrollListener(processMoreListener); + } + + public void adapter(Adapter adapter) { + setAdapter(adapter); + } + + @Override + public void setAdapter(Adapter adapter) { + if (adapter == null) { + return; + } + + if (!(adapter instanceof XRecyclerAdapter)) { + adapter = new XRecyclerAdapter(adapter); + } + super.setAdapter(adapter); + if (adapter.getItemCount() > 0) { + if (getStateCallback() != null) { + getStateCallback().notifyContent(); + } + } + + final XRecyclerAdapter finalAdapter = (XRecyclerAdapter) adapter; + adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { + @Override + public void onChanged() { + super.onChanged(); + update(); + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + super.onItemRangeMoved(fromPosition, toPosition, itemCount); + update(); + } + + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + super.onItemRangeRemoved(positionStart, itemCount); + update(); + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + super.onItemRangeInserted(positionStart, itemCount); + update(); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount) { + super.onItemRangeChanged(positionStart, itemCount); + update(); + } + + private void update() { + int dataCount = finalAdapter.getDataCount(); + if (dataCount > 0) { + if (isRefresh) { + isRefresh = false; + } + if (loadMore) { + loadMoreCompleted(); + } + if (getStateCallback() != null) { + getStateCallback().notifyContent(); + } + } else { + if (finalAdapter.getHeaderSize() > 0 || finalAdapter.getFooterSize() > 0) { + if (loadMoreView != null) { + loadMoreView.setVisibility(GONE); + } + } else { + if (getStateCallback() != null) { + getStateCallback().notifyEmpty(); + } + } + + } + + if (getStateCallback() != null) { + getStateCallback().refreshState(false); + } + } + }); + + this.adapter = (XRecyclerAdapter) adapter; + + } + + public XRecyclerView stateCallback(StateCallback stateCallback) { + this.stateCallback = stateCallback; + return this; + } + + public StateCallback getStateCallback() { + return stateCallback; + } + + @Override + public XRecyclerAdapter getAdapter() { + return adapter; + } + + public void onRefresh() { + currentPage = 1; + isRefresh = true; + if (getOnRefreshAndLoadMoreListener() != null) { + getOnRefreshAndLoadMoreListener().onRefresh(); + } + } + + @Override + public void setLayoutManager(LayoutManager layout) { + if (layout == null) { + throw new IllegalArgumentException("LayoutManager can not be null."); + } + super.setLayoutManager(layout); + + if (layout instanceof GridLayoutManager) { + int spanCount = ((GridLayoutManager) layout).getSpanCount(); + setSpanLookUp(layout, spanCount); + } + + if (layout instanceof StaggeredGridLayoutManager) { + int spanCount = ((StaggeredGridLayoutManager) layout).getSpanCount(); + setSpanLookUp(layout, spanCount); + } + } + + @Override + public boolean fling(int velocityX, int velocityY) { + lastVelocityY = velocityY; + return super.fling((int) (velocityX * xFactor), (int) (velocityY * yFactor)); + } + + public XRecyclerView xFactor(float xFactor) { + this.xFactor = xFactor; + return this; + } + + public XRecyclerView yFactor(float yFactor) { + this.yFactor = yFactor; + return this; + } + + + public XRecyclerView verticalLayoutManager(Context context) { + LinearLayoutManager manager = new LinearLayoutManager(context); + manager.setOrientation(RecyclerView.VERTICAL); + setLayoutManager(manager); + return this; + } + + public XRecyclerView horizontalLayoutManager(Context context) { + LinearLayoutManager manager = new LinearLayoutManager(context); + manager.setOrientation(RecyclerView.HORIZONTAL); + setLayoutManager(manager); + return this; + } + + public XRecyclerView gridLayoutManager(Context context, int spanCount) { + GridLayoutManager manager = new GridLayoutManager(context, spanCount); + setLayoutManager(manager); + return this; + } + + public XRecyclerView verticalStaggeredLayoutManager(int spanCount) { + StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.VERTICAL); + setLayoutManager(manager); + return this; + } + + public XRecyclerView horizontalStaggeredLayoutManager(int spanCount) { + StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.VERTICAL); + setLayoutManager(manager); + return this; + } + + public int getLastVisibleItemPosition() { + return getLastVisibleItemPosition(getLayoutManager()); + } + + private int getLastVisibleItemPosition(RecyclerView.LayoutManager layoutManager) { + int lastVisibleItemPosition = -1; + if (layoutManagerType == null) { + if (layoutManager instanceof LinearLayoutManager) { + layoutManagerType = LayoutManagerType.LINEAR; + } else if (layoutManager instanceof GridLayoutManager) { + layoutManagerType = LayoutManagerType.GRID; + } else if (layoutManager instanceof StaggeredGridLayoutManager) { + layoutManagerType = LayoutManagerType.STAGGERED_GRID; + } else { + throw new RuntimeException("Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager"); + } + } + + switch (layoutManagerType) { + case LINEAR: + lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); + break; + case GRID: + lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition(); + break; + case STAGGERED_GRID: + StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager; + if (lastScrollPositions == null) { + lastScrollPositions = new int[staggeredGridLayoutManager.getSpanCount()]; + } + + staggeredGridLayoutManager.findLastVisibleItemPositions(lastScrollPositions); + lastVisibleItemPosition = findMax(lastScrollPositions); + break; + default: + break; + } + return lastVisibleItemPosition; + } + + + private int getFirstVisibleItemPosition(RecyclerView.LayoutManager layoutManager) { + int firstVisibleItemPosition = -1; + if (layoutManagerType == null) { + if (layoutManager instanceof LinearLayoutManager) { + layoutManagerType = LayoutManagerType.LINEAR; + } else if (layoutManager instanceof GridLayoutManager) { + layoutManagerType = LayoutManagerType.GRID; + } else if (layoutManager instanceof StaggeredGridLayoutManager) { + layoutManagerType = LayoutManagerType.STAGGERED_GRID; + } else { + throw new RuntimeException("Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager"); + } + } + + switch (layoutManagerType) { + case LINEAR: + firstVisibleItemPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); + break; + case GRID: + firstVisibleItemPosition = ((GridLayoutManager) layoutManager).findFirstVisibleItemPosition(); + break; + case STAGGERED_GRID: + StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager; + if (lastScrollPositions == null) { + lastScrollPositions = new int[staggeredGridLayoutManager.getSpanCount()]; + } + + staggeredGridLayoutManager.findLastVisibleItemPositions(firstScrollPositions); + firstVisibleItemPosition = findMin(firstScrollPositions); + break; + default: + break; + } + return firstVisibleItemPosition; + } + + + private int findMax(int[] lastPositions) { + int max = Integer.MIN_VALUE; + for (int value : lastPositions) { + if (value > max) { + max = value; + } + } + return max; + } + + private int findMin(int[] positions) { + int min = Integer.MIN_VALUE; + for (int value : positions) { + if (value < min) { + min = value; + } + } + return min; + } + + + private void setSpanLookUp(RecyclerView.LayoutManager layoutManager, final int spanCount) { + if (layoutManager instanceof GridLayoutManager) { + ((GridLayoutManager) layoutManager).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + if (adapter != null) { + return adapter.isHeaderOrFooter(position) ? spanCount : 1; + } + return GridLayoutManager.DEFAULT_SPAN_COUNT; + } + }); + } else if (layoutManager instanceof StaggeredGridLayoutManager) { + ((StaggeredGridLayoutManager) layoutManager).setSpanCount(spanCount); + } + + } + + /** + * 设置SpanSizeLookup + * + * @param layoutManager + * @param lookup + */ + public void setGridSpanLookUp(GridLayoutManager layoutManager, final GridLayoutManager.SpanSizeLookup lookup) { + layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + if (adapter != null) { + if (adapter.isHeaderOrFooter(position)) { + return 1; + } + + return lookup.getSpanSize(position); + } + return GridLayoutManager.DEFAULT_SPAN_COUNT; + } + }); + } + + public boolean addHeaderView(int position, View view) { + boolean result = false; + if (view == null) { + return result; + } + if (adapter != null) { + result = adapter.addHeadView(position, view); + } + return result; + } + + public boolean addHeaderView(View view) { + boolean result = false; + if (view == null) { + return result; + } + if (adapter != null) { + result = adapter.addHeadView(view); + } + return result; + } + + public boolean removeHeaderView(View view) { + boolean result = false; + if (view == null) { + return result; + } + if (adapter != null) { + result = adapter.removeHeadView(view); + } + return result; + } + + public boolean removeAllHeaderView() { + boolean result = false; + if (adapter != null) { + result = adapter.removeAllHeadersView(); + } + return result; + } + + public boolean addFooterView(View view) { + boolean result = false; + if (view == null) { + return result; + } + if (adapter != null) { + result = adapter.addFootView(view); + } + return result; + } + + public boolean addFooterView(int position, View view) { + boolean result = false; + if (view == null) { + return result; + } + if (adapter != null) { + result = adapter.addFootView(position, view); + } + return result; + } + + public boolean removeFooterView(View view) { + boolean result = false; + if (view == null) { + return result; + } + if (adapter != null) { + result = adapter.removeFootView(view); + } + return result; + } + + public boolean removeAllFootView() { + boolean result = false; + if (adapter != null) { + result = adapter.removeAllFootView(); + } + return result; + } + + public int getHeaderCount() { + int count = 0; + if (adapter != null) { + count = adapter.getHeaderSize(); + } + return count; + } + + public List getHeaderViewList() { + if (adapter != null) { + return adapter.getHeaderViewList(); + } + return Collections.EMPTY_LIST; + } + + public int getFooterCount() { + int count = 0; + if (adapter != null) { + return adapter.getFooterSize(); + } + return count; + } + + public List getFooterViewList() { + if (adapter != null) { + return adapter.getFooterViewList(); + } + return Collections.EMPTY_LIST; + } + + /** + * 使用默认的加载更多 + */ + public void useDefLoadMoreView() { + SimpleLoadMoreFooter loadMoreFooter = new SimpleLoadMoreFooter(getContext()); + setLoadMoreView(loadMoreFooter); + setLoadMoreUIHandler(loadMoreFooter); + } + + + public void loadMoreFooterView(View view) { + setLoadMoreView(view); + setLoadMoreUIHandler((LoadMoreUIHandler) view); + } + + /** + * 设置加载更多布局 + * + * @param view + */ + public void setLoadMoreView(View view) { + if (loadMoreView != null && loadMoreView != view) { + removeFooterView(view); + } + loadMoreView = view; + + addFooterView(view); + } + + public void setLoadMoreUIHandler(LoadMoreUIHandler loadMoreUIHandler) { + this.loadMoreUIHandler = loadMoreUIHandler; + } + + private void loadMoreCompleted() { + if (loadMoreUIHandler != null) { + loadMoreUIHandler.onLoadFinish(totalPage > currentPage); + } + loadMore = false; + if (getStateCallback() != null) { + changeRefreshEnableState(true); + getStateCallback().notifyContent(); + } + } + + public void setPage(final int currentPage, final int totalPage) { + this.currentPage = currentPage; + this.totalPage = totalPage; + if (loadMoreUIHandler != null) { + loadMoreUIHandler.onLoadFinish(totalPage > currentPage); + } + } + + /** + * 改变 刷新可用 的状态 + * + * @param isEnabled + */ + private void changeRefreshEnableState(boolean isEnabled) { + if (!isRefreshEnabled) { + return; + } + if (getStateCallback() != null) { + getStateCallback().refreshEnabled(isEnabled); + } + } + + /** + * 刷新数据 + */ + public void refreshData() { + if (getStateCallback() != null) { + getStateCallback().refreshState(true); + } + if (getOnRefreshAndLoadMoreListener() != null) { + getOnRefreshAndLoadMoreListener().onRefresh(); + } + } + + RecyclerView.OnScrollListener processMoreListener = new RecyclerView.OnScrollListener() { + + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + + if (adapter == null + || recyclerView.getLayoutManager() == null + || isRefresh) { + return; + } + + int totalCount = adapter.getItemCount(); + + if (newState == RecyclerView.SCROLL_STATE_IDLE + && !loadMore + && lastVelocityY > 0 + && getLastVisibleItemPosition(recyclerView.getLayoutManager()) + LOAD_MORE_ITEM_SLOP > totalCount) { + + if (totalPage > currentPage) { + loadMore = true; + + if (getOnRefreshAndLoadMoreListener() != null) { + getOnRefreshAndLoadMoreListener().onLoadMore(++currentPage); + + changeRefreshEnableState(false); + + if (loadMoreUIHandler != null) { + loadMoreUIHandler.onLoading(); + } + } + } else { + loadMoreCompleted(); + } + + } else { + changeRefreshEnableState(true); + } + + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + } + }; + + public XRecyclerView setOnRefreshAndLoadMoreListener(OnRefreshAndLoadMoreListener onRefreshAndLoadMoreListener) { + this.onRefreshAndLoadMoreListener = onRefreshAndLoadMoreListener; + changeRefreshEnableState(true); + return this; + } + + public XRecyclerView noDivider() { + setItemAnimator(new DefaultItemAnimator()); + setHasFixedSize(true); + return this; + } + + public XRecyclerView horizontalDivider(@ColorRes int colorRes, @DimenRes int dimenRes) { + setItemAnimator(new DefaultItemAnimator()); + setHasFixedSize(true); + addItemDecoration(new HorizontalDividerItemDecoration.Builder(getContext()) + .colorResId(colorRes) + .size(getContext().getResources().getDimensionPixelSize(dimenRes)) + .build() + ); + return this; + } + + public XRecyclerView verticalDivider(@ColorRes int colorRes, @DimenRes int dimenRes) { + setItemAnimator(new DefaultItemAnimator()); + setHasFixedSize(true); + addItemDecoration(new VerticalDividerItemDecoration.Builder(getContext()) + .colorResId(colorRes) + .size(getContext().getResources().getDimensionPixelSize(dimenRes)) + .build() + ); + return this; + } + + + public OnRefreshAndLoadMoreListener getOnRefreshAndLoadMoreListener() { + return onRefreshAndLoadMoreListener; + } + + public boolean isRefreshEnabled() { + return isRefreshEnabled; + } + + /** + * 设置是否可下拉刷新 + * + * @param refreshEnabled + */ + public void setRefreshEnabled(boolean refreshEnabled) { + isRefreshEnabled = refreshEnabled; + } + + + enum LayoutManagerType { + LINEAR, GRID, STAGGERED_GRID + } + + public interface StateCallback { + void notifyEmpty(); + + void notifyContent(); + + void refreshState(boolean isRefresh); + + void refreshEnabled(boolean isEnabled); + } + + public interface OnRefreshAndLoadMoreListener { + void onRefresh(); + + void onLoadMore(int page); + } +} \ No newline at end of file diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/FlexibleDividerDecoration.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/FlexibleDividerDecoration.java new file mode 100644 index 0000000..8cb6f7c --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/FlexibleDividerDecoration.java @@ -0,0 +1,342 @@ +package cn.droidlover.xrecyclerview.divider; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.view.View; + +import androidx.annotation.ColorRes; +import androidx.annotation.DimenRes; +import androidx.annotation.DrawableRes; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.RecyclerView; +import cn.droidlover.xrecyclerview.XRecyclerAdapter; + +/** + * Created by wanglei on 2016/10/30. + */ + +public abstract class FlexibleDividerDecoration extends RecyclerView.ItemDecoration { + + private static final int DEFAULT_SIZE = 2; + private static final int[] ATTRS = new int[]{ + android.R.attr.listDivider + }; + + protected enum DividerType { + DRAWABLE, PAINT, COLOR + } + + protected DividerType mDividerType = DividerType.DRAWABLE; + protected VisibilityProvider mVisibilityProvider; + protected PaintProvider mPaintProvider; + protected ColorProvider mColorProvider; + protected DrawableProvider mDrawableProvider; + protected SizeProvider mSizeProvider; + protected boolean mShowLastDivider; + private Paint mPaint; + + protected FlexibleDividerDecoration(Builder builder) { + if (builder.mPaintProvider != null) { + mDividerType = DividerType.PAINT; + mPaintProvider = builder.mPaintProvider; + } else if (builder.mColorProvider != null) { + mDividerType = DividerType.COLOR; + mColorProvider = builder.mColorProvider; + mPaint = new Paint(); + setSizeProvider(builder); + } else { + mDividerType = DividerType.DRAWABLE; + if (builder.mDrawableProvider == null) { + TypedArray a = builder.mContext.obtainStyledAttributes(ATTRS); + final Drawable divider = a.getDrawable(0); + a.recycle(); + mDrawableProvider = new DrawableProvider() { + @Override + public Drawable drawableProvider(int position, RecyclerView parent) { + return divider; + } + }; + } else { + mDrawableProvider = builder.mDrawableProvider; + } + mSizeProvider = builder.mSizeProvider; + } + + mVisibilityProvider = builder.mVisibilityProvider; + mShowLastDivider = builder.mShowLastDivider; + } + + private void setSizeProvider(Builder builder) { + mSizeProvider = builder.mSizeProvider; + if (mSizeProvider == null) { + mSizeProvider = new SizeProvider() { + @Override + public int dividerSize(int position, RecyclerView parent) { + return DEFAULT_SIZE; + } + }; + } + } + + @Override + public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { + int lastChildPosition = -1; + int childCount = mShowLastDivider ? parent.getChildCount() : parent.getChildCount() - 1; + for (int i = 0; i < childCount; i++) { + View child = parent.getChildAt(i); + int childPosition = parent.getChildAdapterPosition(child); + + if (childPosition < lastChildPosition) { + // Avoid remaining divider when animation starts + continue; + } + + + lastChildPosition = childPosition; + + if (ViewCompat.getAlpha(child) < 1) { + // Avoid remaining divider when animation starts + continue; + } + + if (mVisibilityProvider.shouldHideDivider(childPosition, parent)) { + continue; + } + + //headerView or footerView avoid remain divider + RecyclerView.Adapter adapter = parent.getAdapter(); + if (adapter != null) { + if (adapter instanceof XRecyclerAdapter) { + if (((XRecyclerAdapter) adapter).isHeaderOrFooter(childPosition)) { + continue; + } + } + } + + Rect bounds = getDividerBound(childPosition, parent, child); + switch (mDividerType) { + case DRAWABLE: + Drawable drawable = mDrawableProvider.drawableProvider(childPosition, parent); + drawable.setBounds(bounds); + drawable.draw(c); + break; + case PAINT: + mPaint = mPaintProvider.dividerPaint(childPosition, parent); + c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint); + break; + case COLOR: + mPaint.setColor(mColorProvider.dividerColor(childPosition, parent)); + mPaint.setStrokeWidth(mSizeProvider.dividerSize(childPosition, parent)); + c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint); + break; + default: + break; + } + } + } + + @Override + public void getItemOffsets(Rect rect, View v, RecyclerView parent, RecyclerView.State state) { + int position = parent.getChildAdapterPosition(v); + setItemOffsets(rect, position, parent); + } + + protected abstract Rect getDividerBound(int position, RecyclerView parent, View child); + + protected abstract void setItemOffsets(Rect outRect, int position, RecyclerView parent); + + /** + * Interface for controlling divider visibility + */ + public interface VisibilityProvider { + + /** + * Returns true if divider should be hidden. + * + * @param position Divider position + * @param parent RecyclerView + * @return True if the divider at position should be hidden + */ + boolean shouldHideDivider(int position, RecyclerView parent); + } + + /** + * Interface for controlling paint instance for divider drawing + */ + public interface PaintProvider { + + /** + * Returns {@link android.graphics.Paint} for divider + * + * @param position Divider position + * @param parent RecyclerView + * @return Paint instance + */ + Paint dividerPaint(int position, RecyclerView parent); + } + + /** + * Interface for controlling divider color + */ + public interface ColorProvider { + + /** + * Returns {@link android.graphics.Color} value of divider + * + * @param position Divider position + * @param parent RecyclerView + * @return Color value + */ + int dividerColor(int position, RecyclerView parent); + } + + /** + * Interface for controlling drawable object for divider drawing + */ + public interface DrawableProvider { + + /** + * Returns drawable instance for divider + * + * @param position Divider position + * @param parent RecyclerView + * @return Drawable instance + */ + Drawable drawableProvider(int position, RecyclerView parent); + } + + /** + * Interface for controlling divider size + */ + public interface SizeProvider { + + /** + * Returns size value of divider. + * Height for horizontal divider, width for vertical divider + * + * @param position Divider position + * @param parent RecyclerView + * @return Size of divider + */ + int dividerSize(int position, RecyclerView parent); + } + + public static class Builder { + + private Context mContext; + protected Resources mResources; + private PaintProvider mPaintProvider; + private ColorProvider mColorProvider; + private DrawableProvider mDrawableProvider; + private SizeProvider mSizeProvider; + private VisibilityProvider mVisibilityProvider = new VisibilityProvider() { + @Override + public boolean shouldHideDivider(int position, RecyclerView parent) { + return false; + } + }; + private boolean mShowLastDivider = false; + + public Builder(Context context) { + mContext = context; + mResources = context.getResources(); + } + + public T paint(final Paint paint) { + return paintProvider(new PaintProvider() { + @Override + public Paint dividerPaint(int position, RecyclerView parent) { + return paint; + } + }); + } + + public T paintProvider(PaintProvider provider) { + mPaintProvider = provider; + return (T) this; + } + + public T color(final int color) { + return colorProvider(new ColorProvider() { + @Override + public int dividerColor(int position, RecyclerView parent) { + return color; + } + }); + } + + public T colorResId(@ColorRes int colorId) { + return color(mResources.getColor(colorId)); + } + + public T colorProvider(ColorProvider provider) { + mColorProvider = provider; + return (T) this; + } + + public T drawable(@DrawableRes int id) { + return drawable(mResources.getDrawable(id)); + } + + public T drawable(final Drawable drawable) { + return drawableProvider(new DrawableProvider() { + @Override + public Drawable drawableProvider(int position, RecyclerView parent) { + return drawable; + } + }); + } + + public T drawableProvider(DrawableProvider provider) { + mDrawableProvider = provider; + return (T) this; + } + + public T size(final int size) { + return sizeProvider(new SizeProvider() { + @Override + public int dividerSize(int position, RecyclerView parent) { + return size; + } + }); + } + + public T sizeResId(@DimenRes int sizeId) { + return size(mResources.getDimensionPixelSize(sizeId)); + } + + public T sizeProvider(SizeProvider provider) { + mSizeProvider = provider; + return (T) this; + } + + public T visibilityProvider(VisibilityProvider provider) { + mVisibilityProvider = provider; + return (T) this; + } + + public T showLastDivider() { + mShowLastDivider = true; + return (T) this; + } + + protected void checkBuilderParams() { + if (mPaintProvider != null) { + if (mColorProvider != null) { + throw new IllegalArgumentException( + "Use setColor method of Paint class to specify line color. Do not provider ColorProvider if you set PaintProvider."); + } + if (mSizeProvider != null) { + throw new IllegalArgumentException( + "Use setStrokeWidth method of Paint class to specify line size. Do not provider SizeProvider if you set PaintProvider."); + } + } + } + } +} + diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/HorizontalDividerItemDecoration.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/HorizontalDividerItemDecoration.java new file mode 100644 index 0000000..890d367 --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/HorizontalDividerItemDecoration.java @@ -0,0 +1,144 @@ +package cn.droidlover.xrecyclerview.divider; + +import android.content.Context; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.view.View; +import androidx.annotation.DimenRes; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.RecyclerView; + +/** + * Created by wanglei on 2016/10/30. + */ + +public class HorizontalDividerItemDecoration extends FlexibleDividerDecoration { + + private MarginProvider mMarginProvider; + + protected HorizontalDividerItemDecoration(Builder builder) { + super(builder); + mMarginProvider = builder.mMarginProvider; + } + + @Override + protected Rect getDividerBound(int position, RecyclerView parent, View child) { + Rect bounds = new Rect(0, 0, 0, 0); + int transitionX = (int) ViewCompat.getTranslationX(child); + int transitionY = (int) ViewCompat.getTranslationY(child); + RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); + bounds.left = parent.getPaddingLeft() + + mMarginProvider.dividerLeftMargin(position, parent) + transitionX; + bounds.right = parent.getWidth() - parent.getPaddingRight() - + mMarginProvider.dividerRightMargin(position, parent) + transitionX; + + int dividerSize = getDividerSize(position, parent); + if (mDividerType == DividerType.DRAWABLE) { + bounds.top = child.getBottom() + params.topMargin + transitionY; + bounds.bottom = bounds.top + dividerSize; + } else { + bounds.top = child.getBottom() + params.topMargin + dividerSize / 2 + transitionY; + bounds.bottom = bounds.top; + } + + return bounds; + } + + @Override + protected void setItemOffsets(Rect outRect, int position, RecyclerView parent) { + outRect.set(0, 0, 0, getDividerSize(position, parent)); + } + + private int getDividerSize(int position, RecyclerView parent) { + if (mPaintProvider != null) { + return (int) mPaintProvider.dividerPaint(position, parent).getStrokeWidth(); + } else if (mSizeProvider != null) { + return mSizeProvider.dividerSize(position, parent); + } else if (mDrawableProvider != null) { + Drawable drawable = mDrawableProvider.drawableProvider(position, parent); + return drawable.getIntrinsicHeight(); + } + throw new RuntimeException("failed to get size"); + } + + /** + * Interface for controlling divider margin + */ + public interface MarginProvider { + + /** + * Returns left margin of divider. + * + * @param position Divider position + * @param parent RecyclerView + * @return left margin + */ + int dividerLeftMargin(int position, RecyclerView parent); + + /** + * Returns right margin of divider. + * + * @param position Divider position + * @param parent RecyclerView + * @return right margin + */ + int dividerRightMargin(int position, RecyclerView parent); + } + + public static class Builder extends FlexibleDividerDecoration.Builder { + + private MarginProvider mMarginProvider = new MarginProvider() { + @Override + public int dividerLeftMargin(int position, RecyclerView parent) { + return 0; + } + + @Override + public int dividerRightMargin(int position, RecyclerView parent) { + return 0; + } + }; + + public Builder(Context context) { + super(context); + } + + public Builder margin(final int leftMargin, final int rightMargin) { + return marginProvider(new MarginProvider() { + @Override + public int dividerLeftMargin(int position, RecyclerView parent) { + return leftMargin; + } + + @Override + public int dividerRightMargin(int position, RecyclerView parent) { + return rightMargin; + } + }); + } + + public Builder margin(int horizontalMargin) { + return margin(horizontalMargin, horizontalMargin); + } + + public Builder marginResId(@DimenRes int leftMarginId, @DimenRes int rightMarginId) { + return margin(mResources.getDimensionPixelSize(leftMarginId), + mResources.getDimensionPixelSize(rightMarginId)); + } + + public Builder marginResId(@DimenRes int horizontalMarginId) { + return marginResId(horizontalMarginId, horizontalMarginId); + } + + public Builder marginProvider(MarginProvider provider) { + mMarginProvider = provider; + return this; + } + + public HorizontalDividerItemDecoration build() { + checkBuilderParams(); + return new HorizontalDividerItemDecoration(this); + } + } +} + diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/VerticalDividerItemDecoration.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/VerticalDividerItemDecoration.java new file mode 100644 index 0000000..dc8477d --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/VerticalDividerItemDecoration.java @@ -0,0 +1,143 @@ +package cn.droidlover.xrecyclerview.divider; + +import android.content.Context; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.view.View; +import androidx.annotation.DimenRes; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.RecyclerView; + +/** + * Created by wanglei on 2016/10/30. + */ + +public class VerticalDividerItemDecoration extends FlexibleDividerDecoration { + + private MarginProvider mMarginProvider; + + protected VerticalDividerItemDecoration(Builder builder) { + super(builder); + mMarginProvider = builder.mMarginProvider; + } + + @Override + protected Rect getDividerBound(int position, RecyclerView parent, View child) { + Rect bounds = new Rect(0, 0, 0, 0); + int transitionX = (int) ViewCompat.getTranslationX(child); + int transitionY = (int) ViewCompat.getTranslationY(child); + RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); + bounds.top = parent.getPaddingTop() + + mMarginProvider.dividerTopMargin(position, parent) + transitionY; + bounds.bottom = parent.getHeight() - parent.getPaddingBottom() - + mMarginProvider.dividerBottomMargin(position, parent) + transitionY; + + int dividerSize = getDividerSize(position, parent); + if (mDividerType == DividerType.DRAWABLE) { + bounds.left = child.getRight() + params.leftMargin + transitionX; + bounds.right = bounds.left + dividerSize; + } else { + bounds.left = child.getRight() + params.leftMargin + dividerSize / 2 + transitionX; + bounds.right = bounds.left; + } + + return bounds; + } + + @Override + protected void setItemOffsets(Rect outRect, int position, RecyclerView parent) { + outRect.set(0, 0, getDividerSize(position, parent), 0); + } + + private int getDividerSize(int position, RecyclerView parent) { + if (mPaintProvider != null) { + return (int) mPaintProvider.dividerPaint(position, parent).getStrokeWidth(); + } else if (mSizeProvider != null) { + return mSizeProvider.dividerSize(position, parent); + } else if (mDrawableProvider != null) { + Drawable drawable = mDrawableProvider.drawableProvider(position, parent); + return drawable.getIntrinsicWidth(); + } + throw new RuntimeException("failed to get size"); + } + + /** + * Interface for controlling divider margin + */ + public interface MarginProvider { + + /** + * Returns top margin of divider. + * + * @param position Divider position + * @param parent RecyclerView + * @return top margin + */ + int dividerTopMargin(int position, RecyclerView parent); + + /** + * Returns bottom margin of divider. + * + * @param position Divider position + * @param parent RecyclerView + * @return bottom margin + */ + int dividerBottomMargin(int position, RecyclerView parent); + } + + public static class Builder extends FlexibleDividerDecoration.Builder { + + private MarginProvider mMarginProvider = new MarginProvider() { + @Override + public int dividerTopMargin(int position, RecyclerView parent) { + return 0; + } + + @Override + public int dividerBottomMargin(int position, RecyclerView parent) { + return 0; + } + }; + + public Builder(Context context) { + super(context); + } + + public Builder margin(final int topMargin, final int bottomMargin) { + return marginProvider(new MarginProvider() { + @Override + public int dividerTopMargin(int position, RecyclerView parent) { + return topMargin; + } + + @Override + public int dividerBottomMargin(int position, RecyclerView parent) { + return bottomMargin; + } + }); + } + + public Builder margin(int verticalMargin) { + return margin(verticalMargin, verticalMargin); + } + + public Builder marginResId(@DimenRes int topMarginId, @DimenRes int bottomMarginId) { + return margin(mResources.getDimensionPixelSize(topMarginId), + mResources.getDimensionPixelSize(bottomMarginId)); + } + + public Builder marginResId(@DimenRes int verticalMarginId) { + return marginResId(verticalMarginId, verticalMarginId); + } + + public Builder marginProvider(MarginProvider provider) { + mMarginProvider = provider; + return this; + } + + public VerticalDividerItemDecoration build() { + checkBuilderParams(); + return new VerticalDividerItemDecoration(this); + } + } +} diff --git a/xrecycler/src/main/res/layout/x_view_footer_load_more_simple.xml b/xrecycler/src/main/res/layout/x_view_footer_load_more_simple.xml new file mode 100644 index 0000000..3ced55f --- /dev/null +++ b/xrecycler/src/main/res/layout/x_view_footer_load_more_simple.xml @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/xrecycler/src/main/res/layout/x_view_recycler_content_layout.xml b/xrecycler/src/main/res/layout/x_view_recycler_content_layout.xml new file mode 100644 index 0000000..9a6038d --- /dev/null +++ b/xrecycler/src/main/res/layout/x_view_recycler_content_layout.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/xrecycler/src/main/res/values/attrs.xml b/xrecycler/src/main/res/values/attrs.xml new file mode 100644 index 0000000..72835cf --- /dev/null +++ b/xrecycler/src/main/res/values/attrs.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xrecycler/src/main/res/values/colors.xml b/xrecycler/src/main/res/values/colors.xml new file mode 100644 index 0000000..a5a4085 --- /dev/null +++ b/xrecycler/src/main/res/values/colors.xml @@ -0,0 +1,7 @@ + + + #FFC93437 + #FF375BF1 + #FFF7D23E + #FF34A350 + \ No newline at end of file diff --git a/xrecycler/src/main/res/values/strings.xml b/xrecycler/src/main/res/values/strings.xml new file mode 100644 index 0000000..6d51856 --- /dev/null +++ b/xrecycler/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Library + From 65223ee62e7cd96d546af2957d6085f64d25be46 Mon Sep 17 00:00:00 2001 From: Edit <1136160757@qq.com> Date: Wed, 27 Nov 2019 15:18:23 +0800 Subject: [PATCH 02/24] Update Readme.md --- Readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Readme.md b/Readme.md index 31ed840..d38d682 100644 --- a/Readme.md +++ b/Readme.md @@ -203,6 +203,7 @@ dependencies { ## 更新日志 +* 2019-11-27, 修改support依赖为AndroidX依赖 * 2017-04-23,新增proguard rules,upgrade to rx2 * 2016-12-23,新增mvp、base、cache、event、imageloader、log、router * 2016-12-25,新增rxJava、rxAndroid、rxlifecycle、rxpermission、rxbus、net(retrofit) From 97b01b46f1f516d186a1e5be8465f438ae07f407 Mon Sep 17 00:00:00 2001 From: Lennon <1136160757@qq.com> Date: Thu, 28 Nov 2019 11:03:27 +0800 Subject: [PATCH 03/24] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 8 ++++++++ app/src/main/res/layout/activity_main.xml | 8 ++++---- build.gradle | 15 ++++++++++++--- conf.gradle | 4 +++- mvp/build.gradle | 19 ++++++++++++++----- .../xdroidmvp/cache/DiskLruCache.java | 2 +- .../xdroidmvp/imageloader/GlideLoader.java | 2 +- .../cn/droidlover/xdroidmvp/kit/Kits.java | 7 +++++-- .../xdroidmvp/mvp/LazyFragment.java | 2 +- .../droidlover/xdroidmvp/mvp/XActivity.java | 2 +- .../droidlover/xdroidmvp/mvp/XFragment.java | 2 +- .../droidlover/xdroidmvp/router/Router.java | 2 +- xrecycler/build.gradle | 19 +++++++++++++++++-- 13 files changed, 69 insertions(+), 23 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 017d369..d650e23 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,6 +17,14 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + lintOptions { + abortOnError false + } + } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 44c6300..0f112f9 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,10 +1,10 @@ - - - + - \ No newline at end of file + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 0981c19..842d554 100644 --- a/build.gradle +++ b/build.gradle @@ -5,6 +5,7 @@ buildscript { repositories { jcenter() { url 'https://maven.aliyun.com/repository/jcenter' } jcenter() + maven {url 'https://dl.bintray.com/lennon-xia/lennon/'} maven { url 'https://maven.google.com/' name 'Google' @@ -14,18 +15,26 @@ buildscript { classpath 'com.android.tools.build:gradle:3.5.2' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' - classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.0' - + classpath 'com.novoda:bintray-release:0.9.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } - +ext{ + userOrg = 'lennon-xia' // 用户名 + repoName = "lennon" // 仓库名 + groupId = 'com.lennon.utill' // groupId + desc = 'useful tool for android' // 描述 + website = 'https://github.com/lennon19940306/XDroidMvp-AndroidX' // 地址 , 填写真实可访问地址 + licences = ['Apache-2.0']// 描述 , 填写licences +} allprojects { + repositories { jcenter() { url 'https://maven.aliyun.com/repository/jcenter' } mavenCentral() + maven {url 'https://dl.bintray.com/lennon-xia/lennon/'} maven { url 'https://maven.google.com/' name 'Google' diff --git a/conf.gradle b/conf.gradle index 920ff25..78864a7 100644 --- a/conf.gradle +++ b/conf.gradle @@ -35,7 +35,9 @@ ext { "glide" : "com.github.bumptech.glide:glide:4.9.0", "glide-compiler" : "com.github.bumptech.glide:compiler:4.9.0", "picasso" : "com.squareup.picasso:picasso:2.5.2", -// "xrecyclerview" : "com.github.limedroid:ARecyclerView:v1.2.3", + + "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.0", + "avi-loading" : "com.wang.avi:library:1.0.2", "rxbus" : "com.blankj:rxbus:1.2", diff --git a/mvp/build.gradle b/mvp/build.gradle index 4c66b05..a3642a5 100644 --- a/mvp/build.gradle +++ b/mvp/build.gradle @@ -1,7 +1,18 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' +apply plugin: 'com.novoda.bintray-release' -group = 'com.github.limedroid' +publish { + artifactId = 'xdroid-mvp' // artifactId + uploadName = 'XDroidMvp' // 展示名字 + publishVersion = '1.0.0' // 版本 + userOrg = rootProject.userOrg + groupId = rootProject.groupId + repoName = rootProject.repoName + desc = '' + website = rootProject.website + licences = rootProject.licences +} android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -19,7 +30,6 @@ android { } } } - dependencies { api fileTree(dir: 'libs', include: ['*.jar']) api rootProject.ext.dependencies["appcompat-v7"] @@ -28,8 +38,8 @@ dependencies { api rootProject.ext.dependencies["annotations"] api rootProject.ext.dependencies["recyclerview-v7"] -// api rootProject.ext.dependencies["xrecyclerview"] - api project(path: ':xrecycler') + api rootProject.ext.dependencies["xrecyclerview"] + api rootProject.ext.dependencies["butterknife"] api rootProject.ext.dependencies["glide"] annotationProcessor rootProject.ext.dependencies["glide-compiler"] @@ -57,7 +67,6 @@ task sourcesJar(type: Jar) { from android.sourceSets.main.java.srcDirs classifier = 'sources' } - artifacts { archives sourcesJar } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskLruCache.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskLruCache.java index 87b7acf..ac2024f 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskLruCache.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskLruCache.java @@ -16,7 +16,7 @@ package cn.droidlover.xdroidmvp.cache; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import java.io.BufferedInputStream; import java.io.BufferedWriter; diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/GlideLoader.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/GlideLoader.java index 778e75a..9116c25 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/GlideLoader.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/GlideLoader.java @@ -4,7 +4,7 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.widget.ImageView; import com.bumptech.glide.Glide; diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/kit/Kits.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/kit/Kits.java index de0d061..af523b3 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/kit/Kits.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/kit/Kits.java @@ -1,5 +1,6 @@ package cn.droidlover.xdroidmvp.kit; +import android.annotation.SuppressLint; import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; @@ -144,6 +145,7 @@ public static boolean isSystemApplication(Context context, String packageName) { * @param packageName * @return */ + @SuppressLint("NewApi") public static Boolean isTopActivity(Context context, String packageName) { if (context == null || TextUtils.isEmpty(packageName)) { return null; @@ -198,11 +200,12 @@ public static String getAppMetaData(Context context, String key) { * @param context * @return */ + @SuppressLint("NewApi") public static boolean isApplicationInBackground(Context context) { ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List taskList = am.getRunningTasks(1); if (taskList != null && !taskList.isEmpty()) { - ComponentName topActivity = taskList.get(0).topActivity; + ComponentName topActivity = taskList.get(0).topActivity; if (topActivity != null && !topActivity.getPackageName().equals(context.getPackageName())) { return true; } @@ -1011,7 +1014,7 @@ public static long getFirstOfMonth(long mills) { } - + @SuppressLint("MissingPermission") public static class NetWork { public static final String NETWORK_TYPE_WIFI = "wifi"; public static final String NETWORK_TYPE_3G = "eg"; diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java index f3c371b..bb09e92 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java @@ -3,7 +3,7 @@ import android.app.Activity; import android.content.Context; import android.os.Bundle; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java index cc3242a..5c58977 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java @@ -2,7 +2,7 @@ import android.app.Activity; import android.os.Bundle; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.view.Menu; import android.view.View; diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java index d737450..97e40f0 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java @@ -3,7 +3,7 @@ import android.app.Activity; import android.content.Context; import android.os.Bundle; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/router/Router.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/router/Router.java index 7fb53cb..d6e2c50 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/router/Router.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/router/Router.java @@ -4,11 +4,11 @@ import android.content.Intent; import android.os.Bundle; import android.os.Parcelable; -import android.support.annotation.Nullable; import java.io.Serializable; import java.util.ArrayList; +import androidx.annotation.Nullable; import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityOptionsCompat; import cn.droidlover.xdroidmvp.XDroidConf; diff --git a/xrecycler/build.gradle b/xrecycler/build.gradle index 54aae9c..45fc6e5 100644 --- a/xrecycler/build.gradle +++ b/xrecycler/build.gradle @@ -1,7 +1,18 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' - -group = 'com.github.limedroid' +//apply plugin: 'com.novoda.bintray-release' +// +//publish { +// artifactId = 'xrecycler' // artifactId +// uploadName = 'XRecycler' // 展示名字 +// publishVersion = '1.0.0' // 版本 +// userOrg = rootProject.userOrg +// groupId = rootProject.groupId +// repoName = rootProject.repoName +// desc = '' +// website = rootProject.website +// licences = rootProject.licences +//} android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -18,6 +29,10 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + lintOptions { + abortOnError false + } + } dependencies { From 43a337bfce0f8e28866b135a6f9e70dda09676e0 Mon Sep 17 00:00:00 2001 From: Edit <20336448+lennon19940306@users.noreply.github.com> Date: Thu, 28 Nov 2019 11:11:22 +0800 Subject: [PATCH 04/24] Set theme jekyll-theme-hacker --- _config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 _config.yml diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..fc24e7a --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-hacker \ No newline at end of file From 47cccc8a0fb08cb30360bef6f7e34d615619b7d9 Mon Sep 17 00:00:00 2001 From: Edit <20336448+lennon19940306@users.noreply.github.com> Date: Thu, 28 Nov 2019 11:11:34 +0800 Subject: [PATCH 05/24] Set theme jekyll-theme-hacker From a42763bccabca9a8ed9280100308a96b03a60f9b Mon Sep 17 00:00:00 2001 From: Lennon <1136160757@qq.com> Date: Wed, 11 Dec 2019 16:27:52 +0800 Subject: [PATCH 06/24] =?UTF-8?q?=E4=BF=AE=E5=A4=8Drecyclerview=20remove?= =?UTF-8?q?=E6=9C=80=E5=90=8E=E4=B8=80=E9=A1=B9=E5=88=97=E8=A1=A8=E9=A1=B9?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E4=BC=9A=E5=AF=BC=E8=87=B4=E5=B4=A9=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 2 +- app/src/main/res/layout/view_toolbar.xml | 4 +-- build.gradle | 6 +++-- conf.gradle | 4 ++- mvp/build.gradle | 25 ++++++++--------- xrecycler/build.gradle | 27 ++++++++++--------- .../xrecyclerview/RecyclerAdapter.java | 8 ++++-- .../xrecyclerview/XRecyclerView.java | 3 ++- 8 files changed, 45 insertions(+), 34 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index d650e23..953bfff 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -32,5 +32,5 @@ dependencies { api fileTree(dir: 'libs', include: ['*.jar']) annotationProcessor rootProject.ext.dependencies["butterknife-apt"] api rootProject.ext.dependencies["avi-loading"] - api project(":mvp") + api rootProject.ext.dependencies["xdroid-mvp"] } diff --git a/app/src/main/res/layout/view_toolbar.xml b/app/src/main/res/layout/view_toolbar.xml index e343e70..c04fd66 100644 --- a/app/src/main/res/layout/view_toolbar.xml +++ b/app/src/main/res/layout/view_toolbar.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 842d554..d7740f0 100644 --- a/build.gradle +++ b/build.gradle @@ -3,9 +3,10 @@ apply from: "conf.gradle" buildscript { repositories { + maven {url 'https://dl.bintray.com/lennon-xia/lennon/'} jcenter() { url 'https://maven.aliyun.com/repository/jcenter' } jcenter() - maven {url 'https://dl.bintray.com/lennon-xia/lennon/'} + maven { url 'https://maven.google.com/' name 'Google' @@ -32,9 +33,10 @@ ext{ allprojects { repositories { + maven {url 'https://dl.bintray.com/lennon-xia/lennon/'} jcenter() { url 'https://maven.aliyun.com/repository/jcenter' } mavenCentral() - maven {url 'https://dl.bintray.com/lennon-xia/lennon/'} + maven { url 'https://maven.google.com/' name 'Google' diff --git a/conf.gradle b/conf.gradle index 78864a7..f44bab4 100644 --- a/conf.gradle +++ b/conf.gradle @@ -18,6 +18,7 @@ ext { ] dependencies = [ + "appcompat-v7" : "androidx.appcompat:appcompat:${android["latest_integration"]}", "support-v4" : "androidx.legacy:legacy-support-v4:${android["latest_integration"]}", @@ -36,7 +37,8 @@ ext { "glide-compiler" : "com.github.bumptech.glide:compiler:4.9.0", "picasso" : "com.squareup.picasso:picasso:2.5.2", - "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.0", + "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.2", + "xdroid-mvp" : "com.lennon.utill:xdroid-mvp:1.0.0", "avi-loading" : "com.wang.avi:library:1.0.2", "rxbus" : "com.blankj:rxbus:1.2", diff --git a/mvp/build.gradle b/mvp/build.gradle index a3642a5..38589a7 100644 --- a/mvp/build.gradle +++ b/mvp/build.gradle @@ -1,18 +1,19 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' -apply plugin: 'com.novoda.bintray-release' -publish { - artifactId = 'xdroid-mvp' // artifactId - uploadName = 'XDroidMvp' // 展示名字 - publishVersion = '1.0.0' // 版本 - userOrg = rootProject.userOrg - groupId = rootProject.groupId - repoName = rootProject.repoName - desc = '' - website = rootProject.website - licences = rootProject.licences -} +//apply plugin: 'com.novoda.bintray-release' +// +//publish { +// artifactId = 'xdroid-mvp' // artifactId +// uploadName = 'XDroidMvp' // 展示名字 +// publishVersion = '1.0.0' // 版本 +// userOrg = rootProject.userOrg +// groupId = rootProject.groupId +// repoName = rootProject.repoName +// desc = '' +// website = rootProject.website +// licences = rootProject.licences +//} android { compileSdkVersion rootProject.ext.android.compileSdkVersion diff --git a/xrecycler/build.gradle b/xrecycler/build.gradle index 45fc6e5..4a6acca 100644 --- a/xrecycler/build.gradle +++ b/xrecycler/build.gradle @@ -1,18 +1,19 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' -//apply plugin: 'com.novoda.bintray-release' -// -//publish { -// artifactId = 'xrecycler' // artifactId -// uploadName = 'XRecycler' // 展示名字 -// publishVersion = '1.0.0' // 版本 -// userOrg = rootProject.userOrg -// groupId = rootProject.groupId -// repoName = rootProject.repoName -// desc = '' -// website = rootProject.website -// licences = rootProject.licences -//} + +apply plugin: 'com.novoda.bintray-release' + +publish { + artifactId = 'xrecycler' // artifactId + uploadName = 'XRecycler' // 展示名字 + publishVersion = '1.0.2' // 版本 + userOrg = rootProject.userOrg + groupId = rootProject.groupId + repoName = rootProject.repoName + desc = '' + website = rootProject.website + licences = rootProject.licences +} android { compileSdkVersion rootProject.ext.android.compileSdkVersion diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerAdapter.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerAdapter.java index e89c2e0..8408b26 100644 --- a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerAdapter.java +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerAdapter.java @@ -85,7 +85,9 @@ public void removeElement(T element) { int position = data.indexOf(element); data.remove(element); notifyItemRemoved(position); - notifyItemChanged(position); + if (position < data.size()) { + notifyItemChanged(position); + } } } @@ -98,7 +100,9 @@ public void removeElement(int position) { if (data != null && data.size() > position) { data.remove(position); notifyItemRemoved(position); - notifyItemChanged(position); + if (position < data.size()) { + notifyItemChanged(position); + } } } diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerView.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerView.java index fa5944b..109ab3d 100644 --- a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerView.java +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerView.java @@ -7,6 +7,7 @@ import java.util.Collections; import java.util.List; +import android.widget.Adapter; import androidx.annotation.ColorRes; import androidx.annotation.DimenRes; import androidx.annotation.Nullable; @@ -232,7 +233,7 @@ public XRecyclerView verticalStaggeredLayoutManager(int spanCount) { } public XRecyclerView horizontalStaggeredLayoutManager(int spanCount) { - StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.VERTICAL); + StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.HORIZONTAL); setLayoutManager(manager); return this; } From 475058821d00b3045be1b31ae11621a68f88f59b Mon Sep 17 00:00:00 2001 From: Lennon <1136160757@qq.com> Date: Wed, 11 Dec 2019 16:53:59 +0800 Subject: [PATCH 07/24] =?UTF-8?q?=E4=BF=AE=E5=A4=8Drecyclerview=20remove?= =?UTF-8?q?=E6=9C=80=E5=90=8E=E4=B8=80=E9=A1=B9=E5=88=97=E8=A1=A8=E9=A1=B9?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E4=BC=9A=E5=AF=BC=E8=87=B4=E5=B4=A9=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/droidlover/xrecyclerview/RecyclerAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerAdapter.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerAdapter.java index 8408b26..0ed585c 100644 --- a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerAdapter.java +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerAdapter.java @@ -86,7 +86,7 @@ public void removeElement(T element) { data.remove(element); notifyItemRemoved(position); if (position < data.size()) { - notifyItemChanged(position); + notifyItemChanged(position,data.size()-position); } } } @@ -101,7 +101,7 @@ public void removeElement(int position) { data.remove(position); notifyItemRemoved(position); if (position < data.size()) { - notifyItemChanged(position); + notifyItemChanged(position,data.size()-position); } } } From 5aefd7a5ec1d34da7e73486cd83ead57137419dc Mon Sep 17 00:00:00 2001 From: Lennon <1136160757@qq.com> Date: Wed, 11 Dec 2019 16:57:37 +0800 Subject: [PATCH 08/24] =?UTF-8?q?=E4=BF=AE=E5=A4=8Drecyclerview=20remove?= =?UTF-8?q?=E6=9C=80=E5=90=8E=E4=B8=80=E9=A1=B9=E5=88=97=E8=A1=A8=E9=A1=B9?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E4=BC=9A=E5=AF=BC=E8=87=B4=E5=B4=A9=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf.gradle | 2 +- xrecycler/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conf.gradle b/conf.gradle index f44bab4..98e9366 100644 --- a/conf.gradle +++ b/conf.gradle @@ -37,7 +37,7 @@ ext { "glide-compiler" : "com.github.bumptech.glide:compiler:4.9.0", "picasso" : "com.squareup.picasso:picasso:2.5.2", - "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.2", + "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.3", "xdroid-mvp" : "com.lennon.utill:xdroid-mvp:1.0.0", "avi-loading" : "com.wang.avi:library:1.0.2", diff --git a/xrecycler/build.gradle b/xrecycler/build.gradle index 4a6acca..bcb4e31 100644 --- a/xrecycler/build.gradle +++ b/xrecycler/build.gradle @@ -6,7 +6,7 @@ apply plugin: 'com.novoda.bintray-release' publish { artifactId = 'xrecycler' // artifactId uploadName = 'XRecycler' // 展示名字 - publishVersion = '1.0.2' // 版本 + publishVersion = '1.0.3' // 版本 userOrg = rootProject.userOrg groupId = rootProject.groupId repoName = rootProject.repoName From 78d10cd865868af7dd43ee3e7a761c7d90c49fbc Mon Sep 17 00:00:00 2001 From: lennon <1136160757@qq.com> Date: Wed, 25 Dec 2019 17:31:29 +0800 Subject: [PATCH 09/24] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 3 +- build.gradle | 5 +- conf.gradle | 21 +- mvp/build.gradle | 10 +- .../cn/droidlover/xdroidmvp/kit/KnifeKit.java | 45 - .../cn/droidlover/xdroidmvp/mvp/IView.java | 1 - .../xdroidmvp/mvp/LazyFragment.java | 4 +- .../droidlover/xdroidmvp/mvp/XActivity.java | 10 - .../droidlover/xdroidmvp/mvp/XFragment.java | 10 - .../xdroidmvp/mvp/XLazyFragment.java | 9 - settings.gradle | 2 +- test/build.gradle | 3 +- utill/build.gradle | 47 + utill/consumer-rules.pro | 0 utill/proguard-rules.pro | 21 + utill/src/main/AndroidManifest.xml | 12 + .../cn/utill/adapter/BaseViewAdapter.kt | 32 + .../cn/utill/adapter/StringItemAdapter.kt | 81 ++ .../com/lennon/cn/utill/base/BaseActivity.kt | 212 ++++ .../com/lennon/cn/utill/base/BaseApi.java | 50 + .../lennon/cn/utill/base/BaseApplication.kt | 382 ++++++++ .../com/lennon/cn/utill/base/BaseFragment.kt | 145 +++ .../com/lennon/cn/utill/base/BasePresent.java | 13 + .../java/com/lennon/cn/utill/base/BaseView.kt | 19 + .../lennon/cn/utill/bean/BaseViewBean.java | 14 + .../com/lennon/cn/utill/bean/Download.java | 73 ++ .../com/lennon/cn/utill/bean/HttpEntity.java | 67 ++ .../com/lennon/cn/utill/bean/StringBean.java | 9 + .../com/lennon/cn/utill/cache/ACache.java | 72 ++ .../com/lennon/cn/utill/cache/DataCache.java | 138 +++ .../lennon/cn/utill/cache/DataListCache.java | 163 ++++ .../java/com/lennon/cn/utill/conf/Lennon.kt | 106 ++ .../lennon/cn/utill/conf/LennonProvider.kt | 27 + .../dialog/BottomSearchSelectDialog.java | 257 +++++ .../cn/utill/dialog/BottomSelectDialog.java | 223 +++++ .../cn/utill/dialog/CommonAlertDialog.java | 169 ++++ .../cn/utill/dialog/CustomProgressDialog.java | 112 +++ .../utill/dialog/OnAlertDialogListener.java | 12 + .../com/lennon/cn/utill/utill/AmtUtils.java | 250 +++++ .../lennon/cn/utill/utill/AnimationUtil.java | 307 ++++++ .../cn/utill/utill/AppStatusManager.java | 48 + .../cn/utill/utill/AudioPlayerUtil.java | 86 ++ .../cn/utill/utill/AudioRecorderUtil.java | 196 ++++ .../lennon/cn/utill/utill/BitmapOperations.kt | 109 +++ .../lennon/cn/utill/utill/CountdownUtill.java | 57 ++ .../com/lennon/cn/utill/utill/DensityUtils.kt | 148 +++ .../com/lennon/cn/utill/utill/FileUtil.java | 859 +++++++++++++++++ .../lennon/cn/utill/utill/GetDeviceId.java | 271 ++++++ .../com/lennon/cn/utill/utill/IDCard.java | 196 ++++ .../com/lennon/cn/utill/utill/JsonUtils.java | 136 +++ .../java/com/lennon/cn/utill/utill/NetUtil.kt | 43 + .../com/lennon/cn/utill/utill/OSUtils.java | 352 +++++++ .../lennon/cn/utill/utill/PictureUtil.java | 189 ++++ .../lennon/cn/utill/utill/RandomUntil.java | 149 +++ .../lennon/cn/utill/utill/StatusBarUtil.java | 864 +++++++++++++++++ .../lennon/cn/utill/utill/StringUtils.java | 904 ++++++++++++++++++ .../cn/utill/utill/SystemBarTintManager.java | 97 ++ .../lennon/cn/utill/utill/TeminalDevice.java | 346 +++++++ .../lennon/cn/utill/utill/TimeCountUtil.java | 45 + .../com/lennon/cn/utill/utill/TimeUtill.kt | 34 + .../java/com/lennon/cn/utill/utill/Utill.kt | 317 ++++++ .../lennon/cn/utill/utill/UtillString.java | 9 + .../lennon/cn/utill/utill/VersionUtill.java | 131 +++ .../com/lennon/cn/utill/utill/ZipUtil.java | 166 ++++ .../cn/utill/utill/photo/PhotoString.java | 20 + .../cn/utill/utill/photo/SelectPicUtil.java | 254 +++++ .../lennon/cn/utill/utill/rsa/RSACoder.java | 258 +++++ .../lennon/cn/utill/utill/rsa/RSAKeyUtil.java | 92 ++ .../lennon/cn/utill/utill/rsa/RSAUtils.java | 373 ++++++++ .../com/lennon/cn/utill/utill/rx/RxTool.java | 68 ++ .../lennon/cn/utill/utill/time/DateStyle.java | 57 ++ .../lennon/cn/utill/utill/time/DateUtil.java | 771 +++++++++++++++ .../com/lennon/cn/utill/utill/time/Week.java | 43 + .../com/lennon/cn/utill/version/DownLoad.java | 109 +++ .../lennon/cn/utill/version/DownloadAPI.java | 76 ++ .../cn/utill/version/DownloadService.java | 18 + .../download/DownloadProgressInterceptor.java | 28 + .../download/DownloadProgressListener.java | 9 + .../DownloadProgressResponseBody.java | 61 ++ .../version/exception/CustomizeException.java | 11 + .../widget/AutoLineFeedLayoutManager.java | 53 + .../utill/widget/ClearWithSpaceEditText.java | 234 +++++ .../utill/widget/ContainsEmojiEditText.java | 155 +++ .../cn/utill/widget/DrawableTextView.java | 89 ++ .../cn/utill/widget/FlowLayoutManager.java | 157 +++ .../com/lennon/cn/utill/widget/HeadBar.java | 175 ++++ .../lennon/cn/utill/widget/MyRatingBar.java | 244 +++++ .../cn/utill/widget/SearchScanView.java | 224 +++++ .../lennon/cn/utill/widget/SearchView.java | 217 +++++ .../java/com/lennon/cn/utill/widget/Star.java | 178 ++++ .../widget/interf/OnHeadBarListener.java | 12 + utill/src/main/res/anim/pull_in_left.xml | 11 + utill/src/main/res/anim/pull_in_right.xml | 10 + utill/src/main/res/anim/push_out_left.xml | 10 + utill/src/main/res/anim/push_out_right.xml | 11 + utill/src/main/res/anim/pw_push_bottom_in.xml | 8 + .../src/main/res/anim/pw_push_bottom_out.xml | 8 + utill/src/main/res/anim/scale_in.xml | 15 + utill/src/main/res/anim/scale_out.xml | 15 + utill/src/main/res/drawable/btn_ac5454.xml | 5 + utill/src/main/res/drawable/btn_ac5454_f.xml | 7 + .../main/res/drawable/btn_bfbfbf_frame.xml | 17 + utill/src/main/res/drawable/common_button.xml | 5 + .../res/drawable/common_button_bfbfbf.xml | 5 + .../main/res/drawable/common_button_fffff.xml | 8 + .../main/res/drawable/common_dialog_frame.xml | 15 + utill/src/main/res/drawable/conner_f2f2f2.xml | 6 + utill/src/main/res/drawable/conner_fd0202.xml | 5 + utill/src/main/res/drawable/conner_ffffff.xml | 8 + .../main/res/drawable/conner_right_fd0202.xml | 7 + .../res/drawable/linearlayout_fffff_frame.xml | 15 + .../src/main/res/drawable/price_selector.xml | 7 + utill/src/main/res/drawable/table_line.xml | 7 + utill/src/main/res/drawable/table_radio.xml | 7 + utill/src/main/res/drawable/table_select.xml | 5 + .../src/main/res/drawable/table_unselect.xml | 5 + utill/src/main/res/drawable/web_progress.xml | 26 + .../main/res/layout/common_alert_dialog.xml | 130 +++ .../main/res/layout/customprogressdialog.xml | 24 + .../layout/dialog_bottom_search_select.xml | 51 + .../main/res/layout/dialog_bottom_select.xml | 44 + utill/src/main/res/layout/headbar.xml | 57 ++ utill/src/main/res/layout/item_base_view.xml | 21 + .../src/main/res/layout/item_string_item.xml | 20 + .../res/layout/search_scan_view_layout.xml | 70 ++ .../src/main/res/layout/searchview_layout.xml | 64 ++ .../res/mipmap-hdpi/ic_rating_bar_hollow.png | Bin 0 -> 3162 bytes .../res/mipmap-hdpi/ic_rating_bar_solid.png | Bin 0 -> 3166 bytes utill/src/main/res/mipmap-xhdpi/back.png | Bin 0 -> 1186 bytes utill/src/main/res/mipmap-xxhdpi/cha2.png | Bin 0 -> 2129 bytes utill/src/main/res/mipmap-xxhdpi/scan.png | Bin 0 -> 1919 bytes utill/src/main/res/raw/beep.ogg | Bin 0 -> 10185 bytes utill/src/main/res/values/attrs.xml | 50 + utill/src/main/res/values/color.xml | 43 + utill/src/main/res/values/dimens.xml | 73 ++ utill/src/main/res/values/strings.xml | 7 + utill/src/main/res/values/style.xml | 67 ++ utill/src/main/res/values/values.xml | 5 + xrecycler/build.gradle | 26 +- 139 files changed, 13772 insertions(+), 102 deletions(-) delete mode 100644 mvp/src/main/java/cn/droidlover/xdroidmvp/kit/KnifeKit.java create mode 100644 utill/build.gradle create mode 100644 utill/consumer-rules.pro create mode 100644 utill/proguard-rules.pro create mode 100644 utill/src/main/AndroidManifest.xml create mode 100644 utill/src/main/java/com/lennon/cn/utill/adapter/BaseViewAdapter.kt create mode 100644 utill/src/main/java/com/lennon/cn/utill/adapter/StringItemAdapter.kt create mode 100644 utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt create mode 100644 utill/src/main/java/com/lennon/cn/utill/base/BaseApi.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/base/BaseApplication.kt create mode 100644 utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt create mode 100644 utill/src/main/java/com/lennon/cn/utill/base/BasePresent.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt create mode 100644 utill/src/main/java/com/lennon/cn/utill/bean/BaseViewBean.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/bean/Download.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/bean/HttpEntity.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/bean/StringBean.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/cache/ACache.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/cache/DataCache.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/cache/DataListCache.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt create mode 100644 utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt create mode 100644 utill/src/main/java/com/lennon/cn/utill/dialog/BottomSearchSelectDialog.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/dialog/BottomSelectDialog.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/dialog/CommonAlertDialog.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/dialog/CustomProgressDialog.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/dialog/OnAlertDialogListener.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/AmtUtils.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/AnimationUtil.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/AppStatusManager.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/AudioPlayerUtil.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/AudioRecorderUtil.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/BitmapOperations.kt create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/CountdownUtill.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/FileUtil.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/GetDeviceId.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/IDCard.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/JsonUtils.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/NetUtil.kt create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/OSUtils.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/PictureUtil.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/RandomUntil.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/StatusBarUtil.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/StringUtils.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/SystemBarTintManager.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/TeminalDevice.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/TimeCountUtil.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/TimeUtill.kt create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/Utill.kt create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/UtillString.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/VersionUtill.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/ZipUtil.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/photo/PhotoString.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/photo/SelectPicUtil.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSACoder.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSAKeyUtil.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSAUtils.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/rx/RxTool.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/time/DateStyle.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/time/DateUtil.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/time/Week.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/version/DownLoad.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/version/DownloadAPI.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/version/DownloadService.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressInterceptor.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressListener.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressResponseBody.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/version/exception/CustomizeException.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/widget/AutoLineFeedLayoutManager.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/widget/ClearWithSpaceEditText.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/widget/ContainsEmojiEditText.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/widget/DrawableTextView.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/widget/FlowLayoutManager.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/widget/HeadBar.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/widget/MyRatingBar.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/widget/SearchScanView.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/widget/SearchView.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/widget/Star.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/widget/interf/OnHeadBarListener.java create mode 100644 utill/src/main/res/anim/pull_in_left.xml create mode 100644 utill/src/main/res/anim/pull_in_right.xml create mode 100644 utill/src/main/res/anim/push_out_left.xml create mode 100644 utill/src/main/res/anim/push_out_right.xml create mode 100644 utill/src/main/res/anim/pw_push_bottom_in.xml create mode 100644 utill/src/main/res/anim/pw_push_bottom_out.xml create mode 100644 utill/src/main/res/anim/scale_in.xml create mode 100644 utill/src/main/res/anim/scale_out.xml create mode 100644 utill/src/main/res/drawable/btn_ac5454.xml create mode 100644 utill/src/main/res/drawable/btn_ac5454_f.xml create mode 100644 utill/src/main/res/drawable/btn_bfbfbf_frame.xml create mode 100644 utill/src/main/res/drawable/common_button.xml create mode 100644 utill/src/main/res/drawable/common_button_bfbfbf.xml create mode 100644 utill/src/main/res/drawable/common_button_fffff.xml create mode 100644 utill/src/main/res/drawable/common_dialog_frame.xml create mode 100644 utill/src/main/res/drawable/conner_f2f2f2.xml create mode 100644 utill/src/main/res/drawable/conner_fd0202.xml create mode 100644 utill/src/main/res/drawable/conner_ffffff.xml create mode 100644 utill/src/main/res/drawable/conner_right_fd0202.xml create mode 100644 utill/src/main/res/drawable/linearlayout_fffff_frame.xml create mode 100644 utill/src/main/res/drawable/price_selector.xml create mode 100644 utill/src/main/res/drawable/table_line.xml create mode 100644 utill/src/main/res/drawable/table_radio.xml create mode 100644 utill/src/main/res/drawable/table_select.xml create mode 100644 utill/src/main/res/drawable/table_unselect.xml create mode 100644 utill/src/main/res/drawable/web_progress.xml create mode 100644 utill/src/main/res/layout/common_alert_dialog.xml create mode 100644 utill/src/main/res/layout/customprogressdialog.xml create mode 100644 utill/src/main/res/layout/dialog_bottom_search_select.xml create mode 100644 utill/src/main/res/layout/dialog_bottom_select.xml create mode 100644 utill/src/main/res/layout/headbar.xml create mode 100644 utill/src/main/res/layout/item_base_view.xml create mode 100644 utill/src/main/res/layout/item_string_item.xml create mode 100644 utill/src/main/res/layout/search_scan_view_layout.xml create mode 100644 utill/src/main/res/layout/searchview_layout.xml create mode 100644 utill/src/main/res/mipmap-hdpi/ic_rating_bar_hollow.png create mode 100644 utill/src/main/res/mipmap-hdpi/ic_rating_bar_solid.png create mode 100644 utill/src/main/res/mipmap-xhdpi/back.png create mode 100644 utill/src/main/res/mipmap-xxhdpi/cha2.png create mode 100644 utill/src/main/res/mipmap-xxhdpi/scan.png create mode 100644 utill/src/main/res/raw/beep.ogg create mode 100644 utill/src/main/res/values/attrs.xml create mode 100644 utill/src/main/res/values/color.xml create mode 100644 utill/src/main/res/values/dimens.xml create mode 100644 utill/src/main/res/values/strings.xml create mode 100644 utill/src/main/res/values/style.xml create mode 100644 utill/src/main/res/values/values.xml diff --git a/app/build.gradle b/app/build.gradle index 953bfff..3365b95 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -30,7 +30,6 @@ android { dependencies { api fileTree(dir: 'libs', include: ['*.jar']) - annotationProcessor rootProject.ext.dependencies["butterknife-apt"] api rootProject.ext.dependencies["avi-loading"] - api rootProject.ext.dependencies["xdroid-mvp"] + api "com.lennon.utill:xdroid-mvp:1.0.1" } diff --git a/build.gradle b/build.gradle index d7740f0..03cdf7a 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ apply from: "conf.gradle" buildscript { + ext.kotlin_version = '1.3.61' repositories { maven {url 'https://dl.bintray.com/lennon-xia/lennon/'} jcenter() { url 'https://maven.aliyun.com/repository/jcenter' } @@ -13,10 +14,10 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:3.5.2' + classpath 'com.android.tools.build:gradle:3.5.3' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' - classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.0' classpath 'com.novoda:bintray-release:0.9.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/conf.gradle b/conf.gradle index 98e9366..bf80f20 100644 --- a/conf.gradle +++ b/conf.gradle @@ -17,7 +17,23 @@ ext { latest_integration: "latest.integration", ] dependencies = [ + "navigation-fragment-ktx" : "androidx.navigation:navigation-fragment-ktx:2.1.0", + "navigation-ui-ktx" : "androidx.navigation:navigation-ui-ktx:2.1.0", + "navigation-ui" : "androidx.navigation:navigation-ui:2.1.0", + "navigation-fragment" : "androidx.navigation:navigation-fragment:2.1.0", + + "constraintlayout" : "androidx.constraintlayout:constraintlayout:${android["latest_integration"]}", + + "exifinterface" : "androidx.exifinterface:exifinterface:${android["latest_integration"]}", + + "multidex" : "androidx.multidex:multidex:${android["latest_integration"]}", + + "kotlin" : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version", + + "PersistentCookieJar" : "com.github.franmontiel:PersistentCookieJar:${android["latest_integration"]}", + + "qiujuer-ui" : "net.qiujuer.genius:ui:${android["latest_integration"]}", "appcompat-v7" : "androidx.appcompat:appcompat:${android["latest_integration"]}", @@ -29,16 +45,13 @@ ext { "recyclerview-v7" : "androidx.recyclerview:recyclerview:${android["latest_integration"]}", - "butterknife" : "com.jakewharton:butterknife:10.2.0", - "butterknife-apt" : "com.jakewharton:butterknife-compiler:10.2.0", - "eventbus" : "org.greenrobot:eventbus:3.0.0", "glide" : "com.github.bumptech.glide:glide:4.9.0", "glide-compiler" : "com.github.bumptech.glide:compiler:4.9.0", "picasso" : "com.squareup.picasso:picasso:2.5.2", "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.3", - "xdroid-mvp" : "com.lennon.utill:xdroid-mvp:1.0.0", + "xdroid-mvp" : "com.lennon.utill:xdroid-mvp:1.0.3", "avi-loading" : "com.wang.avi:library:1.0.2", "rxbus" : "com.blankj:rxbus:1.2", diff --git a/mvp/build.gradle b/mvp/build.gradle index 38589a7..410aa86 100644 --- a/mvp/build.gradle +++ b/mvp/build.gradle @@ -1,12 +1,16 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' +apply plugin: 'kotlin-android' + +apply plugin: 'kotlin-kapt' +apply plugin: 'kotlin-android-extensions' //apply plugin: 'com.novoda.bintray-release' // //publish { // artifactId = 'xdroid-mvp' // artifactId // uploadName = 'XDroidMvp' // 展示名字 -// publishVersion = '1.0.0' // 版本 +// publishVersion = '1.0.3' // 版本 // userOrg = rootProject.userOrg // groupId = rootProject.groupId // repoName = rootProject.repoName @@ -41,9 +45,9 @@ dependencies { api rootProject.ext.dependencies["xrecyclerview"] - api rootProject.ext.dependencies["butterknife"] api rootProject.ext.dependencies["glide"] annotationProcessor rootProject.ext.dependencies["glide-compiler"] + kapt rootProject.ext.dependencies["glide-compiler"] api rootProject.ext.dependencies["gson"] api rootProject.ext.dependencies["rxandroid"] @@ -58,6 +62,8 @@ dependencies { api rootProject.ext.dependencies["rxlifecycle-android"] api rootProject.ext.dependencies["rxlifecycle-components"] api rootProject.ext.dependencies["rxpermissions"] + + api rootProject.ext.dependencies["kotlin"] } tasks.withType(JavaCompile) { diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/kit/KnifeKit.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/kit/KnifeKit.java deleted file mode 100644 index 9b08959..0000000 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/kit/KnifeKit.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.droidlover.xdroidmvp.kit; - -import android.app.Activity; -import android.app.Dialog; -import android.view.View; - -import butterknife.ButterKnife; -import butterknife.Unbinder; - -/** - * Created by wanglei on 2016/11/27. - */ - -public class KnifeKit { - - public static Unbinder bind(Object target) { - if (target instanceof Activity) { - return ButterKnife.bind((Activity) target); - } else if (target instanceof Dialog) { - return ButterKnife.bind((Dialog) target); - } else if (target instanceof View) { - return ButterKnife.bind((View) target); - } - return Unbinder.EMPTY; - } - - - public static Unbinder bind(Object target, Object source) { - if (source instanceof Activity) { - return ButterKnife.bind(target, (Activity) source); - } else if (source instanceof Dialog) { - return ButterKnife.bind(target, (Dialog) source); - } else if (source instanceof View) { - return ButterKnife.bind(target, (View) source); - } - return Unbinder.EMPTY; - } - - - public static void unbind(Unbinder unbinder) { - if (unbinder != Unbinder.EMPTY) { - unbinder.unbind(); - } - } -} diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java index 56c3224..7c1d76e 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java @@ -8,7 +8,6 @@ */ public interface IView

{ - void bindUI(View rootView); void bindEvent(); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java index bb09e92..d5a5320 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java @@ -220,7 +220,9 @@ public void onDestroyView() { rootView = null; container = null; layoutInflater = null; - if (isInitReady) onDestoryLazy(); + if (isInitReady) { + onDestoryLazy(); + } isInitReady = false; } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java index 5c58977..ba1e88f 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java @@ -4,14 +4,11 @@ import android.os.Bundle; import androidx.annotation.Nullable; import android.view.Menu; -import android.view.View; import com.tbruyelle.rxpermissions2.RxPermissions; -import butterknife.Unbinder; import cn.droidlover.xdroidmvp.XDroidConf; import cn.droidlover.xdroidmvp.event.BusProvider; -import cn.droidlover.xdroidmvp.kit.KnifeKit; import com.trello.rxlifecycle3.components.support.RxAppCompatActivity; /** @@ -26,7 +23,6 @@ public abstract class XActivity

extends RxAppCompatActivity private RxPermissions rxPermissions; - private Unbinder unbinder; @Override @@ -38,18 +34,12 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { if (getLayoutId() > 0) { setContentView(getLayoutId()); - bindUI(null); bindEvent(); } initData(savedInstanceState); } - @Override - public void bindUI(View rootView) { - unbinder = KnifeKit.bind(this); - } - protected VDelegate getvDelegate() { if (vDelegate == null) { vDelegate = VDelegateBase.create(context); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java index 97e40f0..5d0dee2 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java @@ -10,10 +10,8 @@ import com.tbruyelle.rxpermissions2.RxPermissions; -import butterknife.Unbinder; import cn.droidlover.xdroidmvp.XDroidConf; import cn.droidlover.xdroidmvp.event.BusProvider; -import cn.droidlover.xdroidmvp.kit.KnifeKit; import com.trello.rxlifecycle3.components.support.RxFragment; /** @@ -30,8 +28,6 @@ public abstract class XFragment

extends RxFragment implement private RxPermissions rxPermissions; - private Unbinder unbinder; - @Nullable @Override @@ -39,7 +35,6 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa layoutInflater = inflater; if (rootView == null && getLayoutId() > 0) { rootView = inflater.inflate(getLayoutId(), null); - bindUI(rootView); } else { ViewGroup viewGroup = (ViewGroup) rootView.getParent(); if (viewGroup != null) { @@ -63,11 +58,6 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { initData(savedInstanceState); } - @Override - public void bindUI(View rootView) { - unbinder = KnifeKit.bind(this, rootView); - } - protected VDelegate getvDelegate() { if (vDelegate == null) { vDelegate = VDelegateBase.create(context); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java index 1482a54..e442fe6 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java @@ -5,10 +5,8 @@ import com.tbruyelle.rxpermissions2.RxPermissions; -import butterknife.Unbinder; import cn.droidlover.xdroidmvp.XDroidConf; import cn.droidlover.xdroidmvp.event.BusProvider; -import cn.droidlover.xdroidmvp.kit.KnifeKit; /** * Created by wanglei on 2017/1/26. @@ -21,7 +19,6 @@ public abstract class XLazyFragment

private P p; private RxPermissions rxPermissions; - private Unbinder unbinder; @Override protected void onCreateViewLazy(Bundle savedInstanceState) { @@ -31,7 +28,6 @@ protected void onCreateViewLazy(Bundle savedInstanceState) { if (getLayoutId() > 0) { setContentView(getLayoutId()); - bindUI(getRealRootView()); } if (useEventBus()) { BusProvider.getBus().register(this); @@ -40,11 +36,6 @@ protected void onCreateViewLazy(Bundle savedInstanceState) { initData(savedInstanceState); } - @Override - public void bindUI(View rootView) { - unbinder = KnifeKit.bind(this, rootView); - } - @Override public void bindEvent() { diff --git a/settings.gradle b/settings.gradle index ace07e0..ad346bd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':mvp', ':test',':xrecycler' +include ':app', ':mvp', ':test',':xrecycler',':utill' diff --git a/test/build.gradle b/test/build.gradle index 4383c2e..956030e 100644 --- a/test/build.gradle +++ b/test/build.gradle @@ -20,7 +20,6 @@ android { dependencies { api fileTree(dir: 'libs', include: ['*.jar']) - annotationProcessor rootProject.ext.dependencies["butterknife-apt"] - api project(":mvp") + api "com.lennon.utill:xdroid-mvp:1.0.1" } diff --git a/utill/build.gradle b/utill/build.gradle new file mode 100644 index 0000000..0723a40 --- /dev/null +++ b/utill/build.gradle @@ -0,0 +1,47 @@ +apply plugin: 'com.android.library' +apply plugin: 'com.github.dcendents.android-maven' + +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' + +apply plugin: 'com.novoda.bintray-release' +publish { + artifactId = 'lennon-utill' // artifactId + uploadName = 'LennonUtill' // 展示名字 + publishVersion = '1.0.3' // 版本 + userOrg = rootProject.userOrg + groupId = rootProject.groupId + repoName = rootProject.repoName + desc = '' + website = rootProject.website + licences = rootProject.licences +} +android { + compileSdkVersion rootProject.ext.android.compileSdkVersion + + defaultConfig { + minSdkVersion rootProject.ext.android.minSdkVersion + targetSdkVersion rootProject.ext.android.targetSdkVersion + versionCode rootProject.ext.android.versionCode + versionName rootProject.ext.android.versionName + } + + lintOptions { + abortOnError false + } +} +dependencies { + api fileTree(include: ['*.jar'], dir: 'libs') + api rootProject.ext.dependencies["xdroid-mvp"] + + api rootProject.ext.dependencies["PersistentCookieJar"] + api rootProject.ext.dependencies["qiujuer-ui"] + api rootProject.ext.dependencies["multidex"] + api rootProject.ext.dependencies["exifinterface"] + api rootProject.ext.dependencies["constraintlayout"] + + api rootProject.ext.dependencies["navigation-fragment-ktx"] + api rootProject.ext.dependencies["navigation-ui-ktx"] + api rootProject.ext.dependencies["navigation-ui"] + api rootProject.ext.dependencies["navigation-fragment"] +} diff --git a/utill/consumer-rules.pro b/utill/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/utill/proguard-rules.pro b/utill/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/utill/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/utill/src/main/AndroidManifest.xml b/utill/src/main/AndroidManifest.xml new file mode 100644 index 0000000..d4ae61b --- /dev/null +++ b/utill/src/main/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/utill/src/main/java/com/lennon/cn/utill/adapter/BaseViewAdapter.kt b/utill/src/main/java/com/lennon/cn/utill/adapter/BaseViewAdapter.kt new file mode 100644 index 0000000..4a7ed3c --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/adapter/BaseViewAdapter.kt @@ -0,0 +1,32 @@ +package com.lennon.cn.utill.adapter + +import android.content.Context +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import cn.droidlover.xdroidmvp.base.SimpleRecAdapter +import com.lennon.cn.utill.bean.BaseViewBean +import lennon.com.utill.R + +class BaseViewAdapter(context: Context) : SimpleRecAdapter(context) { + override fun newViewHolder(itemView: View): ViewHolder { + return ViewHolder(itemView) + } + + override fun getLayoutId(): Int { + return R.layout.item_base_view + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { +// super.onBindViewHolder(holder, position) + val d = data[position] + d.loadIcon(holder.icon) + d.loadName(holder.name) + } + + class ViewHolder(v: View) : RecyclerView.ViewHolder(v) { + val icon = v.findViewById(R.id.icon) + val name = v.findViewById(R.id.name) + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/adapter/StringItemAdapter.kt b/utill/src/main/java/com/lennon/cn/utill/adapter/StringItemAdapter.kt new file mode 100644 index 0000000..4bde0db --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/adapter/StringItemAdapter.kt @@ -0,0 +1,81 @@ +package com.lennon.cn.utill.adapter + +import android.content.Context +import android.graphics.Typeface +import android.util.TypedValue +import android.view.View +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import cn.droidlover.xdroidmvp.base.SimpleRecAdapter +import com.lennon.cn.utill.bean.StringBean +import com.lennon.cn.utill.utill.TeminalDevice +import com.lennon.cn.utill.utill.Utill +import lennon.com.utill.R + +/** + * Created by lennon on 2017/9/20. + */ + +open class StringItemAdapter(context: Context) : + SimpleRecAdapter.ViewHolder>(context) { + + private var textSize: List? = null + private var typefaces: List? = null + private var colors: List? = null + fun setTypefaces(typefaces: List) { + this.typefaces = typefaces + } + + fun setColors(colors: List) { + this.colors = colors + } + + fun setTextSize(textSize: List) { + this.textSize = textSize + } + + override fun newViewHolder(itemView: View): ViewHolder { + return ViewHolder(itemView) + } + + override fun getLayoutId(): Int { + return R.layout.item_string_item + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { +// super.onBindViewHolder(holder, position) + holder.name.text = data[position].itemString + holder.name.setBackgroundResource(R.color.color_ffffff) + holder.name.setTextColor(Utill.getColor(context.resources, R.color.color_0F83F0)) + if (typefaces != null && typefaces!!.isNotEmpty()) { + holder.name.typeface = typefaces!![position % typefaces!!.size] + } + if (colors != null && colors!!.isNotEmpty()) { + holder.name.setBackgroundColor(Utill.getColor(context.resources, colors!![position % colors!!.size])) + } + if (textSize != null && textSize!!.isNotEmpty()) { + holder.name.textSize = TeminalDevice.dip2px( + context, + textSize!![position % textSize!!.size].toFloat() + ).toFloat() + } + } + + + inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + internal var name = itemView.findViewById(R.id.name) as TextView + + fun upBack(resource: Int, textColor: Int) { + name.setBackgroundResource(resource) + name.setTextColor(Utill.getColor(context.resources, textColor)) + } + + fun unselect() { + upBack(R.drawable.conner_f2f2f2, R.color.color_333333) + } + + fun select() { + upBack(R.drawable.conner_fd0202, R.color.color_ffffff) + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt new file mode 100644 index 0000000..d6eaa52 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt @@ -0,0 +1,212 @@ +package com.lennon.cn.utill.base + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import cn.droidlover.xdroidmvp.log.XLog +import cn.droidlover.xdroidmvp.mvp.XActivity +import cn.droidlover.xdroidmvp.net.NetError + +import com.lennon.cn.utill.conf.Lennon +import com.lennon.cn.utill.dialog.CommonAlertDialog +import com.lennon.cn.utill.dialog.CustomProgressDialog +import com.lennon.cn.utill.dialog.OnAlertDialogListener +import com.lennon.cn.utill.utill.AppStatusManager +import com.lennon.cn.utill.utill.StatusBarUtil +import com.lennon.cn.utill.utill.Utill +import java.lang.Exception + + +abstract class BaseActivity

> : XActivity

(), BaseView

{ + private var TAG = javaClass.simpleName + private var dialog: CustomProgressDialog? = null + + /** * 设置当前窗口亮度 * @param brightness */ + fun setWindowBrightness(brightness: Float) { + val window = window + val lp = window.attributes + lp.screenBrightness = brightness + window.attributes = lp + } + + /** + * 获取当前屏幕亮度 + */ + fun getBrightness(): Int { + val lp = window?.attributes + if (lp != null) { + //screenBrightness 默认为-1 + if (lp.screenBrightness < 0) return 0 + return (lp.screenBrightness * 255).toInt() + } + return 0 + } + + override fun showProgressDialog(msg: String) { + if (dialog != null) dialog!!.dismiss() + dialog = CustomProgressDialog(getContext()) + dialog!!.setMessage(msg) + dialog!!.show() + } + + override fun useEventBus(): Boolean { + return true + } + + override fun closeProgressDialog() { + if (null != dialog) dialog!!.dismiss() + } + + override fun toast(msg: String, second: Int) { + val dialog = CommonAlertDialog(this) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + } + }) + Toast.makeText(getContext(), msg, second) + .show() + } + + override fun toast(msg: String) { + toast(msg, true) + } + + override fun toast(msg: String, runnable: Runnable) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + runnable.run() + } + }) + Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) + .show() + } + + override fun toast(msg: String, flag: Boolean) { + if (flag) { + val dialog = CommonAlertDialog(this) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + } + }) + } + Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) + .show() + } + + override fun showLoading(visibility: Int) { + } + + override fun showLoadingError(errorType: NetError) { + + } + + override fun getContext(): Context { + return this + } + + override fun onRefresh(bRefresh: Boolean) { + XLog.e("$TAG:onRefresh()") + } + + + override fun onCreate(savedInstanceState: Bundle?) { + XLog.e("$TAG:onCreate") +// Thread.setDefaultUncaughtExceptionHandler(CrashHandler(this)) + Lennon.useDensity(this) +// window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + super.onCreate(savedInstanceState) + if (checkAppStatus()) { + BaseApplication.restart(this) + finish() + } else { + BaseApplication.addActivity(this) + try { + if ((this.findViewById(android.R.id.content) as ViewGroup).getChildAt(0) != null) { + StatusBarUtil.setColorNoTranslucent( + this, + Utill.getColor(resources, getMTitleColor()) + ) + // StatusBarUtil.setImmersiveStatusBar(this, false) + } + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + open fun getMTitleColor(): Int { + return Lennon.getTitleColor() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + XLog.e("$TAG:onActivityResult($requestCode,$resultCode)") + super.onActivityResult(requestCode, resultCode, data) + } + + + override fun onStart() { + super.onStart() + XLog.e("$TAG:onStart()") + } + + override fun onDestroy() { + BaseApplication.exitActivity(this) + XLog.e("$TAG:onDestroy()") + super.onDestroy() + } + + override fun onPause() { + XLog.e("$TAG:onPause()") + super.onPause() + } + + override fun onResume() { + XLog.e("$TAG:onResume()") + super.onResume() + } + + override fun onBackPressed() { + XLog.e("$TAG:onBackPressed()") + super.onBackPressed() + } + + override fun onPostResume() { + XLog.e("$TAG:onPostResume()") + super.onPostResume() + } + + override fun onStop() { + XLog.e("$TAG:onStop()") + super.onStop() + } + + private fun checkAppStatus(): Boolean { + XLog.e(TAG + ":" + AppStatusManager.getInstance().appStatus) + return AppStatusManager.getInstance().appStatus == AppStatusManager.AppStatusConstant.APP_FORCE_KILLED + } + + override fun getActivity(): Activity { + return this + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseApi.java b/utill/src/main/java/com/lennon/cn/utill/base/BaseApi.java new file mode 100644 index 0000000..538cded --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseApi.java @@ -0,0 +1,50 @@ +package com.lennon.cn.utill.base; + + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import cn.droidlover.xdroidmvp.net.IModel; +import cn.droidlover.xdroidmvp.net.XApi; +import io.reactivex.Flowable; + +/** + * Created by lennon on 2017/8/10. + */ + +public abstract class BaseApi { + public T service; + protected Class tClass; + + public BaseApi(String url) { + tClass = (Class) getModelClass(0); + try { + service = XApi.getInstance().getRetrofit(url, true).create(tClass); + } catch (Exception e) { + e.printStackTrace(); + BaseApplication.Companion.registerProvider(); + service = XApi.getInstance().getRetrofit(url, true).create(tClass); + } + } + + public final Flowable compose(Flowable flowable) { + return flowable.compose(XApi.getApiTransformer()) + .compose(XApi.getScheduler()); + } + + //泛型类作为父类,可以获取子类的所有实际参数的类型 + @SuppressWarnings("unchecked") + public Class getModelClass(int index) { + Class modelClass; + // 得到泛型父类 + Type genType = this.getClass().getGenericSuperclass(); + //一个泛型类可能有多个泛型形参,比如ClassName 这里有两个泛型形参T和K,Class Name 这里只有1个泛型形参T + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + if (params.length - 1 < index) { + modelClass = null; + } else { + modelClass = (Class) params[index]; + } + return modelClass; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseApplication.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseApplication.kt new file mode 100644 index 0000000..6f3df81 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseApplication.kt @@ -0,0 +1,382 @@ +package com.lennon.cn.utill.base + +import android.app.Activity +import android.content.Context +import android.content.res.Configuration +import android.content.res.Resources +import android.text.TextUtils +import androidx.multidex.MultiDex +import androidx.multidex.MultiDexApplication +import cn.droidlover.xdroidmvp.cache.SharedPref +import cn.droidlover.xdroidmvp.log.XLog +import cn.droidlover.xdroidmvp.net.NetError +import cn.droidlover.xdroidmvp.net.NetProvider +import cn.droidlover.xdroidmvp.net.RequestHandler +import cn.droidlover.xdroidmvp.net.XApi +import com.franmontiel.persistentcookiejar.PersistentCookieJar +import com.franmontiel.persistentcookiejar.cache.SetCookieCache +import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor +import com.lennon.cn.utill.conf.Lennon +import com.lennon.cn.utill.utill.DensityUtils +import com.lennon.cn.utill.utill.TimeUtill +import com.lennon.cn.utill.utill.Utill +import lennon.com.utill.BuildConfig +import okhttp3.* +import java.io.BufferedReader +import java.io.FileReader +import java.util.* + +/** + * Created by lennon on 2017/7/26. + */ +open class BaseApplication : MultiDexApplication() { + + override fun attachBaseContext(base: Context) { + super.attachBaseContext(base) + MultiDex.install(base) + } + + override fun onConfigurationChanged(newConfig: Configuration) { + if (newConfig.fontScale != 1f) + //非默认值 + resources + super.onConfigurationChanged(newConfig) + } + + override fun getResources(): Resources { + val res = super.getResources() + if (res.configuration.fontScale != 1f) {//非默认值 + val newConfig = Configuration() + newConfig.setToDefaults()//设置默认 + res.updateConfiguration(newConfig, res.displayMetrics) + } + return res + } + + open fun init() { + registerProvider() +// val context = applicationContext + // 获取当前包名 +// val packageName = context.packageName + // 获取当前进程名 +// val processName = getProcessName(android.os.Process.myPid()) + // 设置是否为上报进程 +// val strategy = CrashReport.UserStrategy(context) +// strategy.isUploadProcess = processName == null || processName == packageName + // 初始化Bugly +// CrashReport.initCrashReport(context, Lennon.getCrashID(), Lennon.isTest()) +// NLoggerConfig.getInstance() +// .builder() +// .tag("APP") +// .loggerLevel(LoggerLevel.INFO) +// .fileLogger(true) +// .fileDirectory(getDataFile() + "/logs") +// .fileFormatter(SimpleFormatter()) +// .expiredPeriod(7) +// .catchException(true) { _, throwable -> +// getCuttureActivity()?.finish() +// val t = if (throwable != null) { +// throwable +// } else { +// Throwable("未知异常") +// } +// t.printStackTrace() +// getCuttureActivity()!!.runOnUiThread { +// // 使用Toast来显示异常信息 +// object : Thread() { +// override fun run() { +// Looper.prepare() +// Toast.makeText( +// getCuttureActivity(), +// "很抱歉,程序出现异常,即将退出,请前往 我的 上传日志并联系客服:" + t.message, +// Toast.LENGTH_LONG +// ).show() +// Looper.loop() +// } +// }.start() +// +// Toast.makeText( +// getCuttureActivity(), +// "非常抱歉,程序出错了,请前往 我的 上传日志并联系客服:" + t.message, +// Toast.LENGTH_SHORT +// ).show() +// XLog.e("uncaughtException", t.message) +// Lennon.restartApp() +// AppExit() +// } +// }.build() + } + + override fun onCreate() { + DensityUtils.setDensity(this) + appliction = this + super.onCreate() + + } + + companion object { + private var list: ArrayList? = null + private val loger = true + private var appliction: BaseApplication? = null + private var cookiePersistor: SharedPrefsCookiePersistor? = null + private var cookieJar: PersistentCookieJar? = null + private var test = true + /** + * 获取进程号对应的进程名 + * + * @param pid 进程号 + * @return 进程名 + */ + fun getProcessName(pid: Int): String? { + var reader: BufferedReader? = null + try { + reader = BufferedReader(FileReader("/proc/" + pid + "/cmdline")) + var processName = reader.readLine() + if (!TextUtils.isEmpty(processName)) { + processName = processName.trim() + } + return processName + } catch (throwable: Throwable) { + throwable.printStackTrace() + } finally { + try { + if (reader != null) { + reader.close() + } + } catch (exception: Exception) { + exception.printStackTrace() + } + } + return null + } + +// fun init() { +// NLoggerConfig.getInstance() +// .builder() +// .tag("APP") +// .loggerLevel(LoggerLevel.INFO) +// .fileLogger(true) +// .fileDirectory(getDataFile() + "/logs") +// .fileFormatter(SimpleFormatter()) +// .expiredPeriod(7) +// .catchException(true) { _, throwable -> +// getCuttureActivity()?.finish() +// val t = if (throwable != null) { +// throwable +// } else { +// Throwable("未知异常") +// } +// t.printStackTrace() +//// if (getCuttureActivity() != null) { +// getCuttureActivity()!!.runOnUiThread { +// // 使用Toast来显示异常信息 +// object : Thread() { +// override fun run() { +// Looper.prepare() +// Toast.makeText( +// getCuttureActivity(), +// "很抱歉,程序出现异常,即将退出,请联系客服:" + t.message, +// Toast.LENGTH_LONG +// ).show() +// Looper.loop() +// } +// }.start() +// // val dialog = CommonAlertDialog(getCuttureActivity()) +//// dialog.setTitle("非常抱歉") +//// dialog.setCancelable(false) +//// dialog.setCanceledOnTouchOutside(false) +//// dialog.setMsg("程序出错了,请联系客服:" + t.message) +//// dialog.setDialogListener(object : OnAlertDialogListener() { +//// override fun onSure() { +//// super.onSure() +//// dialog.dismiss() +//// XLog.e("uncaughtException", throwable!!) +//// Lennon.restartApp() +//// AppExit() +//// } +//// }) +//// dialog.show() +//// } +//// } else { +// Toast.makeText( +// getCuttureActivity(), +// "非常抱歉,程序出错了,请联系客服:" + t.message, +// Toast.LENGTH_SHORT +// ).show() +// XLog.e("uncaughtException", t.message) +// Lennon.restartApp() +// AppExit() +// } +//// android.os.Process.killProcess(android.os.Process.myPid()) +// }.build() +// +// } + + fun isLoger(): Boolean { + return loger + } + + fun context(): Context? { + return appliction + } + + fun registerProvider() { + if (cookieJar == null) { + cookiePersistor = SharedPrefsCookiePersistor(appliction!!) + cookieJar = PersistentCookieJar(SetCookieCache(), cookiePersistor!!) + } + XApi.registerProvider(object : NetProvider { + + override fun configInterceptors(): Array { + return arrayOfNulls(0) + } + + override fun configHttps(builder: OkHttpClient.Builder) { + + } + + override fun configCookie(): CookieJar? { + return cookieJar + } + + override fun configHandler(): RequestHandler { + return object : RequestHandler { + private var sendTime = 0L + private var url = "" + override fun onBeforeRequest( + request: Request, + chain: Interceptor.Chain + ): Request? { + val builder = chain.request().newBuilder() + builder.addHeader("Content-Type", "application/json;charset=utf-8") + builder.addHeader("Accept", "application/json;charset=utf-8") + sendTime = System.currentTimeMillis() + url = request.url().toString() + try { + return builder.build() + } catch (e: Exception) { + e.printStackTrace() + } + return null + } + + override fun onAfterRequest( + response: Response, + chain: Interceptor.Chain + ): Response { + if (getCuttureActivity() != null) { + val end = System.currentTimeMillis() + val mss = end - sendTime + XLog.e("请求" + url + "耗时" + TimeUtill.formatDuring(mss)) + } + return response + } + } + } + + override fun configConnectTimeoutMills(): Long { + return 60000 + } + + override fun configReadTimeoutMills(): Long { + return 60000 + } + + override fun configLogEnable(): Boolean { + return BaseApplication.isLoger() + } + + override fun handleError(error: NetError): Boolean { + return Lennon.handleNetError(error) + } + + override fun dispatchProgressEnable(): Boolean { + return false + } + }) + } + + fun getDataFile(): String { + Utill.makeDir(Lennon.getFilePathName()) + return Lennon.getFilePathName() + } + + val cookie: List + get() = cookiePersistor!!.loadAll() + + fun clearCookies() { + cookieJar!!.clear() + } + + fun setCookies(cookie: List) {} + fun addActivity(activity: Activity) { + if (list == null) + list = ArrayList() + list!!.add(activity) + } + + fun exitActivity(activity: Activity?) { + if (list != null) { +// list!!.remove(activity) + list!!.remove(activity) + } + } + + + fun getCuttureActivity(): Activity? { + return if (list == null || list!!.size == 0) null else list!![list!!.size - 1] + } + + + fun AppExit() { + Lennon.appExit() + for (activity in list!!) { + activity?.finish() + } + System.exit(0) + } + + fun clean() { + Lennon.clean() + SharedPref.getInstance(context()).clear() + } + + + fun resources(): Resources { + return context()!!.resources + } + + + fun finshActivity() { + for (activity in list!!) { + activity.finish() + } + } + + fun restart(context: Context) { + if (list != null) + list!!.clear() + getAppliction()?.restart(context) + } + + fun getAppliction(): BaseApplication? { + return appliction + } + + fun isTest(): Boolean { + return test && BuildConfig.DEBUG + } + + fun addCookie(cookie: Cookie) { + val l = cookiePersistor!!.loadAll() + l.add(cookie) + cookiePersistor!!.saveAll(l) + } + } + + open fun restart(context: Context) { + + } + + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt new file mode 100644 index 0000000..990c16c --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt @@ -0,0 +1,145 @@ +package com.lennon.cn.utill.base + +import android.app.Activity +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import cn.droidlover.xdroidmvp.log.XLog + +import cn.droidlover.xdroidmvp.mvp.XFragment +import com.lennon.cn.utill.dialog.CommonAlertDialog +import com.lennon.cn.utill.dialog.CustomProgressDialog +import com.lennon.cn.utill.dialog.OnAlertDialogListener + +abstract class BaseFragment

> : XFragment

(), BaseView

{ + private var rootView: View? = null + private var dialog: CustomProgressDialog? = null + private var mActivity: Activity? = null + override fun showLoading(visibility: Int) { + } + + override fun useEventBus(): Boolean { + return true + } + + private var isFirst = true + private var stop = false + + + override fun onRefresh(bRefresh: Boolean) { + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + XLog.e(getName() + " onCreateView") + rootView = super.onCreateView(inflater, container, savedInstanceState) + return rootView + } + + + override fun showProgressDialog(msg: String) { + if (dialog != null) + dialog!!.dismiss() + dialog = CustomProgressDialog(getContext()) + dialog!!.setMessage(msg) + dialog!!.show() + } + + + override fun onResume() { + super.onResume() + XLog.e(getName() + " onResume") + } + + + protected fun getName(): String { + return javaClass.simpleName + } + + override fun closeProgressDialog() { + if (null != dialog) + dialog!!.dismiss() + } + + override fun toast(msg: String) { + toast(msg, true) + } + + override fun toast(msg: String, runnable: Runnable) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + runnable.run() + } + }) + Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) + .show() + } + + override fun toast(msg: String, flag: Boolean) { + if (flag) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + } + }) + } + Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) + .show() + } + + override fun toast(msg: String, second: Int) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + } + }) + Toast.makeText(getContext(), msg, second).show() + } + + override fun onStop() { + XLog.e(getName() + " onStop") + stop = true + super.onStop() + } + + override fun onAttach(activity: Activity) { + XLog.e(getName() + " onAttach") + super.onAttach(activity) + mActivity = activity + } + + override fun onPause() { + XLog.e(getName() + " onPause") + super.onPause() + } + + override fun getContext(): Context? { + return if (mActivity != null) { + mActivity + } else { + BaseApplication.getCuttureActivity() + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BasePresent.java b/utill/src/main/java/com/lennon/cn/utill/base/BasePresent.java new file mode 100644 index 0000000..fca5aac --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/base/BasePresent.java @@ -0,0 +1,13 @@ +package com.lennon.cn.utill.base; + +import cn.droidlover.xdroidmvp.mvp.IView; +import cn.droidlover.xdroidmvp.mvp.XPresent; +import org.jetbrains.annotations.Nullable; + +public abstract class BasePresent extends XPresent { + + public BasePresent(V v){ + attachV(v); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt new file mode 100644 index 0000000..288f5c8 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt @@ -0,0 +1,19 @@ +package com.lennon.cn.utill.base + +import android.app.Activity +import android.content.Context +import cn.droidlover.xdroidmvp.net.NetError + +interface BaseView

?> { + fun showProgressDialog(msg: String) + fun closeProgressDialog() + fun toast(msg: String, second: Int) + fun toast(msg: String) + fun toast(msg: String, runnable: Runnable) + fun toast(msg: String, flag: Boolean) + fun showLoading(visibility: Int) + fun showLoadingError(errorType: NetError) + fun getContext(): Context? + fun onRefresh(bRefresh: Boolean) + fun getActivity(): Activity? +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/bean/BaseViewBean.java b/utill/src/main/java/com/lennon/cn/utill/bean/BaseViewBean.java new file mode 100644 index 0000000..d36d0ff --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/bean/BaseViewBean.java @@ -0,0 +1,14 @@ +package com.lennon.cn.utill.bean; + +import android.widget.ImageView; +import android.widget.TextView; + +public interface BaseViewBean { + public void onClick(); + + public void onLongClick(); + + public void loadIcon(ImageView icon); + + public void loadName(TextView name); +} diff --git a/utill/src/main/java/com/lennon/cn/utill/bean/Download.java b/utill/src/main/java/com/lennon/cn/utill/bean/Download.java new file mode 100644 index 0000000..83b908f --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/bean/Download.java @@ -0,0 +1,73 @@ +package com.lennon.cn.utill.bean; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.Serializable; + +/** + * Created by JokAr on 16/7/5. + */ +public class Download implements Parcelable,Serializable { + + private int progress; + private long currentFileSize; + private long totalFileSize; + + public int getProgress() { + return progress; + } + + public void setProgress(int progress) { + this.progress = progress; + } + + public long getCurrentFileSize() { + return currentFileSize; + } + + public void setCurrentFileSize(long currentFileSize) { + this.currentFileSize = currentFileSize; + } + + public long getTotalFileSize() { + return totalFileSize; + } + + public void setTotalFileSize(long totalFileSize) { + this.totalFileSize = totalFileSize; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(this.progress); + dest.writeLong(this.currentFileSize); + dest.writeLong(this.totalFileSize); + } + + public Download() { + } + + protected Download(Parcel in) { + this.progress = in.readInt(); + this.currentFileSize = in.readLong(); + this.totalFileSize = in.readLong(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Download createFromParcel(Parcel source) { + return new Download(source); + } + + @Override + public Download[] newArray(int size) { + return new Download[size]; + } + }; +} diff --git a/utill/src/main/java/com/lennon/cn/utill/bean/HttpEntity.java b/utill/src/main/java/com/lennon/cn/utill/bean/HttpEntity.java new file mode 100644 index 0000000..7aa1b84 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/bean/HttpEntity.java @@ -0,0 +1,67 @@ +package com.lennon.cn.utill.bean; + +import java.io.Serializable; +import java.util.Collection; + +import cn.droidlover.xdroidmvp.net.IModel; + +/** + * Created by dingyi on 2016/11/30. + */ + +public class HttpEntity implements IModel, Serializable { + private String msg; + private String code; + private T data; + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public boolean isSuccess() { + return "0000".equals(code); + } + + @Override + public boolean isNull() { + if (!"0000".equals(code)) { + return data == null && !"1000".equals(code); + } + return false; + } + + @Override + public boolean isAuthError() { + return "1000".equals(code); + } + + @Override + public boolean isBizError() { + return !"0000".equals(code); + } + + @Override + public String getErrorMsg() { + return msg; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/bean/StringBean.java b/utill/src/main/java/com/lennon/cn/utill/bean/StringBean.java new file mode 100644 index 0000000..41520f1 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/bean/StringBean.java @@ -0,0 +1,9 @@ +package com.lennon.cn.utill.bean; + +/** + * Created by lennon on 2018/5/7. + */ + +public interface StringBean { + String getItemString(); +} diff --git a/utill/src/main/java/com/lennon/cn/utill/cache/ACache.java b/utill/src/main/java/com/lennon/cn/utill/cache/ACache.java new file mode 100644 index 0000000..caaed21 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/cache/ACache.java @@ -0,0 +1,72 @@ +package com.lennon.cn.utill.cache; + +import android.content.Context; + +import cn.droidlover.xdroidmvp.cache.DiskCache; +import cn.droidlover.xdroidmvp.cache.ICache; +import cn.droidlover.xdroidmvp.cache.MemoryCache; + +public class ACache implements ICache { + DiskCache diskCache; + MemoryCache memoryCache; + private static ACache aCache; + + private ACache(Context context) { + diskCache = DiskCache.getInstance(context); + memoryCache = MemoryCache.getInstance(); + } + + public static ACache getInstance(Context context) { + if (aCache == null) { + aCache = new ACache(context); + } + return aCache; + + } + +// @Override +// public synchronized void put(String key, String value) { +// memoryCache.put(key, value); +// diskCache.put(key, value); +// } + + @Override + public void put(String key, Object value) { + memoryCache.put(key, value); + diskCache.put(key, value); + } + + @Override + public String get(String key) { + try { + if (memoryCache.contains(key)) { + return memoryCache.get(key).toString(); + } else if (diskCache.contains(key)) { + String s = diskCache.get(key); + memoryCache.put(key, s); + return s; + } + return ""; + } catch (Exception e) { + e.printStackTrace(); + return ""; + } + } + + @Override + public void remove(String key) { + memoryCache.remove(key); + diskCache.remove(key); + } + + @Override + public boolean contains(String key) { + return memoryCache.contains(key) || diskCache.contains(key); + } + + @Override + public void clear() { + memoryCache.clear(); + diskCache.clear(); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/cache/DataCache.java b/utill/src/main/java/com/lennon/cn/utill/cache/DataCache.java new file mode 100644 index 0000000..695fdb6 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/cache/DataCache.java @@ -0,0 +1,138 @@ +package com.lennon.cn.utill.cache; + +import android.text.TextUtils; + +import com.google.gson.Gson; +import com.lennon.cn.utill.base.BaseApplication; +import com.lennon.cn.utill.base.BaseView; +import com.lennon.cn.utill.bean.HttpEntity; +import com.lennon.cn.utill.conf.Lennon; +import com.trello.rxlifecycle3.LifecycleProvider; + +import java.lang.reflect.Type; + +import androidx.annotation.NonNull; +import cn.droidlover.xdroidmvp.cache.SharedPref; +import cn.droidlover.xdroidmvp.mvp.IView; +import cn.droidlover.xdroidmvp.net.ApiSubscriber; +import cn.droidlover.xdroidmvp.net.NetError; +import cn.droidlover.xdroidmvp.net.XApi; +import io.reactivex.Flowable; + +/** + * Created by lennon on 2017/10/18. + */ + +public class DataCache { + BaseView activity; + String dataKey; + Flowable flowable; + LifecycleProvider lifecycleProvider; + DataCallBack dataCallBack; + ACache maCache; + protected Type tClass; + private boolean refresh = false; + + public static void initDataCache(String DataCacheKey) { + SharedPref.getInstance(BaseApplication.Companion.context()).putString("DataCacheKey", DataCacheKey); + } + + public DataCache(BaseView activity, String dataKey, Flowable flowable, + LifecycleProvider lifecycleProvider, DataCallBack dataCallBack, Type tClass) { + this.tClass = tClass; + this.activity = activity; + this.dataKey = dataKey; + this.flowable = flowable; + this.lifecycleProvider = lifecycleProvider; + this.dataCallBack = dataCallBack; + this.maCache = ACache.getInstance(BaseApplication.Companion.context()); + } + + public void getData() { + T t = getLocalData(); + if (t == null) { + activity.showProgressDialog("加载中……"); + } else { + if (dataCallBack != null) { + dataCallBack.upView(t); + } + } + getDataFromNet(); + } + + public void refresh() { + refresh = true; + getDataFromNet(); + } + + String getKey(String a) { + return a + SharedPref.getInstance(BaseApplication.Companion.context()).getString("DataCacheKey", ""); + } + + void saveData(T t) { + maCache.put(getKey(dataKey), new Gson().toJson(t)); + } + + T getLocalData() { + if (TextUtils.isEmpty(maCache.get(getKey(dataKey)))) { + return null; + } + + T t = new Gson().fromJson(maCache.get(getKey(dataKey)), tClass); + return t; + } + + void getDataFromNet() { + flowable.compose(XApi.getApiTransformer()) + .compose(XApi.getScheduler()) + .compose(lifecycleProvider.bindToLifecycle()) + .subscribe(new ApiSubscriber() { + @Override + protected void onFail(NetError error) { + if (getLocalData() != null) { + dataCallBack.netError(error); + } else { + dataCallBack.netErrorForNoData(error); + } + activity.closeProgressDialog(); + if (error.getType() == NetError.AuthError) { + activity.toast("登陆失效", new Runnable() { + @Override + public void run() { + Lennon.Companion.requserLogin(); + } + }); + return; + } + } + + @Override + public void onNext(T o) { + if (getLocalData() != null) { + String a = new Gson().toJson(getLocalData()); + String b = new Gson().toJson(o); + if (a.equals(b) && refresh) { + dataCallBack.upView(o); + } else if (a.equals(b) && !refresh) { + + } else { + dataCallBack.upView(o); + saveData(o); + } + } else { + dataCallBack.upView(o); + saveData(o); + } + activity.closeProgressDialog(); + } + }); + } + + public interface DataCallBack { + void upView(@NonNull T t); + + void netErrorForNoData(@NonNull NetError error); + + void netError(@NonNull NetError error); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/cache/DataListCache.java b/utill/src/main/java/com/lennon/cn/utill/cache/DataListCache.java new file mode 100644 index 0000000..6f14ec9 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/cache/DataListCache.java @@ -0,0 +1,163 @@ +package com.lennon.cn.utill.cache; + +import android.text.TextUtils; + +import cn.droidlover.xdroidmvp.net.NetError; +import com.google.gson.Gson; +import com.lennon.cn.utill.base.BaseApplication; +import com.lennon.cn.utill.base.BaseView; +import com.lennon.cn.utill.bean.HttpEntity; +import com.lennon.cn.utill.conf.Lennon; +import com.trello.rxlifecycle3.LifecycleProvider; + +import java.lang.reflect.Type; + +import androidx.annotation.NonNull; +import cn.droidlover.xdroidmvp.cache.SharedPref; +import cn.droidlover.xdroidmvp.net.ApiSubscriber; +import cn.droidlover.xdroidmvp.net.XApi; +import io.reactivex.Flowable; + +/** + * @author lennon + * @date 2017/10/18 + */ + +public class DataListCache { + BaseView activity; + String dataKey; + LifecycleProvider lifecycleProvider; + DataCallBack dataCallBack; + ACache maCache; + int startPage = 1; + int page = 0; + protected Type tClass; + private boolean refresh = false; + + public static void initDataCache(String DataCacheKey) { + SharedPref.getInstance(BaseApplication.Companion.context()).putString("DataCacheKey", DataCacheKey); + } + + public DataListCache(BaseView activity, String dataKey, int startPage, + LifecycleProvider lifecycleProvider, DataCallBack dataCallBack, Type tClass) { + this.tClass = tClass; + this.activity = activity; + this.dataKey = dataKey; + this.startPage = startPage; + this.lifecycleProvider = lifecycleProvider; + this.dataCallBack = dataCallBack; + this.maCache = ACache.getInstance(BaseApplication.Companion.context()); + } + + private void getData(int page) { + T t = getLocalData(page); + if (t == null) { + activity.showProgressDialog("加载中……"); + } else { + if (dataCallBack != null) { + dataCallBack.upView(t, page); + } + } + getDataFromNet(page); + } + + public void getData() { + getData(startPage); + } + + public void refresh() { + refresh = true; + getDataFromNet(startPage); + } + + String getKey(String a) { + return a + SharedPref.getInstance(BaseApplication.Companion.context()).getString("DataCacheKey", ""); + } + + public void loadMore() { + getData(page + 1); + } + + void saveData(T t, int page) { + maCache.put(getKey(dataKey) + "_" + page, new Gson().toJson(t)); + } + + T getLocalData(int page) { + if (TextUtils.isEmpty(maCache.get(getKey(dataKey) + "_" + page))) { + return null; + } + T t = new Gson().fromJson(maCache.get(getKey(dataKey) + "_" + page), tClass); + return t; + } + + void getDataFromNet(final int page) { + this.page = page; + dataCallBack.getFlowable(page).compose(XApi.getApiTransformer()) + .compose(XApi.getScheduler()) + .compose(lifecycleProvider.bindToLifecycle()) + .subscribe(new ApiSubscriber() { + @Override + protected void onFail(NetError error) { + if (getLocalData(page) != null) { + dataCallBack.netError(error); + } else { + dataCallBack.netErrorForNoData(error); + } + activity.closeProgressDialog(); + if (error.getType() == NetError.AuthError) { + activity.toast("登陆失效", new Runnable() { + @Override + public void run() { + Lennon.Companion.requserLogin(); + } + }); + return; + } + } + + @Override + public void onNext(T o) { + if (getLocalData(page) != null) { + String a = new Gson().toJson(getLocalData(page)); + String b = new Gson().toJson(o); + if (a.equals(b) && refresh) { + dataCallBack.upView(o, page); + } else if (a.equals(b) && !refresh) { + + } else { + dataCallBack.upView(o, page); + saveData(o, page); + } + } else { + dataCallBack.upView(o, page); + saveData(o, page); + } + activity.closeProgressDialog(); + } + }); + } + + public interface DataCallBack { + /** + * @param t + * @param page + */ + void upView(@NonNull T t, int page); + + /** + * @param error + */ + void netErrorForNoData(@NonNull NetError error); + + /** + * @param error + */ + void netError(@NonNull NetError error); + + /** + * @param page + * @return Flowable + */ + Flowable getFlowable(int page); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt b/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt new file mode 100644 index 0000000..19eed4e --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt @@ -0,0 +1,106 @@ +package com.lennon.cn.utill.conf + +import android.app.Activity +import cn.droidlover.xdroidmvp.net.NetError +import com.lennon.cn.utill.utill.DensityUtils +import lennon.com.utill.R + +class Lennon { + companion object { + + var provider: LennonProvider? = null + + fun registProvider(provider: LennonProvider) { + this.provider = provider + } + + fun getFileProvide(): String { + if (provider == null) { + throw Throwable("请先注册provider") + } + return provider!!.getFileProvide() + } + + fun appName(): String { + if (provider == null) { + throw Throwable("请先注册provider") + } + return provider!!.appName() + } + + fun requserLogin() { + if (provider == null) { + throw Throwable("请先注册provider") + } + provider!!.requserLogin() + } + + fun appExit() { + if (provider == null) { + throw Throwable("请先注册provider") + } + provider!!.appExit() + } + + fun getLogo(): Int { + if (provider == null) { + throw Throwable("请先注册provider") + } + return provider!!.getLogo() + } + + fun getTitleColor(): Int { + if (provider == null) { + throw Throwable("请先注册provider") + } + return if (provider!!.getTitleColor() > 0) { + provider!!.getTitleColor() + } else { + R.color.color_fd0202 + } + } + + fun clean() { + if (provider == null) { + throw Throwable("请先注册provider") + } + provider!!.clean() + } + + fun isTest(): Boolean { + if (provider == null) { + throw Throwable("请先注册provider") + } + return provider!!.isTest() + } + + fun getFilePathName(): String { + if (provider == null) { + throw Throwable("请先注册provider") + } + return provider!!.getFilePathName() + } + + fun useDensity(activity: Activity) { + if (provider == null) { + DensityUtils.setDefault(activity) + } else { + provider!!.useDensity(activity) + } + } + + fun handleNetError(error: NetError): Boolean { + if (provider == null) { + throw Throwable("请先注册provider") + } + return provider!!.handleNetError(error) + } + + fun restartApp() { + if (provider == null) { + throw Throwable("请先注册provider") + } + provider!!.restartApp() + } + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt b/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt new file mode 100644 index 0000000..fa5e4fe --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt @@ -0,0 +1,27 @@ +package com.lennon.cn.utill.conf + +import android.app.Activity +import cn.droidlover.xdroidmvp.net.NetError +import com.lennon.cn.utill.utill.DensityUtils + +abstract class LennonProvider { + abstract fun appName(): String + abstract fun requserLogin() + abstract fun appExit() + abstract fun getLogo(): Int + abstract fun getTitleColor(): Int + abstract fun clean() + abstract fun isTest(): Boolean + abstract fun getFilePathName(): String + open fun useDensity(activity: Activity) { +// if (Utill.isPad(activity)) { +// DensityUtils.setOrientation(activity, DensityUtils.HEIGHT) +// } else { + DensityUtils.setDefault(activity) +// } + } + + abstract fun handleNetError(error: NetError): Boolean + abstract fun restartApp() + abstract fun getFileProvide(): String +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSearchSelectDialog.java b/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSearchSelectDialog.java new file mode 100644 index 0000000..d04ce84 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSearchSelectDialog.java @@ -0,0 +1,257 @@ +package com.lennon.cn.utill.dialog; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.graphics.PixelFormat; +import android.graphics.Typeface; +import android.os.IBinder; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.PopupWindow; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import com.lennon.cn.utill.adapter.StringItemAdapter; +import com.lennon.cn.utill.bean.StringBean; +import com.lennon.cn.utill.widget.SearchView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import cn.droidlover.xrecyclerview.RecyclerItemCallback; +import cn.droidlover.xrecyclerview.XRecyclerView; +import lennon.com.utill.R; + +public class BottomSearchSelectDialog extends PopupWindow { + protected Context context; + private WindowManager wm; + private View maskView; + private SearchView dialog_search; + private XRecyclerView xRecyclerView; + private TextView cancle; + private Listener listener; + private List list; + private List typefaces; + private List colors; + private List textSize; + + public void setTypefaces(List typefaces) { + this.typefaces = typefaces; + } + + public void setColors(List colors) { + this.colors = colors; + } + + public void setTextSize(List textSize) { + this.textSize = textSize; + } + + + public void setListener(Listener listener) { + this.listener = listener; + } + + public interface Listener { + void onItemClick(int position, T model); + + boolean isResult(@NonNull String text, @NonNull T t); + + void onCancel(); + } + + public BottomSearchSelectDialog(Context context, final T... a) { + List t = new ArrayList<>(); + if (a != null) { + Collections.addAll(t, a); + } + this.context = context; + this.list = t; + wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + setContentView(generateCustomView()); + setWidth(ViewGroup.LayoutParams.MATCH_PARENT); + setHeight(ViewGroup.LayoutParams.MATCH_PARENT); + setOutsideTouchable(true); + setFocusable(true); + setBackgroundDrawable(context.getResources().getDrawable(android.R.color.transparent)); + setAnimationStyle(R.style.Animations_BottomPush); + } + + public BottomSearchSelectDialog(Context context, List typefaces, + List colors, + List textSize, final T... a) { + this.typefaces = typefaces; + this.colors = colors; + this.textSize = textSize; + List t = new ArrayList<>(); + if (a != null) { + Collections.addAll(t, a); + } + this.context = context; + this.list = t; + wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + setContentView(generateCustomView()); + setWidth(ViewGroup.LayoutParams.MATCH_PARENT); + setHeight(ViewGroup.LayoutParams.MATCH_PARENT); + setOutsideTouchable(true); + setFocusable(true); + setBackgroundDrawable(context.getResources().getDrawable(android.R.color.transparent)); + setAnimationStyle(R.style.Animations_BottomPush); + } + + public BottomSearchSelectDialog(Context context, List list) { + this.context = context; + this.list = list; + wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + setContentView(generateCustomView()); + setWidth(ViewGroup.LayoutParams.MATCH_PARENT); + setHeight(ViewGroup.LayoutParams.MATCH_PARENT); + setOutsideTouchable(true); + setFocusable(true); + setBackgroundDrawable(context.getResources().getDrawable(android.R.color.transparent)); + setAnimationStyle(R.style.Animations_BottomPush); + } + + private View generateCustomView() { + View root = View.inflate(context, R.layout.dialog_bottom_search_select, null); + dialog_search = root.findViewById(R.id.dialog_bottom_search_select_search); + cancle = root.findViewById(R.id.dialog_bottom_search_select_cancle); + xRecyclerView = root.findViewById(R.id.dialog_bottom_search_select_list); + xRecyclerView.verticalLayoutManager(context); + final StringItemAdapter adapter = new StringItemAdapter(context); + xRecyclerView.setAdapter(adapter); + adapter.setRecItemClick(new RecyclerItemCallback() { + @Override + public void onItemClick(int position, T model, int tag, StringItemAdapter.ViewHolder holder) { + super.onItemClick(position, model, tag, holder); + if (listener != null) { + listener.onItemClick(position, model); + } + } + }); + dialog_search.setSearchViewListener(new SearchView.SearchViewListener() { + @Override + public void onClear() { + adapter.setData(BottomSearchSelectDialog.this.list); + } + + @Override + public void onSearch(String text) { + List l = new ArrayList<>(); + if (listener != null) { + for (T t : list) { + if (listener.isResult(text, t)) { + l.add(t); + } + } + } else { + l.addAll(list); + } + adapter.setData(l); + } + + @Override + public void onResult(String text) { + + } + + @Override + public void onSearchByButton(String text) { + + } + }); + cancle.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (listener != null) { + listener.onCancel(); + } + } + }); + if (list != null) { + adapter.setData(list); + } + if (typefaces != null) { + adapter.setTypefaces(typefaces); + } + if (textSize != null) { + adapter.setTextSize(textSize); + } + if (colors != null) { + adapter.setColors(colors); + } + return root; + } + + @TargetApi(23) + private void initType() { + // 解决华为手机在home建进入后台后,在进入应用,蒙层出现在popupWindow上层的bug。 + // android4.0及以上版本都有这个hide方法,根据jvm原理,可以直接调用,选择android6.0版本进行编译即可。 + setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL); + } + + @Override + public void showAtLocation(View parent, int gravity, int x, int y) { + addMaskView(parent.getWindowToken()); + super.showAtLocation(parent, gravity, x, y); + } + + @Override + public void showAsDropDown(View anchor, int xoff, int yoff) { + addMaskView(anchor.getWindowToken()); + super.showAsDropDown(anchor, xoff, yoff); + } + + @Override + public void dismiss() { + removeMaskView(); + super.dismiss(); + } + + /** + * 显示在界面的底部 + */ + public void show(Activity activity) { + showAtLocation(activity.getWindow().getDecorView(), Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0); + } + + private void addMaskView(IBinder token) { + WindowManager.LayoutParams p = new WindowManager.LayoutParams(); + p.width = WindowManager.LayoutParams.MATCH_PARENT; + p.height = WindowManager.LayoutParams.MATCH_PARENT; + p.format = PixelFormat.TRANSLUCENT; + p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; + p.token = token; + p.windowAnimations = android.R.style.Animation_Toast; + maskView = new View(context); + maskView.setBackgroundColor(0x7f000000); + maskView.setFitsSystemWindows(false); + // 华为手机在home建进入后台后,在进入应用,蒙层出现在popupWindow上层,导致界面卡死, + // 这里新增加按bug返回。 + // initType方法已经解决该问题,但是还是留着这个按back返回功能,防止其他手机出现华为手机类似问题。 + maskView.setOnKeyListener(new View.OnKeyListener() { + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + removeMaskView(); + return true; + } + return false; + } + }); + wm.addView(maskView, p); + } + + private void removeMaskView() { + if (maskView != null) { + wm.removeViewImmediate(maskView); + maskView = null; + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSelectDialog.java b/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSelectDialog.java new file mode 100644 index 0000000..7be7eb8 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSelectDialog.java @@ -0,0 +1,223 @@ +package com.lennon.cn.utill.dialog; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.graphics.PixelFormat; +import android.graphics.Typeface; +import android.os.IBinder; +import android.view.*; +import android.widget.PopupWindow; +import android.widget.TextView; + +import androidx.annotation.ColorRes; + +import cn.droidlover.xrecyclerview.RecyclerItemCallback; +import cn.droidlover.xrecyclerview.XRecyclerView; + +import com.lennon.cn.utill.adapter.StringItemAdapter; +import com.lennon.cn.utill.bean.StringBean; + +import lennon.com.utill.R; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Created by lennon on 2018/5/16. + */ + +public class BottomSelectDialog extends PopupWindow { + protected Context context; + private WindowManager wm; + private View maskView; + private XRecyclerView xRecyclerView; + private TextView cancel; + private Listener listener; + private List list; + private List typefaces; + private List colors; + private List textSize; + + public void setTypefaces(List typefaces) { + this.typefaces = typefaces; + } + + public void setColors(List colors) { + this.colors = colors; + } + + public void setTextSize(List textSize) { + this.textSize = textSize; + } + + + public void setListener(Listener listener) { + this.listener = listener; + } + + public interface Listener { + void onItemClick(int position, T model); + + void onCancel(); + } + + public BottomSelectDialog(Context context, final T... a) { + List t = new ArrayList<>(); + if (a != null) { + Collections.addAll(t, a); + } + this.context = context; + this.list = t; + wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + setContentView(generateCustomView()); + setWidth(ViewGroup.LayoutParams.MATCH_PARENT); + setHeight(ViewGroup.LayoutParams.MATCH_PARENT); + setOutsideTouchable(true); + setFocusable(true); + setBackgroundDrawable(context.getResources().getDrawable(android.R.color.transparent)); + setAnimationStyle(R.style.Animations_BottomPush); + } + + public BottomSelectDialog(Context context, List typefaces, + List colors, + List textSize, final T... a) { + this.typefaces = typefaces; + this.colors = colors; + this.textSize = textSize; + List t = new ArrayList<>(); + if (a != null) { + Collections.addAll(t, a); + } + this.context = context; + this.list = t; + wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + setContentView(generateCustomView()); + setWidth(ViewGroup.LayoutParams.MATCH_PARENT); + setHeight(ViewGroup.LayoutParams.MATCH_PARENT); + setOutsideTouchable(true); + setFocusable(true); + setBackgroundDrawable(context.getResources().getDrawable(android.R.color.transparent)); + setAnimationStyle(R.style.Animations_BottomPush); + } + + public BottomSelectDialog(Context context, List list) { + this.context = context; + this.list = list; + wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + setContentView(generateCustomView()); + setWidth(ViewGroup.LayoutParams.MATCH_PARENT); + setHeight(ViewGroup.LayoutParams.MATCH_PARENT); + setOutsideTouchable(true); + setFocusable(true); + setBackgroundDrawable(context.getResources().getDrawable(android.R.color.transparent)); + setAnimationStyle(R.style.Animations_BottomPush); + } + + public View generateCustomView() { + View root = View.inflate(context, R.layout.dialog_bottom_select, null); + xRecyclerView = root.findViewById(R.id.list); + xRecyclerView.verticalLayoutManager(context); + StringItemAdapter adapter = new StringItemAdapter(context); + xRecyclerView.setAdapter(adapter); + adapter.setRecItemClick(new RecyclerItemCallback() { + @Override + public void onItemClick(int position, T model, int tag, StringItemAdapter.ViewHolder holder) { + super.onItemClick(position, model, tag, holder); + if (listener != null) { + listener.onItemClick(position, model); + } + } + }); + if (list != null) { + adapter.setData(list); + } + if (typefaces != null) { + adapter.setTypefaces(typefaces); + } + if (textSize != null) { + adapter.setTextSize(textSize); + } + if (colors != null) { + adapter.setColors(colors); + } + cancel = root.findViewById(R.id.cancel); + cancel.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (listener != null) { + listener.onCancel(); + } + dismiss(); + } + }); + return root; + } + + @TargetApi(23) + private void initType() { + // 解决华为手机在home建进入后台后,在进入应用,蒙层出现在popupWindow上层的bug。 + // android4.0及以上版本都有这个hide方法,根据jvm原理,可以直接调用,选择android6.0版本进行编译即可。 + setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL); + } + + @Override + public void showAtLocation(View parent, int gravity, int x, int y) { + addMaskView(parent.getWindowToken()); + super.showAtLocation(parent, gravity, x, y); + } + + @Override + public void showAsDropDown(View anchor, int xoff, int yoff) { + addMaskView(anchor.getWindowToken()); + super.showAsDropDown(anchor, xoff, yoff); + } + + @Override + public void dismiss() { + removeMaskView(); + super.dismiss(); + } + + /** + * 显示在界面的底部 + */ + public void show(Activity activity) { + showAtLocation(activity.getWindow().getDecorView(), Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0); + } + + private void addMaskView(IBinder token) { + WindowManager.LayoutParams p = new WindowManager.LayoutParams(); + p.width = WindowManager.LayoutParams.MATCH_PARENT; + p.height = WindowManager.LayoutParams.MATCH_PARENT; + p.format = PixelFormat.TRANSLUCENT; + p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; + p.token = token; + p.windowAnimations = android.R.style.Animation_Toast; + maskView = new View(context); + maskView.setBackgroundColor(0x7f000000); + maskView.setFitsSystemWindows(false); + // 华为手机在home建进入后台后,在进入应用,蒙层出现在popupWindow上层,导致界面卡死, + // 这里新增加按bug返回。 + // initType方法已经解决该问题,但是还是留着这个按back返回功能,防止其他手机出现华为手机类似问题。 + maskView.setOnKeyListener(new View.OnKeyListener() { + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + removeMaskView(); + return true; + } + return false; + } + }); + wm.addView(maskView, p); + } + + private void removeMaskView() { + if (maskView != null) { + wm.removeViewImmediate(maskView); + maskView = null; + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/CommonAlertDialog.java b/utill/src/main/java/com/lennon/cn/utill/dialog/CommonAlertDialog.java new file mode 100644 index 0000000..ed73600 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/CommonAlertDialog.java @@ -0,0 +1,169 @@ +package com.lennon.cn.utill.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.*; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; +import lennon.com.utill.R; + +/** + * Created by dingyi on 2016/11/23. + */ + +public class CommonAlertDialog extends Dialog { + + private TextView mTitleTv; + private TextView mMsgTv; + private TextView mSureTv; + private TextView mCancleTv; + private EditText mQtyEdt; + private EditText mContentEdt; + private boolean cancelable = true; + + private OnAlertDialogListener mOnListener; + private String mQtyString; + private int mIndex; + + public CommonAlertDialog(Context context) { + super(context, R.style.common_dialog); + setParams(); + } + + public void setIndex(int index) { + this.mIndex = index; + } + + public int getIndex() { + return mIndex; + } + + public void setTitle(String msg) { + mTitleTv.setText(msg); + } + + public void setMsg(String msg) { + mMsgTv.setVisibility(View.VISIBLE); + mQtyEdt.setVisibility(View.GONE); + mContentEdt.setVisibility(View.GONE); + mMsgTv.setText(msg); + } + + public void setSureMsg(String msg) { + mSureTv.setText(msg); + } + + public void setCancleMsg(String msg) { + mCancleTv.setText(msg); + } + + @Override + public void setCancelable(boolean flag) { + super.setCancelable(flag); + cancelable = flag; + } + + public void setQty(String text) { + mMsgTv.setVisibility(View.GONE); + mContentEdt.setVisibility(View.GONE); + mQtyEdt.setVisibility(View.VISIBLE); + mQtyString = text; + mQtyEdt.setText(text); + mQtyEdt.setSelection(text.length()); + } + + public void setContent(String content) { + mMsgTv.setVisibility(View.GONE); + mQtyEdt.setVisibility(View.GONE); + mContentEdt.setVisibility(View.VISIBLE); + mContentEdt.setText(content); + mContentEdt.setSelection(mContentEdt.getText().length()); + } + + + public void setDialogListener(OnAlertDialogListener listener) { + this.mOnListener = listener; + } + + + public void disableCancle() { + mCancleTv.setVisibility(View.GONE); + } + + private void setParams() { + setContentView(R.layout.common_alert_dialog); + Window window = getWindow(); + window.setLayout(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + mTitleTv = (TextView) findViewById(R.id.common_alert_dialog_title_tv); + mMsgTv = (TextView) findViewById(R.id.common_alert_dialog_msg_tv); + mSureTv = (TextView) findViewById(R.id.common_alert_dialog_sure_tv); + mCancleTv = (TextView) findViewById(R.id.common_alert_dialog_cancle_tv); + mQtyEdt = (EditText) findViewById(R.id.common_alert_dialog_qty_edt); + mContentEdt = (EditText) findViewById(R.id.common_alert_dialog_content_edt); + mSureTv.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (null != mOnListener) { + if (mMsgTv.getVisibility() == View.VISIBLE) { + mOnListener.onSure(); + } else if (mContentEdt.getVisibility() == View.VISIBLE) { + mOnListener.onSure(mContentEdt.getText().toString()); + } else { + mOnListener.onSure(mIndex, mQtyString); + } + } + } + }); + mCancleTv.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (null != mOnListener) { + mOnListener.onCancle(); + } + } + }); + + mQtyEdt.addTextChangedListener(mWatcher); + setOnKeyListener(new OnKeyListener() { + @Override + public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_SEARCH) { + if (mOnListener != null) { + mOnListener.onCancle(); + return true; + } + return false; + } else { + return false; //默认返回 false + } + } + }); + } + + private TextWatcher mWatcher = new TextWatcher() { + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, + int after) { + + } + + @Override + public void afterTextChanged(Editable s) { + if (s != null && !"".equals(s.toString())) { + mQtyString = s.toString(); + } else { + mQtyString = "0"; + } + } + }; +} diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/CustomProgressDialog.java b/utill/src/main/java/com/lennon/cn/utill/dialog/CustomProgressDialog.java new file mode 100644 index 0000000..e894884 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/CustomProgressDialog.java @@ -0,0 +1,112 @@ +package com.lennon.cn.utill.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import net.qiujuer.genius.ui.widget.Loading; + +import lennon.com.utill.R; + + +/** + * @author luoying + */ +public class CustomProgressDialog extends Dialog { + + private Context mContext; + private Loading loading; + + public CustomProgressDialog(Context context) { + super(context, R.style.dialog_progress); + this.mContext = context; + setParams(); + } + + public CustomProgressDialog(Context context, String title, String msg) { + super(context, R.style.dialog_progress); + this.mContext = context; + setParams(); + setTitile(title); + setMessage(msg); + } + + public CustomProgressDialog(Context context, int msg_resid) { + super(context, R.style.dialog_progress); + this.mContext = context; + setParams(); + setMessage(msg_resid); + } + + public CustomProgressDialog(Context context, int title_resid, int msg_resid) { + super(context, R.style.dialog_progress); + this.mContext = context; + setParams(); + setTitile(title_resid); + setMessage(msg_resid); + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + loading = (Loading) findViewById(R.id.progressdialog_anim_progress); + loading.start(); + } + + /** + * setTitile 标题 + * + * @param strTitle + * @return + */ + public void setTitile(String strTitle) { + + } + + /** + * setTitile 标题 + * + * @param title_resid + * @return + */ + public void setTitile(int title_resid) { + + } + + /** + * setMessage 提示内容 + * + * @param strMessage + * @return + */ + public void setMessage(String strMessage) { + TextView tvMsg = (TextView) findViewById(R.id.progressdialog_ms_tv); + if (tvMsg != null) { + tvMsg.setVisibility(View.VISIBLE); + tvMsg.setText(strMessage); + } else { + tvMsg.setVisibility(View.GONE); + } + } + + /** + * setMessage 提示内容 + * + * @param resid + * @return + */ + public void setMessage(int resid) { + TextView tvMsg = (TextView) findViewById(R.id.progressdialog_ms_tv); + if (tvMsg != null) { + tvMsg.setText(resid); + } + } + + private void setParams() { + setContentView(R.layout.customprogressdialog); + // 设置全屏 + getWindow().setLayout(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/OnAlertDialogListener.java b/utill/src/main/java/com/lennon/cn/utill/dialog/OnAlertDialogListener.java new file mode 100644 index 0000000..c6d696a --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/OnAlertDialogListener.java @@ -0,0 +1,12 @@ +package com.lennon.cn.utill.dialog; + +/** + * Created by dingyi on 2016/12/2. + */ + +public class OnAlertDialogListener { + public void onSure(){}; + public void onSure(String content){}; + public void onSure(int index, String content){}; + public void onCancle(){}; +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/AmtUtils.java b/utill/src/main/java/com/lennon/cn/utill/utill/AmtUtils.java new file mode 100644 index 0000000..6527716 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/AmtUtils.java @@ -0,0 +1,250 @@ +package com.lennon.cn.utill.utill; + +import java.math.BigDecimal; + +public class AmtUtils { + + /** + * 功能描述:金额字符串转换:单位分转成单元 + *

+ * 传入需要转换的金额字符串 + * + * @return 转换后的金额字符串 + */ + public static String fenToYuan(Object o) { + if (o == null) { + return "0.00"; + } + String s = o.toString(); +// if ("".equals(s)) { +// return "0.00"; +// } + if (s.contains(".")) { + s = s.substring(0, s.indexOf(".")); + } + int len = -1; + StringBuilder sb = new StringBuilder(); + if (s != null && s.trim().length() > 0 && !"null".equalsIgnoreCase(s)) { + s = removeZero(s); + if (s != null && s.trim().length() > 0 && !"null".equalsIgnoreCase(s)) { + len = s.length(); + int tmp = s.indexOf("-"); + if (tmp >= 0) { + if (len == 2) { + sb.append("-0.0").append(s.substring(1)); + } else if (len == 3) { + sb.append("-0.").append(s.substring(1)); + } else { + sb.append(s.substring(0, len - 2)).append(".").append(s.substring(len - 2)); + } + } else { + if (len == 1) { + sb.append("0.0").append(s); + } else if (len == 2) { + sb.append("0.").append(s); + } else { + sb.append(s.substring(0, len - 2)).append(".").append(s.substring(len - 2)); + } + } + } else { + sb.append("0.00"); + } + } else { + sb.append("0.00"); + } + return sb.toString(); + } + + /** + * 功能描述:金额字符串转换:单位元转成单分 + *

+ * 传入需要转换的金额字符串 + * + * @return 转换后的金额字符串 + */ + public static String yuanToFen(Object o) { + if (o == null) { + return "0"; + } + String s = o.toString(); + int posIndex = -1; + String str = ""; + StringBuilder sb = new StringBuilder(); + if (s != null && s.trim().length() > 0 && !"null".equalsIgnoreCase(s)) { + posIndex = s.indexOf("."); + if (posIndex > 0) { + int len = s.length(); + if (len == posIndex + 1) { + str = s.substring(0, posIndex); + if (str == "0") { + str = ""; + } + sb.append(str).append("00"); + } else if (len == posIndex + 2) { + str = s.substring(0, posIndex); + if (str == "0") { + str = ""; + } + sb.append(str).append(s.substring(posIndex + 1, posIndex + 2)).append("0"); + } else if (len == posIndex + 3) { + str = s.substring(0, posIndex); + if (str == "0") { + str = ""; + } + sb.append(str).append(s.substring(posIndex + 1, posIndex + 3)); + } else { + str = s.substring(0, posIndex); + if (str == "0") { + str = ""; + } + sb.append(str).append(s.substring(posIndex + 1, posIndex + 3)); + } + } else { + sb.append(s).append("00"); + } + } else { + sb.append("0"); + } + str = removeZero(sb.toString()); + if (str != null && str.trim().length() > 0 && !"null".equalsIgnoreCase(str.trim())) { + return str; + } else { + return "0"; + } + } + + /** + * 功能描述:去除字符串首部为"0"字符 + * + * @param str 传入需要转换的字符串 + * @return 转换后的字符串 + */ + public static String removeZero(String str) { + char ch; + String result = ""; + if (str != null && str.trim().length() > 0 && !"null".equalsIgnoreCase(str.trim())) { + try { + for (int i = 0; i < str.length(); i++) { + ch = str.charAt(i); + if (ch != '0') { + result = str.substring(i); + break; + } + } + } catch (Exception e) { + result = ""; + } + } else { + result = ""; + } + return result; + + } + + /** + * 金额(分) + * + * @param rate 手续费v + * 小数点后位数 + * @return + */ + public static String getSettleAmt(String amountfen, double rate) { + String feeAmtStr = String.valueOf(Double.valueOf(AmtUtils.fenToYuan(amountfen)) * rate); + BigDecimal mData = new BigDecimal(feeAmtStr).setScale(2, BigDecimal.ROUND_HALF_UP); + String resultAmt = AmtUtils.yuanToFen(String.valueOf(mData)); + return resultAmt; + } + + /** + * 金额为分的格式 + */ + private static final String CURRENCY_FEN_REGEX = "\\-?[0-9]+"; + + /** + * 将分为单位的转换为元并返回金额格式的字符串 (除100) + * + * @param amount + * @return + * @throws Exception + */ + public static String changeF2Y(Long amount) throws Exception { + if (!amount.toString().matches(CURRENCY_FEN_REGEX)) { + throw new Exception("金额格式有误"); + } + int flag = 0; + String amString = amount.toString(); + if (amString.charAt(0) == '-') { + flag = 1; + amString = amString.substring(1); + } + StringBuffer result = new StringBuffer(); + if (amString.length() == 1) { + result.append("0.0").append(amString); + } else if (amString.length() == 2) { + result.append("0.").append(amString); + } else { + String intString = amString.substring(0, amString.length() - 2); + for (int i = 1; i <= intString.length(); i++) { + if ((i - 1) % 3 == 0 && i != 1) { + result.append(","); + } + result.append(intString.substring(intString.length() - i, intString.length() - i + 1)); + } + result.reverse().append(".").append(amString.substring(amString.length() - 2)); + } + if (flag == 1) { + return "-" + result.toString(); + } else { + return result.toString(); + } + } + + /** + * 将分为单位的转换为元 (除100) + * + * @param amount + * @return + * @throws Exception + */ + public static String changeF2Y(String amount) throws Exception { + if (!amount.matches(CURRENCY_FEN_REGEX)) { + throw new Exception("金额格式有误"); + } + return String.format("%.2f", BigDecimal.valueOf(Long.valueOf(amount)).divide(new BigDecimal(100))); + } + + /** + * 将元为单位的转换为分 (乘100) + * + * @param amount + * @return + */ + public static String changeY2F(Long amount) { + return BigDecimal.valueOf(amount).multiply(new BigDecimal(100)).toString(); + } + + /** + * 将元为单位的转换为分 替换小数点,支持以逗号区分的金额 + * + * @param amount + * @return + */ + public static String changeY2F(String amount) { + String currency = amount.replaceAll("\\$|\\¥|\\,", ""); + //处理包含, ¥ 或者$的金额 + int index = currency.indexOf("."); + int length = currency.length(); + long amLong = 0L; + if (index == -1) { + amLong = Long.parseLong(currency + "00"); + } else if (length - index >= 3) { + amLong = Long.parseLong((currency.substring(0, index + 3)).replace(".", "")); + } else if (length - index == 2) { + amLong = Long.parseLong((currency.substring(0, index + 2)).replace(".", "") + 0); + } else { + amLong = Long.parseLong((currency.substring(0, index + 1)).replace(".", "") + "00"); + } + return Long.toString(amLong); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/AnimationUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/AnimationUtil.java new file mode 100644 index 0000000..0f071e9 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/AnimationUtil.java @@ -0,0 +1,307 @@ +package com.lennon.cn.utill.utill; + +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.RotateAnimation; +import android.view.animation.ScaleAnimation; + +public class AnimationUtil { + /** + * 默认动画持续时间 + */ + public static final long DEFAULT_ANIMATION_DURATION = 400; + + + /** + * 获取一个旋转动画 + * + * @param fromDegrees 开始角度 + * @param toDegrees 结束角度 + * @param pivotXType 旋转中心点X轴坐标相对类型 + * @param pivotXValue 旋转中心点X轴坐标 + * @param pivotYType 旋转中心点Y轴坐标相对类型 + * @param pivotYValue 旋转中心点Y轴坐标 + * @param durationMillis 持续时间 + * @param animationListener 动画监听器 + * @return 一个旋转动画 + */ + public static RotateAnimation getRotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue, long durationMillis, Animation.AnimationListener animationListener) { + RotateAnimation rotateAnimation = new RotateAnimation(fromDegrees, + toDegrees, pivotXType, pivotXValue, pivotYType, pivotYValue); + rotateAnimation.setDuration(durationMillis); + if (animationListener != null) { + rotateAnimation.setAnimationListener(animationListener); + } + return rotateAnimation; + } + + + /** + * 获取一个根据视图自身中心点旋转的动画 + * + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + * @return 一个根据中心点旋转的动画 + */ + public static RotateAnimation getRotateAnimationByCenter(long durationMillis, Animation.AnimationListener animationListener) { + return getRotateAnimation(0f, 359f, Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, 0.5f, durationMillis, + animationListener); + } + + + /** + * 获取一个根据中心点旋转的动画 + * + * @param duration 动画持续时间 + * @return 一个根据中心点旋转的动画 + */ + public static RotateAnimation getRotateAnimationByCenter(long duration) { + return getRotateAnimationByCenter(duration, null); + } + + + /** + * 获取一个根据视图自身中心点旋转的动画 + * + * @param animationListener 动画监听器 + * @return 一个根据中心点旋转的动画 + */ + public static RotateAnimation getRotateAnimationByCenter(Animation.AnimationListener animationListener) { + return getRotateAnimationByCenter(DEFAULT_ANIMATION_DURATION, + animationListener); + } + + + /** + * 获取一个根据中心点旋转的动画 + * + * @return 一个根据中心点旋转的动画,默认持续时间为DEFAULT_ANIMATION_DURATION + */ + public static RotateAnimation getRotateAnimationByCenter() { + return getRotateAnimationByCenter(DEFAULT_ANIMATION_DURATION, null); + } + + + /** + * 获取一个透明度渐变动画 + * + * @param fromAlpha 开始时的透明度 + * @param toAlpha 结束时的透明度都 + * @param durationMillis 持续时间 + * @param animationListener 动画监听器 + * @return 一个透明度渐变动画 + */ + public static AlphaAnimation getAlphaAnimation(float fromAlpha, float toAlpha, long durationMillis, Animation.AnimationListener animationListener) { + AlphaAnimation alphaAnimation = new AlphaAnimation(fromAlpha, toAlpha); + alphaAnimation.setDuration(durationMillis); + if (animationListener != null) { + alphaAnimation.setAnimationListener(animationListener); + } + return alphaAnimation; + } + + + /** + * 获取一个透明度渐变动画 + * + * @param fromAlpha 开始时的透明度 + * @param toAlpha 结束时的透明度都 + * @param durationMillis 持续时间 + * @return 一个透明度渐变动画 + */ + public static AlphaAnimation getAlphaAnimation(float fromAlpha, float toAlpha, long durationMillis) { + return getAlphaAnimation(fromAlpha, toAlpha, durationMillis, null); + } + + + /** + * 获取一个透明度渐变动画 + * + * @param fromAlpha 开始时的透明度 + * @param toAlpha 结束时的透明度都 + * @param animationListener 动画监听器 + * @return 一个透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION + */ + public static AlphaAnimation getAlphaAnimation(float fromAlpha, float toAlpha, Animation.AnimationListener animationListener) { + return getAlphaAnimation(fromAlpha, toAlpha, DEFAULT_ANIMATION_DURATION, animationListener); + } + + + /** + * 获取一个透明度渐变动画 + * + * @param fromAlpha 开始时的透明度 + * @param toAlpha 结束时的透明度都 + * @return 一个透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION + */ + public static AlphaAnimation getAlphaAnimation(float fromAlpha, float toAlpha) { + return getAlphaAnimation(fromAlpha, toAlpha, DEFAULT_ANIMATION_DURATION, null); + } + + + /** + * 获取一个由完全显示变为不可见的透明度渐变动画 + * + * @param durationMillis 持续时间 + * @param animationListener 动画监听器 + * @return 一个由完全显示变为不可见的透明度渐变动画 + */ + public static AlphaAnimation getHiddenAlphaAnimation(long durationMillis, Animation.AnimationListener animationListener) { + return getAlphaAnimation(1.0f, 0.0f, durationMillis, animationListener); + } + + + /** + * 获取一个由完全显示变为不可见的透明度渐变动画 + * + * @param durationMillis 持续时间 + * @return 一个由完全显示变为不可见的透明度渐变动画 + */ + public static AlphaAnimation getHiddenAlphaAnimation(long durationMillis) { + return getHiddenAlphaAnimation(durationMillis, null); + } + + + /** + * 获取一个由完全显示变为不可见的透明度渐变动画 + * + * @param animationListener 动画监听器 + * @return 一个由完全显示变为不可见的透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION + */ + public static AlphaAnimation getHiddenAlphaAnimation(Animation.AnimationListener animationListener) { + return getHiddenAlphaAnimation(DEFAULT_ANIMATION_DURATION, animationListener); + } + + + /** + * 获取一个由完全显示变为不可见的透明度渐变动画 + * + * @return 一个由完全显示变为不可见的透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION + */ + public static AlphaAnimation getHiddenAlphaAnimation() { + return getHiddenAlphaAnimation(DEFAULT_ANIMATION_DURATION, null); + } + + + /** + * 获取一个由不可见变为完全显示的透明度渐变动画 + * + * @param durationMillis 持续时间 + * @param animationListener 动画监听器 + * @return 一个由不可见变为完全显示的透明度渐变动画 + */ + public static AlphaAnimation getShowAlphaAnimation(long durationMillis, Animation.AnimationListener animationListener) { + return getAlphaAnimation(0.0f, 1.0f, durationMillis, animationListener); + } + + + /** + * 获取一个由不可见变为完全显示的透明度渐变动画 + * + * @param durationMillis 持续时间 + * @return 一个由不可见变为完全显示的透明度渐变动画 + */ + public static AlphaAnimation getShowAlphaAnimation(long durationMillis) { + return getAlphaAnimation(0.0f, 1.0f, durationMillis, null); + } + + + /** + * 获取一个由不可见变为完全显示的透明度渐变动画 + * + * @param animationListener 动画监听器 + * @return 一个由不可见变为完全显示的透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION + */ + public static AlphaAnimation getShowAlphaAnimation(Animation.AnimationListener animationListener) { + return getAlphaAnimation(0.0f, 1.0f, DEFAULT_ANIMATION_DURATION, animationListener); + } + + + /** + * 获取一个由不可见变为完全显示的透明度渐变动画 + * + * @return 一个由不可见变为完全显示的透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION + */ + public static AlphaAnimation getShowAlphaAnimation() { + return getAlphaAnimation(0.0f, 1.0f, DEFAULT_ANIMATION_DURATION, null); + } + + + /** + * 获取一个缩小动画 + * + * @param durationMillis 时间 + * @param animationListener 监听 + * @return 一个缩小动画 + */ + public static ScaleAnimation getLessenScaleAnimation(long durationMillis, Animation.AnimationListener animationListener) { + ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 0.0f, 1.0f, + 0.0f, ScaleAnimation.RELATIVE_TO_SELF, + ScaleAnimation.RELATIVE_TO_SELF); + scaleAnimation.setDuration(durationMillis); + scaleAnimation.setAnimationListener(animationListener); + return scaleAnimation; + } + + + /** + * 获取一个缩小动画 + * + * @param durationMillis 时间 + * @return 一个缩小动画 + */ + public static ScaleAnimation getLessenScaleAnimation(long durationMillis) { + return getLessenScaleAnimation(durationMillis, null); + } + + + /** + * 获取一个缩小动画 + * + * @param animationListener 监听 + * @return 返回一个缩小的动画 + */ + public static ScaleAnimation getLessenScaleAnimation(Animation.AnimationListener animationListener) { + return getLessenScaleAnimation(DEFAULT_ANIMATION_DURATION, animationListener); + } + + + /** + * 获取一个放大动画 + * + * @param durationMillis 时间 + * @param animationListener 监听 + * @return 返回一个放大的效果 + */ + public static ScaleAnimation getAmplificationAnimation(long durationMillis, Animation.AnimationListener animationListener) { + ScaleAnimation scaleAnimation = new ScaleAnimation(0.0f, 1.0f, 0.0f, + 1.0f, ScaleAnimation.RELATIVE_TO_SELF, + ScaleAnimation.RELATIVE_TO_SELF); + scaleAnimation.setDuration(durationMillis); + scaleAnimation.setAnimationListener(animationListener); + return scaleAnimation; + } + + + /** + * 获取一个放大动画 + * + * @param durationMillis 时间 + * @return 返回一个放大的效果 + */ + public static ScaleAnimation getAmplificationAnimation(long durationMillis) { + return getAmplificationAnimation(durationMillis, null); + } + + + /** + * 获取一个放大动画 + * + * @param animationListener 监听 + * @return 返回一个放大的效果 + */ + public static ScaleAnimation getAmplificationAnimation(Animation.AnimationListener animationListener) { + return getAmplificationAnimation(DEFAULT_ANIMATION_DURATION, animationListener); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/AppStatusManager.java b/utill/src/main/java/com/lennon/cn/utill/utill/AppStatusManager.java new file mode 100644 index 0000000..3608900 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/AppStatusManager.java @@ -0,0 +1,48 @@ +package com.lennon.cn.utill.utill; + +/** + * Created by lennon on 2018/1/23. + */ + +public class AppStatusManager { + private static AppStatusManager mInstance = null; + + private int appStatus = AppStatusConstant.APP_FORCE_KILLED; + + private AppStatusManager() { + + } + + public static AppStatusManager getInstance() { + if(mInstance==null) { + synchronized (AppStatusManager.class) { + if(mInstance==null) { + mInstance = new AppStatusManager(); + } + } + } + return mInstance; + } + + public void setAppStatus(int appStatus) { + this.appStatus = appStatus; + } + + public int getAppStatus() { + return appStatus; + } + + + public static class AppStatusConstant { + + /** + * App被回收,初始状态 + */ + public static final int APP_FORCE_KILLED = 0; + + /** + * 正常运行 + */ + public static final int APP_NORMAL = 1; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/AudioPlayerUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/AudioPlayerUtil.java new file mode 100644 index 0000000..4eb22ef --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/AudioPlayerUtil.java @@ -0,0 +1,86 @@ +package com.lennon.cn.utill.utill; + +import android.content.Context; +import android.media.MediaMetadataRetriever; +import android.media.MediaPlayer; +import android.net.Uri; +import cn.droidlover.xdroidmvp.log.XLog; + +import java.io.IOException; +import java.util.HashMap; + +/** + * @author lennon + *

+ * 作者:lennon on 2019/2/18 16:30 + *

+ * 邮箱:1136160757@qq.com + */ +public class AudioPlayerUtil { + private static final String TAG = "AudioRecordTest"; + private MediaPlayer mPlayer; + + public AudioPlayerUtil() { + } + + public MediaPlayer start(String mFileName, MediaPlayer.OnCompletionListener listener) { + if (this.mPlayer == null) { + this.mPlayer = new MediaPlayer(); + } else { + this.mPlayer.reset(); + } + + try { + this.mPlayer.setDataSource(mFileName); + this.mPlayer.prepareAsync(); + this.mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + @Override + public void onPrepared(MediaPlayer mediaPlayer) { + mediaPlayer.start(); + } + }); + if (listener != null) { + this.mPlayer.setOnCompletionListener(listener); + } + } catch (Exception var4) { + var4.printStackTrace(); + } + return mPlayer; + + } + + public MediaPlayer start(Context context, String mFileUrl, MediaPlayer.OnCompletionListener listener) { + if (this.mPlayer == null) { + this.mPlayer = new MediaPlayer(); + } else { + this.mPlayer.reset(); + } + try { + Uri uri = Uri.parse(mFileUrl); + this.mPlayer.setDataSource(context, uri); + this.mPlayer.prepareAsync(); + this.mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + @Override + public void onPrepared(MediaPlayer mediaPlayer) { + mediaPlayer.start(); + } + }); + if (listener != null) { + this.mPlayer.setOnCompletionListener(listener); + } + } catch (Exception var4) { + var4.printStackTrace(); + } + return mPlayer; + + } + + public void stop() { + if (this.mPlayer != null) { + this.mPlayer.stop(); + this.mPlayer.release(); + this.mPlayer = null; + } + + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/AudioRecorderUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/AudioRecorderUtil.java new file mode 100644 index 0000000..9da9804 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/AudioRecorderUtil.java @@ -0,0 +1,196 @@ +package com.lennon.cn.utill.utill; + +import android.media.MediaRecorder; +import android.os.Handler; + +import java.io.File; +import java.io.IOException; + +/** + * @author lennon + *

+ * 作者:lennon on 2019/2/18 16:29 + *

+ * 邮箱:1136160757@qq.com + */ +public class AudioRecorderUtil { + private final String TAG = AudioRecorderUtil.class.getName(); + public static final int MAX_LENGTH = 60000; + private String filePath; + private String folderPath; + private MediaRecorder mMediaRecorder; + private int maxLength; + private long startTime; + private long endTime; + private OnAudioStatusUpdateListener audioStatusUpdateListener; + private final Handler mHandler = new Handler(); + private Runnable mUpdateMicStatusTimer = new Runnable() { + @Override + public void run() { + AudioRecorderUtil.this.updateMicStatus(); + } + }; + private int BASE = 1; + private int SPACE = 100; + + public AudioRecorderUtil(String folderPath) { + File path = new File(folderPath); + if (!path.exists()) { + path.mkdirs(); + } + + this.folderPath = folderPath; + this.maxLength = '\uea60'; + } + + public String start() { + if (this.mMediaRecorder == null) { + this.mMediaRecorder = new MediaRecorder(); + } else { + try { + this.mMediaRecorder.stop(); + this.mMediaRecorder.reset(); + this.mMediaRecorder.release(); + } catch (Exception var2) { + this.mMediaRecorder.reset(); + } + } + + this.mHandler.removeCallbacks(this.mUpdateMicStatusTimer); + + try { + this.mMediaRecorder.setAudioSource(1); + this.mMediaRecorder.setOutputFormat(0); + this.mMediaRecorder.setAudioEncoder(1); + this.filePath = this.folderPath + File.separator + System.currentTimeMillis() + ".mp3"; + this.mMediaRecorder.setOutputFile(this.filePath); + this.mMediaRecorder.setMaxDuration(this.maxLength); + this.mMediaRecorder.prepare(); + this.mMediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() { + @Override + public void onInfo(MediaRecorder mr, int what, int extra) { + if (what == 800) { + AudioRecorderUtil.this.stop(); + } + + } + }); + this.mMediaRecorder.start(); + this.startTime = System.currentTimeMillis(); + this.updateMicStatus(); + if (this.audioStatusUpdateListener != null) { + this.audioStatusUpdateListener.onStart(); + } + } catch (IllegalStateException var3) { + if (this.audioStatusUpdateListener != null) { + this.audioStatusUpdateListener.onError(var3); + } + + this.cancel(); + } catch (IOException var4) { + if (this.audioStatusUpdateListener != null) { + this.audioStatusUpdateListener.onError(var4); + } + + this.cancel(); + } + return filePath; + } + + public long getSumTime() { + return this.startTime == 0L ? 0L : System.currentTimeMillis() - this.startTime; + } + + public long stop() { + if (this.mMediaRecorder == null) { + return 0L; + } else { + this.endTime = System.currentTimeMillis(); + + try { + this.mMediaRecorder.stop(); + this.mMediaRecorder.reset(); + this.mMediaRecorder.release(); + this.mMediaRecorder = null; + if (this.audioStatusUpdateListener != null) { + this.audioStatusUpdateListener.onStop(this.filePath); + } + } catch (RuntimeException var3) { + this.mMediaRecorder.reset(); + this.mMediaRecorder.release(); + this.mMediaRecorder = null; + File file = new File(this.filePath); + if (file.exists()) { + file.delete(); + } + + if (this.audioStatusUpdateListener != null) { + this.audioStatusUpdateListener.onError(var3); + } + } + + this.filePath = ""; + return this.endTime - this.startTime; + } + } + + public void cancel() { + try { + this.mMediaRecorder.stop(); + this.mMediaRecorder.reset(); + this.mMediaRecorder.release(); + this.mMediaRecorder = null; + } catch (RuntimeException var2) { + this.mMediaRecorder.reset(); + this.mMediaRecorder.release(); + this.mMediaRecorder = null; + } + + File file = new File(this.filePath); + if (file.exists()) { + file.delete(); + } + + this.filePath = ""; + if (this.audioStatusUpdateListener != null) { + this.audioStatusUpdateListener.onCancel(); + } + + } + + public void setMaxLength(int maxLength) { + this.maxLength = maxLength; + } + + private void updateMicStatus() { + if (this.mMediaRecorder != null) { + double ratio = (double) this.mMediaRecorder.getMaxAmplitude() / (double) this.BASE; + double db = 0.0D; + if (ratio > 1.0D) { + db = 20.0D * Math.log10(ratio); + if (null != this.audioStatusUpdateListener) { + this.audioStatusUpdateListener.onProgress(db, System.currentTimeMillis() - this.startTime); + } + } + + this.mHandler.postDelayed(this.mUpdateMicStatusTimer, (long) this.SPACE); + } + + } + + public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener audioStatusUpdateListener) { + this.audioStatusUpdateListener = audioStatusUpdateListener; + } + + public interface OnAudioStatusUpdateListener { + void onStart(); + + void onProgress(double var1, long var3); + + void onError(Exception var1); + + void onCancel(); + + void onStop(String var1); + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/BitmapOperations.kt b/utill/src/main/java/com/lennon/cn/utill/utill/BitmapOperations.kt new file mode 100644 index 0000000..7742c50 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/BitmapOperations.kt @@ -0,0 +1,109 @@ +package com.lennon.cn.utill.utill + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Matrix +import android.graphics.Paint + +object BitmapOperations { + + private val paint: Paint + get() { + val paint = Paint() + paint.isAntiAlias = true + return paint + } + + /** + * 按比例缩放图片 + * baseBitmap: 需要缩放的原图 + * float x:横向缩放比例 + * float y:纵向缩放比例 + */ + fun bitmapScale(baseBitmap: Bitmap, x: Float, y: Float): Bitmap { + // 因为要将图片放大,所以要根据放大的尺寸重新创建Bitmap + val afterBitmap = Bitmap.createBitmap((baseBitmap.width * x).toInt(), (baseBitmap.height * y).toInt(), baseBitmap.config) + val canvas = Canvas(afterBitmap) + // 初始化Matrix对象 + val matrix = Matrix() + // 根据传入的参数设置缩放比例 + matrix.setScale(x, y) + // 根据缩放比例,把图片draw到Canvas上 + canvas.drawBitmap(baseBitmap, matrix, paint) + return afterBitmap + } + + /** + * 按给定尺寸缩放图片 + * baseBitmap: 需要缩放的原图 + * new_width:缩放后的宽度 + * new_height:缩放后的高度 + */ + fun bitmapScale(baseBitmap: Bitmap, new_width: Int, new_height: Int): Bitmap { + // 因为要将图片放大,所以要根据放大的尺寸重新创建Bitmap + val afterBitmap = Bitmap.createBitmap(new_width, new_height, baseBitmap.config) + val canvas = Canvas(afterBitmap) + // 初始化Matrix对象 + val matrix = Matrix() + // 根据传入的参数设置缩放比例 + val sx = new_width.toFloat() / baseBitmap.width.toFloat() + val sy = new_height.toFloat() / baseBitmap.height.toFloat() + matrix.setScale(sx, sy) + // 根据缩放比例,把图片draw到Canvas上 + canvas.drawBitmap(baseBitmap, matrix, paint) + return afterBitmap + } + + /** + * 倾斜图片 + */ + fun bitmapSkew(baseBitmap: Bitmap, dx: Float, dy: Float): Bitmap { + // 根据图片的倾斜比例,计算变换后图片的大小, + val afterBitmap = Bitmap.createBitmap(baseBitmap.width + (baseBitmap.width * dx).toInt(), baseBitmap.height + (baseBitmap.height * dy).toInt(), baseBitmap.config) + val canvas = Canvas(afterBitmap) + val matrix = Matrix() + // 设置图片倾斜的比例 + matrix.setSkew(dx, dy) + canvas.drawBitmap(baseBitmap, matrix, paint) + return afterBitmap + } + + /** + * 图片移动 + */ + fun bitmapTranslate(baseBitmap: Bitmap, dx: Float, dy: Float): Bitmap { + // 需要根据移动的距离来创建图片的拷贝图大小 + val afterBitmap = Bitmap.createBitmap((baseBitmap.width + dx).toInt(), (baseBitmap.height + dy).toInt(), baseBitmap.config) + val canvas = Canvas(afterBitmap) + val matrix = Matrix() + // 设置移动的距离 + matrix.setTranslate(dx, dy) + canvas.drawBitmap(baseBitmap, matrix, paint) + return afterBitmap + } + + /** + * 图片绕中心旋转 + */ + fun bitmapRotate(baseBitmap: Bitmap, degrees: Float): Bitmap { + // 创建一个和原图一样大小的图片 + val afterBitmap = Bitmap.createBitmap(baseBitmap.width, baseBitmap.height, baseBitmap.config) + val canvas = Canvas(afterBitmap) + val matrix = Matrix() + // 根据原图的中心位置旋转 + matrix.setRotate(degrees, baseBitmap.width.toFloat() / 2.0f, baseBitmap.height.toFloat() / 2.0f) + canvas.drawBitmap(baseBitmap, matrix, paint) + return afterBitmap + } + + /** + * 图片绕中心旋转 + */ + fun rotaingImage(bitmap: Bitmap, angle: Int): Bitmap { + //旋转图片 动作 + val matrix = Matrix() + matrix.postRotate(angle.toFloat()) + // 创建新的图片 + return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/CountdownUtill.java b/utill/src/main/java/com/lennon/cn/utill/utill/CountdownUtill.java new file mode 100644 index 0000000..5d33a04 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/CountdownUtill.java @@ -0,0 +1,57 @@ +package com.lennon.cn.utill.utill; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.os.CountDownTimer; +import android.widget.TextView; +import lennon.com.utill.R; + +/** + * Created by lennon on 2017/9/24. + */ + +public class CountdownUtill extends CountDownTimer { + private Activity mActivity; + private TextView mBtn; + private boolean mIsEnable = false; + private Listener listener; + private String startString = "", endString = ""; + + public CountdownUtill(Activity activity, long millisInFuture, long countDownInterval, TextView button, Listener listener) { + super(millisInFuture, countDownInterval); + this.mActivity = activity; + this.mBtn = button; + this.mIsEnable = true; + this.listener = listener; + } + + @SuppressLint("SetTextI18n") + @Override + public void onFinish() { + mBtn.setText("0" + mActivity.getResources().getString(R.string.resend_sms)); + if (listener != null) { + listener.onFinish(); + } + } + + @SuppressLint("SetTextI18n") + @Override + public void onTick(long millisUntilFinished) { + if (mIsEnable) + mBtn.setEnabled(false); + mIsEnable = false; + mBtn.setText(startString + (millisUntilFinished / 1000) + mActivity.getResources().getString(R.string.resend_sms) + endString); + } + + public void setStartString(String startString) { + this.startString = startString; + } + + public void setEndString(String endString) { + this.endString = endString; + } + + public interface Listener { + void onFinish(); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt b/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt new file mode 100644 index 0000000..53fcf17 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt @@ -0,0 +1,148 @@ +package com.lennon.cn.utill.utill + +import android.app.Activity +import android.app.Application +import android.content.ComponentCallbacks +import android.content.Context +import android.content.res.Configuration +import android.util.DisplayMetrics + +object DensityUtils { + private var appDensity: Float = 0.toFloat() + private var appScaledDensity: Float = 0.toFloat() + private var appDisplayMetrics: DisplayMetrics? = null + private var barHeight: Int = 0 + val WIDTH = "width" + val HEIGHT = "height" + + /** + * 在Application里初始化一下 + * @param application + */ + fun setDensity(application: Application) { + //获取application的DisplayMetrics + appDisplayMetrics = application.resources.displayMetrics + //获取状态栏高度 + barHeight = getStatusBarHeight(application) + + if (appDensity == 0f) { + //初始化的时候赋值 + appDensity = appDisplayMetrics!!.density + appScaledDensity = appDisplayMetrics!!.scaledDensity + + //添加字体变化的监听 + application.registerComponentCallbacks(object : ComponentCallbacks { + override fun onConfigurationChanged(newConfig: Configuration) { + //字体改变后,将appScaledDensity重新赋值 + if (newConfig != null && newConfig.fontScale > 0) { + appScaledDensity = application.resources.displayMetrics.scaledDensity + } + } + + override fun onLowMemory() {} + }) + } + } + + /** + * 此方法在BaseActivity中做初始化(如果不封装BaseActivity的话,直接用下面那个方法就好) + * 在setContentView()之前设置 + * @param activity + */ + fun setDefault(activity: Activity) { + setAppOrientation(activity, WIDTH) + } + + /** + * 此方法用于在某一个Activity里面更改适配的方向 + * 在setContentView()之前设置 + * @param activity + * @param orientation + */ + fun setOrientation(activity: Activity, orientation: String) { + setAppOrientation(activity, orientation) + } + /** + * 此方法用于在某一个Activity里面更改适配的方向 + * 在setContentView()之前设置 + * @param activity + * @param orientation + */ + fun setOrientation(activity: Activity, orientation: String,f: Float) { + setAppOrientation(activity, orientation,f) + } + /** + * targetDensity + * targetScaledDensity + * targetDensityDpi + * 这三个参数是统一修改过后的值 + * orientation:方向值,传入width或height + */ + private fun setAppOrientation(activity: Activity, orientation: String, f: Float) { + + val targetDensity: Float + + if (orientation == "height") { + targetDensity = (appDisplayMetrics!!.heightPixels - barHeight) / f//设计图的高度 单位:dp + } else { + targetDensity = appDisplayMetrics!!.widthPixels / f//设计图的宽度 单位:dp + } + + val targetScaledDensity = targetDensity * (appScaledDensity / appDensity) + val targetDensityDpi = (160 * targetDensity).toInt() + + /** + * + * 最后在这里将修改过后的值赋给系统参数 + * 只修改Activity的density值 + */ + val activityDisplayMetrics = activity.resources.displayMetrics + activityDisplayMetrics.density = targetDensity + activityDisplayMetrics.scaledDensity = targetScaledDensity + activityDisplayMetrics.densityDpi = targetDensityDpi + } + + /** + * targetDensity + * targetScaledDensity + * targetDensityDpi + * 这三个参数是统一修改过后的值 + * orientation:方向值,传入width或height + */ + private fun setAppOrientation(activity: Activity, orientation: String) { + val targetDensity: Float + if (orientation == "height") { + targetDensity = (appDisplayMetrics!!.heightPixels - barHeight) / 1080f//设计图的高度 单位:dp + } else { + targetDensity = appDisplayMetrics!!.widthPixels / 420f//设计图的宽度 单位:dp + } + + val targetScaledDensity = targetDensity * (appScaledDensity / appDensity) + val targetDensityDpi = (160 * targetDensity).toInt() + + /** + * + * 最后在这里将修改过后的值赋给系统参数 + * 只修改Activity的density值 + */ + val activityDisplayMetrics = activity.resources.displayMetrics + activityDisplayMetrics.density = targetDensity + activityDisplayMetrics.scaledDensity = targetScaledDensity + activityDisplayMetrics.densityDpi = targetDensityDpi + } + + /** + * 获取状态栏高度 + * + * @param context + * @return + */ + fun getStatusBarHeight(context: Context): Int { + var result = 0 + val resourceId = context.resources.getIdentifier("status_bar_height", "dimen", "android") + if (resourceId > 0) { + result = context.resources.getDimensionPixelSize(resourceId) + } + return result + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/FileUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/FileUtil.java new file mode 100644 index 0000000..9cebc61 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/FileUtil.java @@ -0,0 +1,859 @@ +package com.lennon.cn.utill.utill; + +import android.app.Application; +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.os.Environment; +import android.os.StatFs; +import android.text.TextUtils; +import cn.droidlover.xdroidmvp.log.XLog; +import com.lennon.cn.utill.base.BaseApplication; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + + +/** + * 文件操作工具包 + * + * @author liux (http://my.oschina.net/liux) + * @version 1.0 + * @created 2012-3-21 + */ +public class FileUtil { + public static AssetFileDescriptor getAssetFileDescription(Application application, String filename) throws IOException { + AssetManager manager = application.getAssets(); + return manager.openFd(filename); + } + + /** + * 根据文件绝对路径获取文件名 + * + * @param filePath + * @return + */ + public static String getFileName(String filePath) { + if (TextUtils.isEmpty(filePath)) { + return ""; + } else { + return filePath.substring(filePath.lastIndexOf(File.separator) + 1); + } + } + + //创建一个临时目录,用于复制临时文件,如assets目录下的离线资源文件 + public static String createTmpDir(Context context) { + String sampleDir = "baiduTTS"; + String tmpDir = Environment.getExternalStorageDirectory().toString() + "/" + sampleDir; + if (!FileUtil.makeDir(tmpDir)) { + tmpDir = context.getExternalFilesDir(sampleDir).getAbsolutePath(); + if (!FileUtil.makeDir(sampleDir)) { + throw new RuntimeException("create model resources dir failed :" + tmpDir); + } + } + return tmpDir; + } + + public static boolean makeDir(String dirPath) { + File file = new File(dirPath); + if (!file.exists()) { + return file.mkdirs(); + } else { + return true; + } + } + + public static void copyFromAssets(AssetManager assets, String source, String dest, boolean isCover) throws IOException { + File file = new File(dest); + if (isCover || (!isCover && !file.exists())) { + InputStream is = null; + FileOutputStream fos = null; + try { + is = assets.open(source); + String path = dest; + fos = new FileOutputStream(path); + byte[] buffer = new byte[1024]; + int size = 0; + while ((size = is.read(buffer, 0, 1024)) >= 0) { + fos.write(buffer, 0, size); + } + } finally { + if (fos != null) { + try { + fos.close(); + } finally { + if (is != null) { + is.close(); + } + } + } + } + } + } + + /** + * 写文本文件 在Android系统中,文件保存在 /data/data/PACKAGE_NAME/files 目录下 + * + * @param context + */ + public static void write(Context context, String fileName, String content) { + if (content == null) { + content = ""; + } + + try { + FileOutputStream fos = context.openFileOutput(fileName, + Context.MODE_PRIVATE); + fos.write(content.getBytes()); + + fos.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 读取文本文件 + * + * @param context + * @param fileName + * @return + */ + public static String read(Context context, String fileName) { + try { + FileInputStream in = context.openFileInput(fileName); + return readInStream(in); + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + public static String readInStream(InputStream inStream) { + try { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[512]; + int length = -1; + while ((length = inStream.read(buffer)) != -1) { + outStream.write(buffer, 0, length); + } + + outStream.close(); + inStream.close(); + return outStream.toString(); + } catch (IOException e) { + XLog.e("FileTest " + e.getMessage()); + } + return null; + } + + public static File createFile(String folderPath, String fileName) { + File destDir = new File(folderPath); + if (!destDir.exists()) { + destDir.mkdirs(); + } + return new File(folderPath, fileName + fileName); + } + + /** + * 向手机写图片 + * + * @param buffer + * @param folder + * @param fileName + * @return + */ + public static boolean writeFile(byte[] buffer, String folder, + String fileName) { + boolean writeSucc = false; + + boolean sdCardExist = Environment.getExternalStorageState().equals( + Environment.MEDIA_MOUNTED); + + String folderPath = ""; + if (sdCardExist) { + folderPath = Environment.getExternalStorageDirectory() + + File.separator + folder + File.separator; + } else { + writeSucc = false; + } + + File fileDir = new File(folderPath); + if (!fileDir.exists()) { + fileDir.mkdirs(); + } + + File file = new File(folderPath + fileName); + FileOutputStream out = null; + try { + out = new FileOutputStream(file); + out.write(buffer); + writeSucc = true; + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (out != null) { + out.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + return writeSucc; + } + + /** + * 根据文件的绝对路径获取文件名但不包含扩展名 + * + * @param filePath + * @return + */ + public static String getFileNameNoFormat(String filePath) { + if (StringUtils.isEmpty(filePath)) { + return ""; + } + int point = filePath.lastIndexOf('.'); + return filePath.substring(filePath.lastIndexOf(File.separator) + 1, + point); + } + + /** + * 获取文件扩展名 + * + * @param fileName + * @return + */ + public static String getFileFormat(String fileName) { + if (StringUtils.isEmpty(fileName)) { + return ""; + } + + int point = fileName.lastIndexOf('.'); + return fileName.substring(point + 1); + } + + /** + * 获取文件大小 + * + * @param filePath + * @return + */ + public static long getFileSize(String filePath) { + long size = 0; + + File file = new File(filePath); + if (file != null && file.exists()) { + size = file.length(); + } + return size; + } + + /** + * 获取文件大小 + * + * @param size 字节 + * @return + */ + public static String getFileSize(long size) { + if (size <= 0) { + return "0"; + } + java.text.DecimalFormat df = new java.text.DecimalFormat("##.##"); + float temp = (float) size / 1024; + if (temp >= 1024) { + return df.format(temp / 1024) + "M"; + } else { + return df.format(temp) + "K"; + } + } + + /** + * 转换文件大小 + * + * @param fileS + * @return B/KB/MB/GB + */ + public static String formatFileSize(long fileS) { + java.text.DecimalFormat df = new java.text.DecimalFormat("#.00"); + String fileSizeString = ""; + if (fileS < 1024) { + fileSizeString = df.format((double) fileS) + "B"; + } else if (fileS < 1048576) { + fileSizeString = df.format((double) fileS / 1024) + "KB"; + } else if (fileS < 1073741824) { + fileSizeString = df.format((double) fileS / 1048576) + "MB"; + } else { + fileSizeString = df.format((double) fileS / 1073741824) + "G"; + } + return fileSizeString; + } + + /** + * 获取目录文件大小 + * + * @param dir + * @return + */ + public static long getDirSize(File dir) { + if (dir == null) { + return 0; + } + if (!dir.isDirectory()) { + return 0; + } + long dirSize = 0; + File[] files = dir.listFiles(); + if (files != null) { + + for (File file : files) { + if (file.isFile()) { + dirSize += file.length(); + } else if (file.isDirectory()) { + dirSize += file.length(); + dirSize += getDirSize(file); // 递归调用继续统计 + } + } + } + return dirSize; + } + + public static AssetFileDescriptor getAssetFileDescription(String path) throws IOException { + return BaseApplication.Companion.getAppliction().getAssets().openFd(path); + } + + /** + * 获取目录文件个数 + * + * @return + */ + public long getFileList(File dir) { + long count = 0; + File[] files = dir.listFiles(); + count = files.length; + for (File file : files) { + if (file.isDirectory()) { + count = count + getFileList(file);// 递归 + count--; + } + } + return count; + } + + public static byte[] toBytes(InputStream in) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int ch; + while ((ch = in.read()) != -1) { + out.write(ch); + } + byte buffer[] = out.toByteArray(); + out.close(); + return buffer; + } + + /** + * 检查文件是否存在 + * + * @param name + * @return + */ + public static boolean checkFileExists(String name) { + boolean status; + if (!"".equals(name)) { + File path = Environment.getExternalStorageDirectory(); + File newPath = new File(path.toString() + name); + status = newPath.exists(); + } else { + status = false; + } + return status; + } + + /** + * 检查路径是否存在 + * + * @param path + * @return + */ + public static boolean checkFilePathExists(String path) { + return new File(path).exists(); + } + + /** + * 计算SD卡的剩余空间 + * + * @return 返回-1,说明没有安装sd卡 + */ + public static long getFreeDiskSpace() { + String status = Environment.getExternalStorageState(); + long freeSpace = 0; + if (status.equals(Environment.MEDIA_MOUNTED)) { + try { + File path = Environment.getExternalStorageDirectory(); + StatFs stat = new StatFs(path.getPath()); + long blockSize = stat.getBlockSize(); + long availableBlocks = stat.getAvailableBlocks(); + freeSpace = availableBlocks * blockSize / 1024; + } catch (Exception e) { + e.printStackTrace(); + } + } else { + return -1; + } + return (freeSpace); + } + + /** + * 新建目录 + * + * @param directoryName + * @return + */ + public static boolean createDirectory(String directoryName) { + boolean status; + if (!"".equals(directoryName)) { + File path = Environment.getExternalStorageDirectory(); + File newPath = new File(path.toString() + directoryName); + status = newPath.mkdir(); + status = true; + } else { + status = false; + } + return status; + } + + /** + * 检查是否安装SD卡 + * + * @return + */ + public static boolean checkSaveLocationExists() { + String sDCardStatus = Environment.getExternalStorageState(); + boolean status; + if (sDCardStatus.equals(Environment.MEDIA_MOUNTED)) { + status = true; + } else { + status = false; + } + return status; + } + + /** + * 检查是否安装外置的SD卡 + * + * @return + */ + public static boolean checkExternalSDExists() { + + Map evn = System.getenv(); + return evn.containsKey("SECONDARY_STORAGE"); + } + + /** + * 复制单个文件 + * + * @param oldPath String 原文件路径 如:c:/fqf.txt + * @param newPath String 复制后路径 如:f:/fqf.txt + * @return boolean + */ + public void copyFile(String oldPath, String newPath) { + try { + int bytesum = 0; + int byteread = 0; + File oldfile = new File(oldPath); + if (oldfile.exists()) { //文件存在时 + InputStream inStream = new FileInputStream(oldPath); //读入原文件 + FileOutputStream fs = new FileOutputStream(newPath); + byte[] buffer = new byte[1444]; + int length; + while ((byteread = inStream.read(buffer)) != -1) { + bytesum += byteread; //字节数 文件大小 + System.out.println(bytesum); + fs.write(buffer, 0, byteread); + } + inStream.close(); + } + } catch (Exception e) { + System.out.println("复制单个文件操作出错"); + e.printStackTrace(); + + } + } + + public static void copyFileforAsses(String assetName, String newPath) { + try { + int bytesum = 0; + int byteread = 0; + InputStream inStream = BaseApplication.Companion.context().getAssets().open(assetName); + FileOutputStream fs = new FileOutputStream(newPath); + byte[] buffer = new byte[1444]; + int length; + while ((byteread = inStream.read(buffer)) != -1) { + bytesum += byteread; //字节数 文件大小 + System.out.println(bytesum); + fs.write(buffer, 0, byteread); + } + inStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 删除目录(包括:目录里的所有文件) + * + * @param fileName + * @return + */ + public static boolean deleteDirectory(String fileName) { + boolean status; + SecurityManager checker = new SecurityManager(); + + if (!"".equals(fileName)) { + + File path = Environment.getExternalStorageDirectory(); + File newPath = new File(path.toString() + fileName); + checker.checkDelete(newPath.toString()); + if (newPath.isDirectory()) { + String[] listfile = newPath.list(); + try { + for (int i = 0; i < listfile.length; i++) { + File deletedFile = new File(newPath.toString() + "/" + + listfile[i].toString()); + deletedFile.delete(); + } + newPath.delete(); + status = true; + } catch (Exception e) { + e.printStackTrace(); + status = false; + } + + } else { + status = false; + } + } else { + status = false; + } + return status; + } + + /** + * 删除文件 + * + * @param fileName + * @return + */ + public static boolean deleteFile(String fileName) { + boolean status; + SecurityManager checker = new SecurityManager(); + + if (!"".equals(fileName)) { + + File path = Environment.getExternalStorageDirectory(); + File newPath = new File(path.toString() + fileName); + checker.checkDelete(newPath.toString()); + if (newPath.isFile()) { + try { + newPath.delete(); + status = true; + } catch (SecurityException se) { + se.printStackTrace(); + status = false; + } + } else { + status = false; + } + } else { + status = false; + } + return status; + } + + /** + * 删除空目录 + *

+ * 返回 0代表成功 ,1 代表没有删除权限, 2代表不是空目录,3 代表未知错误 + * + * @return + */ + public static int deleteBlankPath(String path) { + File f = new File(path); + if (!f.canWrite()) { + return 1; + } + if (f.list() != null && f.list().length > 0) { + return 2; + } + if (f.delete()) { + return 0; + } + return 3; + } + + /** + * 重命名 + * + * @param oldName + * @param newName + * @return + */ + public static boolean reNamePath(String oldName, String newName) { + File f = new File(oldName); + return f.renameTo(new File(newName)); + } + + /** + * 删除文件 + * + * @param filePath + */ + public static boolean deleteFileWithPath(String filePath) { + SecurityManager checker = new SecurityManager(); + File f = new File(filePath); + checker.checkDelete(filePath); + if (f.isFile()) { + f.delete(); + return true; + } + return false; + } + + /** + * 清空一个文件夹 + */ + public static void clearFileWithPath(String filePath) { + List files = FileUtil.listPathFiles(filePath); + if (files.isEmpty()) { + return; + } + for (File f : files) { + if (f.isDirectory()) { + clearFileWithPath(f.getAbsolutePath()); + } else { + f.delete(); + } + } + } + + /** + * 获取SD卡的根目录 + * + * @return + */ + public static String getSDRoot() { + + return Environment.getExternalStorageDirectory().getAbsolutePath(); + } + + /** + * 获取手机外置SD卡的根目录 + * + * @return + */ + public static String getExternalSDRoot() { + + Map evn = System.getenv(); + + return evn.get("SECONDARY_STORAGE"); + } + + /** + * 列出root目录下所有子目录 + * + * @return 绝对路径 + */ + public static List listPath(String root) { + List allDir = new ArrayList(); + SecurityManager checker = new SecurityManager(); + File path = new File(root); + checker.checkRead(root); + // 过滤掉以.开始的文件夹 + if (path.isDirectory()) { + for (File f : path.listFiles()) { + if (f.isDirectory() && !f.getName().startsWith(".")) { + allDir.add(f.getAbsolutePath()); + } + } + } + return allDir; + } + + /** + * 获取一个文件夹下的所有文件 + * + * @param root + * @return + */ + public static List listPathFiles(String root) { + List allDir = new ArrayList(); + SecurityManager checker = new SecurityManager(); + File path = new File(root); + checker.checkRead(root); + File[] files = path.listFiles(); + for (File f : files) { + if (f.isFile()) { + allDir.add(f); + } else { + listPath(f.getAbsolutePath()); + } + } + return allDir; + } + + public enum PathStatus { + SUCCESS, EXITS, ERROR + } + + /** + * 创建目录 + */ + public static PathStatus createPath(String newPath) { + File path = new File(newPath); + if (path.exists()) { + return PathStatus.EXITS; + } + if (path.mkdir()) { + return PathStatus.SUCCESS; + } else { + return PathStatus.ERROR; + } + } + + /** + * 截取路径名 + * + * @return + */ + public static String getPathName(String absolutePath) { + int start = absolutePath.lastIndexOf(File.separator) + 1; + int end = absolutePath.length(); + return absolutePath.substring(start, end); + } + + /** + * 获取应用程序缓存文件夹下的指定目录 + * + * @param context + * @param dir + * @return + */ + public static String getAppCache(Context context, String dir) { + String savePath = context.getCacheDir().getAbsolutePath() + "/" + dir + "/"; + File savedir = new File(savePath); + if (!savedir.exists()) { + savedir.mkdirs(); + } + savedir = null; + return savePath; + } + + /** + * 关闭流 + * + * @param closeables + */ + public static void closeIO(Closeable... closeables) { + if (null == closeables || closeables.length <= 0) { + return; + } + for (Closeable cb : closeables) { + try { + if (null == cb) { + continue; + } + cb.close(); + } catch (IOException e) { + throw new RuntimeException( + FileUtil.class.getClass().getName(), e); + } + } + } + + /** + * 图片写入文件 + * + * @param bitmap 图片 + * @param filePath 文件路径 + * @return 是否写入成功 + */ + public static boolean bitmapToFile(Bitmap bitmap, int quality, Bitmap.CompressFormat format, String filePath) { + boolean isSuccess = false; + if (bitmap == null) { + return isSuccess; + } + File file = new File(filePath.substring(0, + filePath.lastIndexOf(File.separator))); + if (!file.exists()) { + file.mkdirs(); + } + + OutputStream out = null; + try { + out = new BufferedOutputStream(new FileOutputStream(filePath), + 8 * 1024); + isSuccess = bitmap.compress(format, quality, out); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } finally { + closeIO(out); + } + return isSuccess; + } + + /** + * 获取SD卡下指定文件夹的绝对路径 + * + * @return 返回SD卡下的指定文件夹的绝对路径 + */ + public static String getSavePath(String folderName) { + return getSaveFolder(folderName).getAbsolutePath(); + } + + /** + * 获取文件夹对象 + * + * @return 返回SD卡下的指定文件夹对象,若文件夹不存在则创建 + */ + public static File getSaveFolder(String folderName) { + File file = new File(getSDCardPath() + File.separator + folderName + + File.separator); + file.mkdirs(); + return file; + } + + public static String getSDCardPath() { + return Environment.getExternalStorageDirectory().getAbsolutePath(); + } + + /** + * 写入文件 + * + * @param in + * @param file + */ + public static void writeFile(InputStream in, File file) throws IOException { + if (!file.getParentFile().exists()) { + file.getParentFile().mkdirs(); + } + + if (file != null && file.exists()) { + file.delete(); + } + + FileOutputStream out = new FileOutputStream(file); + byte[] buffer = new byte[1024 * 128]; + int len = -1; + while ((len = in.read(buffer)) != -1) { + out.write(buffer, 0, len); + } + out.flush(); + out.close(); + in.close(); + + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/GetDeviceId.java b/utill/src/main/java/com/lennon/cn/utill/utill/GetDeviceId.java new file mode 100644 index 0000000..5eac5a8 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/GetDeviceId.java @@ -0,0 +1,271 @@ +package com.lennon.cn.utill.utill; + +import android.app.Activity; +import android.content.Context; +import android.os.Build; +import android.text.TextUtils; +import cn.droidlover.xdroidmvp.cache.SharedPref; +import cn.droidlover.xdroidmvp.log.XLog; +import com.lennon.cn.utill.base.BaseApplication; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; + + +public class GetDeviceId { + //保存的文件 采用隐藏文件的形式进行保存 + private static final String DEVICES_FILE_NAME = ".DEVICES"; + + /** + * 获取设备唯一标识符 + * + * @param context + * @return + */ + public static String getDeviceId(Activity context) { + //读取保存的在sd卡中的唯一标识符 + List deviceIds = readDeviceID(context); + if (deviceIds != null) { + XLog.e("readDeviceID-------------------------" + deviceIds.toString()); + } + if (deviceIds != null && deviceIds.size() > 0) { + if (deviceIds.size() == 1) { + String deviceId = deviceIds.get(0); + //判断是否已经生成过, + if (deviceId != null && !"".equals(deviceId)) { + if (deviceId.contains("-") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + return VersionUtill.getUUID(); + } + return deviceId.replace("deviceId:", "").trim(); + } + } else { + if (!TextUtils.isEmpty(deviceIds.get(0))) { + String deviceId = deviceIds.get(0).replace("deviceId:", "").trim(); + //判断是否已经生成过, + if (!"".equals(deviceId)) { + return deviceId; + } + } + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + return VersionUtill.getUUID(); + } else { + return VersionUtill.getAndroidId(context); + } + } + + /** + * 读取固定的文件中的内容,这里就是读取sd卡中保存的设备唯一标识符 + * + * @param context + * @return + */ + public static List readDeviceID(Context context) { + File file = getDevicesDir(context); + File newFile = getNewDevicesDir(context); + List strings = getFileContent(new File(newFile.getAbsolutePath() + "/", "devices.txt")); + if (strings.size() > 0) { + return strings; + } +// return getFileContent(file); + +// try { +// List strings = new ArrayList<>(); +// FileInputStream fis = new FileInputStream(newFile); +// InputStreamReader isr = new InputStreamReader(fis, "UTF-8"); +// BufferedReader in = new BufferedReader(isr); +// String temp; +// while ((temp = in.readLine()) != null) { +// strings.add(temp); +// } +// in.close(); +// XLog.e("-------------readDeviceIdFile" + strings.toString()); +// if (strings.size() > 0) { +// return strings; +// } +// } catch (IOException e) { +// e.printStackTrace(); +// } + try { + strings = new ArrayList<>(); + FileInputStream fis = new FileInputStream(file); + InputStreamReader isr = new InputStreamReader(fis, "UTF-8"); + BufferedReader in = new BufferedReader(isr); + String temp; + while ((temp = in.readLine()) != null) { + strings.add(temp); + } + in.close(); + XLog.e("-------------readDeviceIdFile" + strings.toString()); + return strings; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + /** + * 保存 内容到 SD卡中, 这里保存的就是 设备唯一标识符 + * + * @param str + * @param context + */ + public static void saveDeviceID(String str, Context context) { + File file = getDevicesDir(context); + File newFile = getNewDevicesDir(context); + File f = new File(newFile.getPath(), newFile.getName() + ".txt"); + try { + if (f.exists()) { + f.delete(); + } + } catch (Exception e) { + e.printStackTrace(); + XLog.e(e.getMessage()); + } + +// try { + writeTxtToFile("deviceId:" + str, newFile.getAbsolutePath() + "/", "devices.txt"); +// writer.write("deviceId:" + str); +// writer.newLine(); + writeTxtToFile("deviceId-version:2", newFile.getAbsolutePath() + "/", "devices.txt"); +// out.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// writeTxtToFile("deviceId:" + str, file.getParent(), file.getName()); +// writeTxtToFile("deviceId-version:2", file.getParent(), file.getName()); + try { + FileOutputStream fos = new FileOutputStream(file); + OutputStreamWriter out = new OutputStreamWriter(fos, "UTF-8"); + BufferedWriter writer = new BufferedWriter(out); + writer.write("deviceId:" + str); + writer.newLine(); + writer.write("deviceId-version:2"); + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + + // 将字符串写入到文本文件中 + private static void writeTxtToFile(String strcontent, String filePath, String fileName) { + //生成文件夹之后,再生成文件,不然会出错 + makeFilePath(filePath, fileName); + + String strFilePath = filePath + fileName; + // 每次写入时,都换行写 + String strContent = strcontent + "\r\n"; + try { + File file = new File(strFilePath); + if (!file.exists()) { + XLog.d("TestFile", "Create the file:" + strFilePath); + file.getParentFile().mkdirs(); + file.createNewFile(); + } + RandomAccessFile raf = new RandomAccessFile(file, "rwd"); + raf.seek(file.length()); + raf.write(strContent.getBytes()); + raf.close(); + } catch (Exception e) { + XLog.e("TestFile", "Error on write File:" + e); + } + } + +//生成文件 + + private static File makeFilePath(String filePath, String fileName) { + File file = null; + makeRootDirectory(filePath); + try { + file = new File(filePath + fileName); + if (!file.exists()) { + file.createNewFile(); + } + } catch (Exception e) { + e.printStackTrace(); + } + return file; + } + +//生成文件夹 + + private static void makeRootDirectory(String filePath) { + File file = null; + try { + file = new File(filePath); + if (!file.exists()) { + file.mkdir(); + } + } catch (Exception e) { + XLog.e("error:", e + ""); + } + } + + //读取指定目录下的所有TXT文件的文件内容 + private static List getFileContent(File file) { + List content = new ArrayList<>(); + if (!file.isDirectory()) { //检查此路径名的文件是否是一个目录(文件夹) +// if (file.getName().endsWith("txt")) {//文件格式为""文件 + try { + InputStream instream = new FileInputStream(file); + if (instream != null) { + InputStreamReader inputreader + = new InputStreamReader(instream, "UTF-8"); + BufferedReader buffreader = new BufferedReader(inputreader); + String line = ""; + //分行读取 + while ((line = buffreader.readLine()) != null) { + String s = "" + line; + content.add(s); + } + instream.close();//关闭输入流 + } + } catch (java.io.FileNotFoundException e) { + XLog.e("TestFile", "The File doesn't not exist."); + } catch (IOException e) { + XLog.e("TestFile", e.getMessage()); + } +// } + } + return content; + } + + /** + * 统一处理设备唯一标识 保存的文件的地址 + * + * @param context + * @return + */ + private static File getDevicesDir(Context context) { + File mCropFile = null; + mCropFile = new File(BaseApplication.Companion.getDataFile(), DEVICES_FILE_NAME); + if (!mCropFile.getParentFile().exists()) { + mCropFile.getParentFile().mkdirs(); + } + if (!mCropFile.exists()) { + try { + mCropFile.createNewFile(); + } catch (Exception e) { + e.printStackTrace(); + } + } + XLog.e("getDevicesDir-----------------" + context.getFilesDir() + "-------------" + mCropFile.getParent() + "-----------------" + mCropFile.getName()); + return mCropFile; + } + + private static File getNewDevicesDir(Context context) { + File mCropFile = null; + mCropFile = new File(context.getFilesDir(), "devices"); + if (!mCropFile.getParentFile().exists()) { + mCropFile.getParentFile().mkdirs(); + } + XLog.e("getDevicesDir-----------------" + context.getFilesDir() + "-------------" + mCropFile.getParent() + "-----------------" + mCropFile.getName() + "-------------" + mCropFile.getPath()); + return mCropFile; + } + +} + diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/IDCard.java b/utill/src/main/java/com/lennon/cn/utill/utill/IDCard.java new file mode 100644 index 0000000..ff89d4e --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/IDCard.java @@ -0,0 +1,196 @@ +package com.lennon.cn.utill.utill; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Hashtable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created by lennon on 2017/6/2. + */ + +public class IDCard { + /*********************************** 身份证验证开始 ****************************************/ + /** + * 身份证号码验证 1、号码的结构 公民身份号码是特征组合码,由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为:六位数字地址码, + * 八位数字出生日期码,三位数字顺序码和一位数字校验码。 2、地址码(前六位数) + * 表示编码对象常住户口所在县(市、旗、区)的行政区划代码,按GB/T2260的规定执行。 3、出生日期码(第七位至十四位) + * 表示编码对象出生的年、月、日,按GB/T7408的规定执行,年、月、日代码之间不用分隔符。 4、顺序码(第十五位至十七位) + * 表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号, 顺序码的奇数分配给男性,偶数分配给女性。 5、校验码(第十八位数) + * (1)十七位数字本体码加权求和公式 S = Sum(Ai * Wi), i = 0, ... , 16 ,先对前17位数字的权求和 + * Ai:表示第i位置上的身份证号码数字值 Wi:表示第i位置上的加权因子 Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2 + * (2)计算模 Y = mod(S, 11) (3)通过模得到对应的校验码 Y: 0 1 2 3 4 5 6 7 8 9 10 校验码: 1 0 X 9 8 7 6 5 4 3 2 + */ + + /** + * 功能:身份证的有效验证 + * + * @param IDStr 身份证号 + * @return 有效:返回"" 无效:返回String信息 + * @throws ParseException + */ + public static String IDCardValidate(String IDStr) { + String errorInfo = "";// 记录错误信息 + String[] ValCodeArr = {"1", "0", "x", "9", "8", "7", "6", "5", "4", + "3", "2"}; + String[] Wi = {"7", "9", "10", "5", "8", "4", "2", "1", "6", "3", "7", + "9", "10", "5", "8", "4", "2"}; + String Ai = ""; + // ================ 号码的长度 15位或18位 ================ + if (IDStr.length() != 15 && IDStr.length() != 18) { + errorInfo = "身份证号码长度应该为15位或18位。"; + return errorInfo; + } + // =======================(end)======================== + + // ================ 数字 除最后以为都为数字 ================ + if (IDStr.length() == 18) { + Ai = IDStr.substring(0, 17); + } else { + Ai = IDStr.substring(0, 6) + "19" + IDStr.substring(6, 15); + } + if (!isNumeric(Ai)) { + errorInfo = "身份证15位号码都应为数字 ; 18位号码除最后一位外,都应为数字。"; + return errorInfo; + } + // =======================(end)======================== + + // ================ 出生年月是否有效 ================ + String strYear = Ai.substring(6, 10);// 年份 + String strMonth = Ai.substring(10, 12);// 月份 + String strDay = Ai.substring(12, 14);// 月份 + if (!isDate(strYear + "-" + strMonth + "-" + strDay)) { + errorInfo = "身份证生日无效。"; + return errorInfo; + } + GregorianCalendar gc = new GregorianCalendar(); + SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd"); + try { + if ((gc.get(Calendar.YEAR) - Integer.parseInt(strYear)) > 150 + || (gc.getTime().getTime() - s.parse( + strYear + "-" + strMonth + "-" + strDay).getTime()) < 0) { + errorInfo = "身份证生日不在有效范围。"; + return errorInfo; + } + } catch (NumberFormatException e) { + e.printStackTrace(); + return "识别出错"; + } catch (ParseException e) { + e.printStackTrace(); + return "识别出错"; + } + if (Integer.parseInt(strMonth) > 12 || Integer.parseInt(strMonth) == 0) { + errorInfo = "身份证月份无效"; + return errorInfo; + } + if (Integer.parseInt(strDay) > 31 || Integer.parseInt(strDay) == 0) { + errorInfo = "身份证日期无效"; + return errorInfo; + } + // =====================(end)===================== + + // ================ 地区码时候有效 ================ + Hashtable h = GetAreaCode(); + if (h.get(Ai.substring(0, 2)) == null) { + errorInfo = "身份证地区编码错误。"; + return errorInfo; + } + // ============================================== + + // ================ 判断最后一位的值 ================ + int TotalmulAiWi = 0; + for (int i = 0; i < 17; i++) { + TotalmulAiWi = TotalmulAiWi + + Integer.parseInt(String.valueOf(Ai.charAt(i))) + * Integer.parseInt(Wi[i]); + } + int modValue = TotalmulAiWi % 11; + String strVerifyCode = ValCodeArr[modValue]; + Ai = Ai + strVerifyCode; + + if (IDStr.length() == 18) { + if (!Ai.equals(IDStr)) { + errorInfo = "身份证无效,不是合法的身份证号码"; + return errorInfo; + } + } else { + return ""; + } + // =====================(end)===================== + return ""; + } + + /** + * 功能:设置地区编码 + * + * @return Hashtable 对象 + */ + @SuppressWarnings("unchecked") + private static Hashtable GetAreaCode() { + Hashtable hashtable = new Hashtable(); + hashtable.put("11", "北京"); + hashtable.put("12", "天津"); + hashtable.put("13", "河北"); + hashtable.put("14", "山西"); + hashtable.put("15", "内蒙古"); + hashtable.put("21", "辽宁"); + hashtable.put("22", "吉林"); + hashtable.put("23", "黑龙江"); + hashtable.put("31", "上海"); + hashtable.put("32", "江苏"); + hashtable.put("33", "浙江"); + hashtable.put("34", "安徽"); + hashtable.put("35", "福建"); + hashtable.put("36", "江西"); + hashtable.put("37", "山东"); + hashtable.put("41", "河南"); + hashtable.put("42", "湖北"); + hashtable.put("43", "湖南"); + hashtable.put("44", "广东"); + hashtable.put("45", "广西"); + hashtable.put("46", "海南"); + hashtable.put("50", "重庆"); + hashtable.put("51", "四川"); + hashtable.put("52", "贵州"); + hashtable.put("53", "云南"); + hashtable.put("54", "西藏"); + hashtable.put("61", "陕西"); + hashtable.put("62", "甘肃"); + hashtable.put("63", "青海"); + hashtable.put("64", "宁夏"); + hashtable.put("65", "新疆"); + hashtable.put("71", "台湾"); + hashtable.put("81", "香港"); + hashtable.put("82", "澳门"); + hashtable.put("91", "国外"); + return hashtable; + } + + /** + * 功能:判断字符串是否为数字 + * + * @param str + * @return + */ + private static boolean isNumeric(String str) { + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Pattern pattern = Pattern.compile("[0-9]*"); + Matcher isNum = pattern.matcher(str); + return isNum.matches(); + } + + /** + * 功能:判断字符串是否为日期格式 + * + * @param + * @return + */ + public static boolean isDate(String strDate) { + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Pattern pattern = Pattern + .compile("^((\\d{2}(([02468][048])|([13579][26]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])))))|(\\d{2}(([02468][1235679])|([13579][01345789]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|(1[0-9])|(2[0-8]))))))(\\s(((0?[0-9])|([1-2][0-3]))\\:([0-5]?[0-9])((\\s)|(\\:([0-5]?[0-9])))))?$"); + Matcher m = pattern.matcher(strDate); + return m.matches(); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/JsonUtils.java b/utill/src/main/java/com/lennon/cn/utill/utill/JsonUtils.java new file mode 100644 index 0000000..599883e --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/JsonUtils.java @@ -0,0 +1,136 @@ +package com.lennon.cn.utill.utill; + + +import android.text.TextUtils; +import cn.droidlover.xdroidmvp.log.XLog; +import com.google.gson.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class JsonUtils { + private static Gson mGson; + + static { + if (mGson == null) { + mGson = new Gson(); + } + } + + public static String toJson(Object object) { + return mGson.toJson(object); + } + + @SuppressWarnings("unchecked") + public static List> str2ListMap(String jsonArrStr) { + System.out.println(jsonArrStr); + List> jsonObjList = new ArrayList>(); + List jsonList = jsonToList(jsonArrStr); + for (Object object : jsonList) { + String jsonStr = mGson.toJson(object); + Map json = jsonToMap(jsonStr); + System.out.println(json.toString()); + jsonObjList.add((Map) json); + } + return jsonObjList; + } + + /** + * 将传入的json字符串解析为List集合 + * + * @param jsonStr + * @return + */ + public static List jsonToList(String jsonStr) { + List ObjectList = null; + java.lang.reflect.Type type = new com.google.gson.reflect.TypeToken>() { + }.getType(); + ObjectList = mGson.fromJson(jsonStr, type); + return ObjectList; + } + + /** + * 将传入的json字符串解析为Map集合 + * + * @param jsonStr + * @return + */ + public static Map jsonToMap(String jsonStr) { + Map ObjectMap = null; + java.lang.reflect.Type type = new com.google.gson.reflect.TypeToken>() { + }.getType(); + ObjectMap = mGson.fromJson(jsonStr, type); + return ObjectMap; + } + + /** + * map转json字符串 + * + * @param m + * @return + */ + public static String map2json(Map m) { + mGson.toJson(m).toString(); + return mGson.toJson(m).toString(); + } + + /** + * JsonObject 转换为对象 + */ + public static Object jsonToObject(final Class cls, final JsonDeserializer deserializer, final JsonObject json) { + GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapter(cls, deserializer); + Gson gson = builder.create(); + gson.fromJson(json, cls); + return gson.fromJson(json, cls); + } + + public static T jsonToObject(String json, Class tClass) { + if (TextUtils.isEmpty(json)) { + return null; + } + T t; + try { + Gson gson = new Gson(); + t = gson.fromJson(json, tClass); + } catch (Exception e) { + XLog.e(e.getMessage()); + return null; + } + return t; + } + + /** + * JsonObject 转换为对象数组 + */ + public static List jsonToList(final Class cls, final JsonDeserializer deserializer, final JsonArray json) { + GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapter(cls, deserializer); + Gson gson = builder.create(); + List items = new ArrayList<>(); + for (JsonElement element : json) { + items.add(gson.fromJson(element, cls)); + } + return items; + } + + /** + * string to json array + * + * @param datas + * @param + * @return + */ + public static JsonArray listToJson(List datas) { + JsonParser parser = new JsonParser(); + Gson gson = new Gson(); + String str = gson.toJson(datas); + JsonArray aJson = parser.parse(str).getAsJsonArray(); + return aJson; + } + + public static String listToJsonString(List list) { + return new Gson().toJson(list); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/NetUtil.kt b/utill/src/main/java/com/lennon/cn/utill/utill/NetUtil.kt new file mode 100644 index 0000000..3ebbe18 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/NetUtil.kt @@ -0,0 +1,43 @@ +package com.lennon.cn.utill.utill + +import android.content.Context +import android.net.ConnectivityManager +import android.net.NetworkInfo + +/** + * Created by lennon on 2017/12/25. + */ + +object NetUtil { + /** + * 没有连接网络 + */ + val NETWORK_NONE = -1 + /** + * 移动网络 + */ + val NETWORK_MOBILE = 0 + /** + * 无线网络 + */ + val NETWORK_WIFI = 1 + + fun getNetWorkState(context: Context): Int { + // 得到连接管理器对象 + val connectivityManager = context + .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + + val activeNetworkInfo = connectivityManager.activeNetworkInfo + if (activeNetworkInfo != null && activeNetworkInfo.isConnected) { + + if (activeNetworkInfo.type == ConnectivityManager.TYPE_WIFI) { + return NETWORK_WIFI + } else if (activeNetworkInfo.type == ConnectivityManager.TYPE_MOBILE) { + return NETWORK_MOBILE + } + } else { + return NETWORK_NONE + } + return NETWORK_NONE + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/OSUtils.java b/utill/src/main/java/com/lennon/cn/utill/utill/OSUtils.java new file mode 100644 index 0000000..3ec862c --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/OSUtils.java @@ -0,0 +1,352 @@ +package com.lennon.cn.utill.utill; + +import android.os.Environment; +import android.text.TextUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class OSUtils { + private OSUtils() { + } + + /** + * ROM 类型 + */ + private static final ROM ROM_TYPE = initRomType(); + + private static final String KEY_DISPLAY_ID = "ro.build.display.id"; + private static final String KEY_BASE_OS_VERSION = "ro.build.version.base_os"; + private static final String KEY_CLIENT_ID_BASE = "ro.com.google.clientidbase"; + + // 小米 : MIUI + private static final String KEY_MIUI_VERSION = "ro.build.version.incremental"; // "7.6.15" + private static final String KEY_MIUI_VERSION_NANE = "ro.miui.ui.version.name"; // "V8" + private static final String KEY_MIUI_VERSION_CODE = "ro.miui.ui.version.code"; // "6" + + private static final String VALUE_MIUI_CLIENT_ID_BASE = "android-xiaomi"; + // 华为 : EMUI + private static final String KEY_EMUI_VERSION = "ro.build.version.emui"; // "EmotionUI_3.0" + private static final String KEY_EMUI_API_LEVEL = "ro.build.hw_emui_api_level"; // + private static final String KEY_EMUI_SYSTEM_VERSION = "ro.confg.hw_systemversion"; // "T1-A21wV100R001C233B008_SYSIMG" + // 魅族 : Flyme + private static final String KEY_FLYME_PUBLISHED = "ro.flyme.published"; // "true" + private static final String KEY_FLYME_SETUP = "ro.meizu.setupwizard.flyme"; // "true" + + private static final String VALUE_FLYME_DISPLAY_ID_CONTAIN = "Flyme"; // "Flyme OS 4.5.4.2U" + // OPPO : ColorOS + private static final String KEY_COLOROS_VERSION = "ro.oppo.theme.version"; // "703" + private static final String KEY_COLOROS_THEME_VERSION = "ro.oppo.version"; // "" + private static final String KEY_COLOROS_ROM_VERSION = "ro.rom.different.version"; // "ColorOS2.1" + + private static final String VALUE_COLOROS_BASE_OS_VERSION_CONTAIN = "OPPO"; // "OPPO/R7sm/R7sm:5.1.1/LMY47V/1440928800:user/release-keys" + private static final String VALUE_COLOROS_CLIENT_ID_BASE = "android-oppo"; + // vivo : FuntouchOS + private static final String KEY_FUNTOUCHOS_BOARD_VERSION = "ro.vivo.board.version"; // "MD" + private static final String KEY_FUNTOUCHOS_OS_NAME = "ro.vivo.os.name"; // "Funtouch" + private static final String KEY_FUNTOUCHOS_OS_VERSION = "ro.vivo.os.version"; // "3.0" + private static final String KEY_FUNTOUCHOS_DISPLAY_ID = "ro.vivo.os.build.display.id"; // "FuntouchOS_3.0" + private static final String KEY_FUNTOUCHOS_ROM_VERSION = "ro.vivo.rom.version"; // "rom_3.1" + + private static final String VALUE_FUNTOUCHOS_CLIENT_ID_BASE = "android-vivo"; + // Samsung + private static final String VALUE_SAMSUNG_BASE_OS_VERSION_CONTAIN = "samsung"; // "samsung/zeroltezc/zeroltechn:6.0.1/MMB29K/G9250ZCU2DQD1:user/release-keys" + private static final String VALUE_SAMSUNG_CLIENT_ID_BASE = "android-samsung"; + // Sony + private static final String KEY_SONY_PROTOCOL_TYPE = "ro.sony.irremote.protocol_type"; // "2" + private static final String KEY_SONY_ENCRYPTED_DATA = "ro.sony.fota.encrypteddata"; // "supported" + + private static final String VALUE_SONY_CLIENT_ID_BASE = "android-sonyericsson"; + // 乐视 : eui + private static final String KEY_EUI_VERSION = "ro.letv.release.version"; // "5.9.023S" + private static final String KEY_EUI_VERSION_DATE = "ro.letv.release.version_date"; // "5.9.023S_03111" + private static final String KEY_EUI_NAME = "ro.product.letv_name"; // "乐1s" + private static final String KEY_EUI_MODEL = "ro.product.letv_model"; // "Letv X500" + // 金立 : amigo + private static final String KEY_AMIGO_ROM_VERSION = "ro.gn.gnromvernumber"; // "GIONEE ROM5.0.16" + private static final String KEY_AMIGO_SYSTEM_UI_SUPPORT = "ro.gn.amigo.systemui.support"; // "yes" + + private static final String VALUE_AMIGO_DISPLAY_ID_CONTAIN = "amigo"; // "amigo3.5.1" + private static final String VALUE_AMIGO_CLIENT_ID_BASE = "android-gionee"; + // 酷派 : yulong + private static final String KEY_YULONG_VERSION_RELEASE = "ro.yulong.version.release"; // "5.1.046.P1.150921.8676_M01" + private static final String KEY_YULONG_VERSION_TAG = "ro.yulong.version.tag"; // "LC" + + private static final String VALUE_YULONG_CLIENT_ID_BASE = "android-coolpad"; + // HTC : Sense + private static final String KEY_SENSE_BUILD_STAGE = "htc.build.stage"; // "2" + private static final String KEY_SENSE_BLUETOOTH_SAP = "ro.htc.bluetooth.sap"; // "true" + + private static final String VALUE_SENSE_CLIENT_ID_BASE = "android-htc-rev"; + // LG : LG + private static final String KEY_LG_SW_VERSION = "ro.lge.swversion"; // "D85720b" + private static final String KEY_LG_SW_VERSION_SHORT = "ro.lge.swversion_short"; // "V20b" + private static final String KEY_LG_FACTORY_VERSION = "ro.lge.factoryversion"; // "LGD857AT-00-V20b-CUO-CN-FEB-17-2015+0" + // 联想 + private static final String KEY_LENOVO_DEVICE = "ro.lenovo.device"; // "phone" + private static final String KEY_LENOVO_PLATFORM = "ro.lenovo.platform"; // "qualcomm" + private static final String KEY_LENOVO_ADB = "ro.lenovo.adb"; // "apkctl,speedup" + + private static final String VALUE_LENOVO_CLIENT_ID_BASE = "android-lenovo"; + + /** + * 获取 ROM 类型 + * + * @return ROM + */ + public static ROM getRomType() { + return ROM_TYPE; + } + + /** + * 初始化 ROM 类型 + */ + private static ROM initRomType() { + ROM rom = ROM.Other; + FileInputStream is = null; + try { + Properties buildProperties = new Properties(); + is = new FileInputStream(new File(Environment.getRootDirectory(), "build.prop")); + buildProperties.load(is); + + if (buildProperties.containsKey(KEY_MIUI_VERSION_NANE) || buildProperties.containsKey(KEY_MIUI_VERSION_CODE)) { + // MIUI + rom = ROM.MIUI; + if (buildProperties.containsKey(KEY_MIUI_VERSION_NANE)) { + String versionName = buildProperties.getProperty(KEY_MIUI_VERSION_NANE); + if (!TextUtils.isEmpty(versionName) && versionName.matches("[Vv]\\d+")) { // V8 + try { + rom.setBaseVersion(Integer.parseInt(versionName.split("[Vv]")[1])); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + if (buildProperties.containsKey(KEY_MIUI_VERSION)) { + String versionStr = buildProperties.getProperty(KEY_MIUI_VERSION); + if (!TextUtils.isEmpty(versionStr) && versionStr.matches("[\\d.]+")) { + rom.setVersion(versionStr); + } + } + } else if (buildProperties.containsKey(KEY_EMUI_VERSION) || buildProperties.containsKey(KEY_EMUI_API_LEVEL) + || buildProperties.containsKey(KEY_EMUI_SYSTEM_VERSION)) { + // EMUI + rom = ROM.EMUI; + if (buildProperties.containsKey(KEY_EMUI_VERSION)) { + String versionStr = buildProperties.getProperty(KEY_EMUI_VERSION); + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Matcher matcher = Pattern.compile("EmotionUI_([\\d.]+)").matcher(versionStr); // EmotionUI_3.0 + if (!TextUtils.isEmpty(versionStr) && matcher.find()) { + try { + String version = matcher.group(1); + rom.setVersion(version); + rom.setBaseVersion(Integer.parseInt(version.split("\\.")[0])); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } else if (buildProperties.containsKey(KEY_FLYME_SETUP) || buildProperties.containsKey(KEY_FLYME_PUBLISHED)) { + // Flyme + rom = ROM.Flyme; + if (buildProperties.containsKey(KEY_DISPLAY_ID)) { + String versionStr = buildProperties.getProperty(KEY_DISPLAY_ID); + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Matcher matcher = Pattern.compile("Flyme[^\\d]*([\\d.]+)[^\\d]*").matcher(versionStr); // Flyme OS 4.5.4.2U + if (!TextUtils.isEmpty(versionStr) && matcher.find()) { + try { + String version = matcher.group(1); + rom.setVersion(version); + rom.setBaseVersion(Integer.parseInt(version.split("\\.")[0])); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } else if (buildProperties.containsKey(KEY_COLOROS_VERSION) || buildProperties.containsKey(KEY_COLOROS_THEME_VERSION) + || buildProperties.containsKey(KEY_COLOROS_ROM_VERSION)) { + // ColorOS + rom = ROM.ColorOS; + if (buildProperties.containsKey(KEY_COLOROS_ROM_VERSION)) { + String versionStr = buildProperties.getProperty(KEY_COLOROS_ROM_VERSION); + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Matcher matcher = Pattern.compile("ColorOS([\\d.]+)").matcher(versionStr); // ColorOS2.1 + if (!TextUtils.isEmpty(versionStr) && matcher.find()) { + try { + String version = matcher.group(1); + rom.setVersion(version); + rom.setBaseVersion(Integer.parseInt(version.split("\\.")[0])); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } else if (buildProperties.containsKey(KEY_FUNTOUCHOS_OS_NAME) || buildProperties.containsKey(KEY_FUNTOUCHOS_OS_VERSION) + || buildProperties.containsKey(KEY_FUNTOUCHOS_DISPLAY_ID)) { + // FuntouchOS + rom = ROM.FuntouchOS; + if (buildProperties.containsKey(KEY_FUNTOUCHOS_OS_VERSION)) { + String versionStr = buildProperties.getProperty(KEY_FUNTOUCHOS_OS_VERSION); + if (!TextUtils.isEmpty(versionStr) && versionStr.matches("[\\d.]+")) { // 3.0 + try { + rom.setVersion(versionStr); + rom.setBaseVersion(Integer.parseInt(versionStr.split("\\.")[0])); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } else if (buildProperties.containsKey(KEY_EUI_VERSION) || buildProperties.containsKey(KEY_EUI_NAME) + || buildProperties.containsKey(KEY_EUI_MODEL)) { + // EUI + rom = ROM.EUI; + if (buildProperties.containsKey(KEY_EUI_VERSION)) { + String versionStr = buildProperties.getProperty(KEY_EUI_VERSION); + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Matcher matcher = Pattern.compile("([\\d.]+)[^\\d]*").matcher(versionStr); // 5.9.023S + if (!TextUtils.isEmpty(versionStr) && matcher.find()) { + try { + String version = matcher.group(1); + rom.setVersion(version); + rom.setBaseVersion(Integer.parseInt(version.split("\\.")[0])); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } else if (buildProperties.containsKey(KEY_AMIGO_ROM_VERSION) || buildProperties.containsKey(KEY_AMIGO_SYSTEM_UI_SUPPORT)) { + // amigo + rom = ROM.AmigoOS; + if (buildProperties.containsKey(KEY_DISPLAY_ID)) { + String versionStr = buildProperties.getProperty(KEY_DISPLAY_ID); + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Matcher matcher = Pattern.compile("amigo([\\d.]+)[a-zA-Z]*").matcher(versionStr); // "amigo3.5.1" + if (!TextUtils.isEmpty(versionStr) && matcher.find()) { + try { + String version = matcher.group(1); + rom.setVersion(version); + rom.setBaseVersion(Integer.parseInt(version.split("\\.")[0])); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } else if (buildProperties.containsKey(KEY_SONY_PROTOCOL_TYPE) || buildProperties.containsKey(KEY_SONY_ENCRYPTED_DATA)) { + // Sony + rom = ROM.Sony; + } else if (buildProperties.containsKey(KEY_YULONG_VERSION_RELEASE) || buildProperties.containsKey(KEY_YULONG_VERSION_TAG)) { + // YuLong + rom = ROM.YuLong; + } else if (buildProperties.containsKey(KEY_SENSE_BUILD_STAGE) || buildProperties.containsKey(KEY_SENSE_BLUETOOTH_SAP)) { + // Sense + rom = ROM.Sense; + } else if (buildProperties.containsKey(KEY_LG_SW_VERSION) || buildProperties.containsKey(KEY_LG_SW_VERSION_SHORT) + || buildProperties.containsKey(KEY_LG_FACTORY_VERSION)) { + // LG + rom = ROM.LG; + } else if (buildProperties.containsKey(KEY_LENOVO_DEVICE) || buildProperties.containsKey(KEY_LENOVO_PLATFORM) + || buildProperties.containsKey(KEY_LENOVO_ADB)) { + // Lenovo + rom = ROM.Lenovo; + } else if (buildProperties.containsKey(KEY_DISPLAY_ID)) { + String displayId = buildProperties.getProperty(KEY_DISPLAY_ID); + if (!TextUtils.isEmpty(displayId)) { + if (displayId.contains(VALUE_FLYME_DISPLAY_ID_CONTAIN)) { + return ROM.Flyme; + } else if (displayId.contains(VALUE_AMIGO_DISPLAY_ID_CONTAIN)) { + return ROM.AmigoOS; + } + } + } else if (buildProperties.containsKey(KEY_BASE_OS_VERSION)) { + String baseOsVersion = buildProperties.getProperty(KEY_BASE_OS_VERSION); + if (!TextUtils.isEmpty(baseOsVersion)) { + if (baseOsVersion.contains(VALUE_COLOROS_BASE_OS_VERSION_CONTAIN)) { + return ROM.ColorOS; + } else if (baseOsVersion.contains(VALUE_SAMSUNG_BASE_OS_VERSION_CONTAIN)) { + return ROM.SamSung; + } + } + } else if (buildProperties.containsKey(KEY_CLIENT_ID_BASE)) { + String clientIdBase = buildProperties.getProperty(KEY_CLIENT_ID_BASE); + switch (clientIdBase) { + case VALUE_MIUI_CLIENT_ID_BASE: + return ROM.MIUI; + case VALUE_COLOROS_CLIENT_ID_BASE: + return ROM.ColorOS; + case VALUE_FUNTOUCHOS_CLIENT_ID_BASE: + return ROM.FuntouchOS; + case VALUE_SAMSUNG_CLIENT_ID_BASE: + return ROM.SamSung; + case VALUE_SONY_CLIENT_ID_BASE: + return ROM.Sony; + case VALUE_YULONG_CLIENT_ID_BASE: + return ROM.YuLong; + case VALUE_SENSE_CLIENT_ID_BASE: + return ROM.Sense; + case VALUE_LENOVO_CLIENT_ID_BASE: + return ROM.Lenovo; + case VALUE_AMIGO_CLIENT_ID_BASE: + return ROM.AmigoOS; + default: + break; + } + } + + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return rom; + } + + public enum ROM { + MIUI, // 小米 + Flyme, // 魅族 + EMUI, // 华为 + ColorOS, // OPPO + FuntouchOS, // vivo + SmartisanOS, // 锤子 + EUI, // 乐视 + Sense, // HTC + AmigoOS, // 金立 + _360OS, // 奇酷360 + NubiaUI, // 努比亚 + H2OS, // 一加 + YunOS, // 阿里巴巴 + YuLong, // 酷派 + + SamSung, // 三星 + Sony, // 索尼 + Lenovo, // 联想 + LG, // LG + + Google, // 原生 + + Other; // CyanogenMod, Lewa OS, 百度云OS, Tencent OS, 深度OS, IUNI OS, Tapas OS, Mokee + + private int baseVersion = -1; + private String version; + + void setVersion(String version) { + this.version = version; + } + + void setBaseVersion(int baseVersion) { + this.baseVersion = baseVersion; + } + + public int getBaseVersion() { + return baseVersion; + } + + public String getVersion() { + return version; + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/PictureUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/PictureUtil.java new file mode 100644 index 0000000..da53e2c --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/PictureUtil.java @@ -0,0 +1,189 @@ +package com.lennon.cn.utill.utill; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import androidx.exifinterface.media.ExifInterface; +import android.net.Uri; +import android.os.Environment; +import android.util.Base64; +import com.lennon.cn.utill.base.BaseApplication; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + + +/** + * + * @author lennon + * @date 2017/6/7 + */ + +public class PictureUtil { + + public static String compressImage(String filePath, int quality) { + //获取一定尺寸的图片 + Bitmap bm = getSmallBitmap(filePath); + //获取相片拍摄角度 + int degree = readPictureDegree(filePath); + //旋转照片角度,防止头像横着显示 + if (degree != 0) { + bm = rotateBitmap(bm, degree); + } + File outputFile = new File(BaseApplication.Companion.context().getCacheDir() + "/image/" + FileUtil.getFileName(filePath)); + try { + if (!outputFile.exists()) { + outputFile.getParentFile().mkdirs(); + //outputFile.createNewFile(); + } else { + outputFile.delete(); + } + FileOutputStream out = new FileOutputStream(outputFile); + bm.compress(Bitmap.CompressFormat.JPEG, quality, out); + } catch (Exception e) { + } + return outputFile.getPath(); + } + + /** + * 根据路径获得图片信息并按比例压缩,返回bitmap + */ + public static Bitmap getSmallBitmap(String filePath) { + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true;//只解析图片边沿,获取宽高 + BitmapFactory.decodeFile(filePath, options); + // 计算缩放比 + options.inSampleSize = calculateInSampleSize(options, 480, 800); + // 完整解析图片返回bitmap + options.inJustDecodeBounds = false; + return BitmapFactory.decodeFile(filePath, options); + } + + + /** + * 获取照片角度 + * + * @param path + * @return + */ + public static int readPictureDegree(String path) { + int degree = 0; + try { + ExifInterface exifInterface = new ExifInterface(path); + int orientation = exifInterface.getAttributeInt( + ExifInterface.TAG_ORIENTATION, + ExifInterface.ORIENTATION_NORMAL); + switch (orientation) { + case ExifInterface.ORIENTATION_ROTATE_90: + degree = 90; + break; + case ExifInterface.ORIENTATION_ROTATE_180: + degree = 180; + break; + case ExifInterface.ORIENTATION_ROTATE_270: + degree = 270; + break; + default: + break; + } + } catch (IOException e) { + e.printStackTrace(); + } + return degree; + } + + /** + * 旋转照片 + * + * @param bitmap + * @param degress + * @return + */ + public static Bitmap rotateBitmap(Bitmap bitmap, int degress) { + if (bitmap != null) { + Matrix m = new Matrix(); + m.postRotate(degress); + bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), + bitmap.getHeight(), m, true); + return bitmap; + } + return bitmap; + } + + public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { + final int height = options.outHeight; + final int width = options.outWidth; + int inSampleSize = 1; + if (height > reqHeight || width > reqWidth) { + final int heightRatio = Math.round((float) height / (float) reqHeight); + final int widthRatio = Math.round((float) width / (float) reqWidth); + inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; + } + return inSampleSize; + } + + public static String bitmapToString(String filePath) { + Bitmap bm = getSmallBitmap(filePath, 480, 800); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bm.compress(Bitmap.CompressFormat.JPEG, 40, baos); + byte[] b = baos.toByteArray(); + return Base64.encodeToString(b, Base64.DEFAULT); + } + + + /** + * 根据路径获得突破并压缩返回bitmap用于显示 + * + * @param filePath 图片的路径 + * @param reqWidth 要求的图片的像素 + * @param reqHeight 要求的图片的像素 + * @return + */ + public static Bitmap getSmallBitmap(String filePath, int reqWidth, int reqHeight) { + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(filePath, options); + + options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); + + options.inJustDecodeBounds = false; + + return BitmapFactory.decodeFile(filePath, options); + } + + public static void deleteTempFile(String path) { + File file = new File(path); + if (file.exists()) { + file.delete(); + } + } + + public static void galleryAddPic(Context context, String path) { + Intent mediaScanIntent = new Intent( + Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + File f = new File(path); + Uri contentUri = Uri.fromFile(f); + mediaScanIntent.setData(contentUri); + context.sendBroadcast(mediaScanIntent); + } + + public static File getAlbumDir() { + File dir = new File( + Environment + .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), + getAlbumName()); + if (!dir.exists()) { + dir.mkdirs(); + } + return dir; + } + + public static String getAlbumName() { + return UtillString.SAMPLE_DIR_NAME; + } +} + diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/RandomUntil.java b/utill/src/main/java/com/lennon/cn/utill/utill/RandomUntil.java new file mode 100644 index 0000000..331f800 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/RandomUntil.java @@ -0,0 +1,149 @@ +package com.lennon.cn.utill.utill; + +import java.util.Random; + +/** + * 随机数、字母 工具类 + * Created by admin on 2017/2/20. + */ +public class RandomUntil { + + /** + * 生成一个0 到 count 之间的随机数 + * + * @param endNum + * @return + */ + public static int getNum(int endNum) { + if (endNum > 0) { + Random random = new Random(); + return random.nextInt(endNum); + } + return 0; + } + + /** + * 生成一个startNum 到 endNum之间的随机数(不包含endNum的随机数) + * + * @param startNum + * @param endNum + * @return + */ + public static int getNum(int startNum, int endNum) { + if (endNum > startNum) { + Random random = new Random(); + return random.nextInt(endNum - startNum) + startNum; + } + return 0; + } + + /** + * 生成随机大写字母 + * + * @return + */ + public static String getLargeLetter() { + Random random = new Random(); + return String.valueOf((char) (random.nextInt(27) + 'A')); + } + + /** + * 生成随机大写字母字符串 + * + * @return + */ + public static String getLargeLetter(int size) { + StringBuffer buffer = new StringBuffer(); + Random random = new Random(); + for (int i = 0; i < size; i++) { + buffer.append((char) (random.nextInt(27) + 'A')); + } + return buffer.toString(); + } + + /** + * 生成随机小写字母 + * + * @return + */ + public static String getSmallLetter() { + Random random = new Random(); + return String.valueOf((char) (random.nextInt(27) + 'a')); + } + + /** + * 生成随机小写字母字符串 + * + * @return + */ + public static String getSmallLetter(int size) { + StringBuffer buffer = new StringBuffer(); + Random random = new Random(); + for (int i = 0; i < size; i++) { + buffer.append((char) (random.nextInt(27) + 'a')); + } + return buffer.toString(); + } + + /** + * 数字与小写字母混编字符串 + * + * @param size + * @return + */ + public static String getNumSmallLetter(int size) { + StringBuffer buffer = new StringBuffer(); + Random random = new Random(); + for (int i = 0; i < size; i++) { + if (random.nextInt(2) % 2 == 0) {//字母 + buffer.append((char) (random.nextInt(27) + 'a')); + } else {//数字 + buffer.append(random.nextInt(10)); + } + } + return buffer.toString(); + } + + /** + * 数字与大写字母混编字符串 + * + * @param size + * @return + */ + public static String getNumLargeLetter(int size) { + StringBuffer buffer = new StringBuffer(); + Random random = new Random(); + for (int i = 0; i < size; i++) { + if (random.nextInt(2) % 2 == 0) {//字母 + buffer.append((char) (random.nextInt(27) + 'A')); + } else {//数字 + buffer.append(random.nextInt(10)); + } + } + return buffer.toString(); + } + + /** + * 数字与大小写字母混编字符串 + * + * @param size + * @return + */ + public static String getNumLargeSmallLetter(int size) { + StringBuffer buffer = new StringBuffer(); + Random random = new Random(); + for (int i = 0; i < size; i++) { + if (random.nextInt(2) % 2 == 0) {//字母 + if (random.nextInt(2) % 2 == 0) { + buffer.append((char) (random.nextInt(27) + 'A')); + } else { + buffer.append((char) (random.nextInt(27) + 'a')); + } + } else {//数字 + buffer.append(random.nextInt(10)); + } + } + return buffer.toString(); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/StatusBarUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/StatusBarUtil.java new file mode 100644 index 0000000..1f0efbf --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/StatusBarUtil.java @@ -0,0 +1,864 @@ +package com.lennon.cn.utill.utill; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.graphics.Color; +import android.os.Build; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.LinearLayout; +import androidx.annotation.ColorInt; +import androidx.annotation.IntDef; +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.drawerlayout.widget.DrawerLayout; +import lennon.com.utill.R; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class StatusBarUtil { + public final static int TYPE_MIUI = 0; + public final static int TYPE_FLYME = 1; + public final static int TYPE_M = 3;//6.0 + + @IntDef({TYPE_MIUI, + TYPE_FLYME, + TYPE_M}) + @Retention(RetentionPolicy.SOURCE) + @interface ViewType { + } + public static final int DEFAULT_STATUS_BAR_ALPHA = 112; + private static final int FAKE_STATUS_BAR_VIEW_ID = R.id.statusbarutil_fake_status_bar_view; + private static final int FAKE_TRANSLUCENT_VIEW_ID = R.id.statusbarutil_translucent_view; + private static final int TAG_KEY_HAVE_SET_OFFSET = -123; + + /** + * 设置状态栏颜色 + * + * @param activity 需要设置的 activity + * @param color 状态栏颜色值 + */ + public static void setColor(Activity activity, @ColorInt int color) { + setColor(activity, color, DEFAULT_STATUS_BAR_ALPHA); + } + + /** + * 设置状态栏颜色 + * + * @param activity 需要设置的activity + * @param color 状态栏颜色值 + * @param statusBarAlpha 状态栏透明度 + */ + + public static void setColor(Activity activity, @ColorInt int color, @IntRange(from = 0, to = 255) int statusBarAlpha) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + activity.getWindow().setStatusBarColor(calculateStatusColor(color, statusBarAlpha)); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); + View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID); + if (fakeStatusBarView != null) { + if (fakeStatusBarView.getVisibility() == View.GONE) { + fakeStatusBarView.setVisibility(View.VISIBLE); + } + fakeStatusBarView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha)); + } else { + decorView.addView(createStatusBarView(activity, color, statusBarAlpha)); + } + setRootView(activity); + } + } + + /** + * 为滑动返回界面设置状态栏颜色 + * + * @param activity 需要设置的activity + * @param color 状态栏颜色值 + */ + public static void setColorForSwipeBack(Activity activity, int color) { + setColorForSwipeBack(activity, color, DEFAULT_STATUS_BAR_ALPHA); + } + + /** + * 为滑动返回界面设置状态栏颜色 + * + * @param activity 需要设置的activity + * @param color 状态栏颜色值 + * @param statusBarAlpha 状态栏透明度 + */ + public static void setColorForSwipeBack(Activity activity, @ColorInt int color, + @IntRange(from = 0, to = 255) int statusBarAlpha) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + + ViewGroup contentView = ((ViewGroup) activity.findViewById(android.R.id.content)); + View rootView = contentView.getChildAt(0); + int statusBarHeight = getStatusBarHeight(activity); + if (rootView != null && rootView instanceof CoordinatorLayout) { + final CoordinatorLayout coordinatorLayout = (CoordinatorLayout) rootView; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + coordinatorLayout.setFitsSystemWindows(false); + contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha)); + boolean isNeedRequestLayout = contentView.getPaddingTop() < statusBarHeight; + if (isNeedRequestLayout) { + contentView.setPadding(0, statusBarHeight, 0, 0); + coordinatorLayout.post(new Runnable() { + @Override + public void run() { + coordinatorLayout.requestLayout(); + } + }); + } + } else { + coordinatorLayout.setStatusBarBackgroundColor(calculateStatusColor(color, statusBarAlpha)); + } + } else { + contentView.setPadding(0, statusBarHeight, 0, 0); + contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha)); + } + setTransparentForWindow(activity); + } + } + + /** + * 设置状态栏纯色 不加半透明效果 + * + * @param activity 需要设置的 activity + * @param color 状态栏颜色值 + */ + public static void setColorNoTranslucent(Activity activity, @ColorInt int color) { + setColor(activity, color, 0); + } + + /** + * 设置状态栏颜色(5.0以下无半透明效果,不建议使用) + * + * @param activity 需要设置的 activity + * @param color 状态栏颜色值 + */ + @Deprecated + public static void setColorDiff(Activity activity, @ColorInt int color) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + transparentStatusBar(activity); + ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content); + // 移除半透明矩形,以免叠加 + View fakeStatusBarView = contentView.findViewById(FAKE_STATUS_BAR_VIEW_ID); + if (fakeStatusBarView != null) { + if (fakeStatusBarView.getVisibility() == View.GONE) { + fakeStatusBarView.setVisibility(View.VISIBLE); + } + fakeStatusBarView.setBackgroundColor(color); + } else { + contentView.addView(createStatusBarView(activity, color)); + } + setRootView(activity); + } + + /** + * 使状态栏半透明 + *

+ * 适用于图片作为背景的界面,此时需要图片填充到状态栏 + * + * @param activity 需要设置的activity + */ + public static void setTranslucent(Activity activity) { + setTranslucent(activity, DEFAULT_STATUS_BAR_ALPHA); + } + + /** + * 使状态栏半透明 + *

+ * 适用于图片作为背景的界面,此时需要图片填充到状态栏 + * + * @param activity 需要设置的activity + * @param statusBarAlpha 状态栏透明度 + */ + public static void setTranslucent(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + setTransparent(activity); + addTranslucentView(activity, statusBarAlpha); + } + + /** + * 针对根布局是 CoordinatorLayout, 使状态栏半透明 + *

+ * 适用于图片作为背景的界面,此时需要图片填充到状态栏 + * + * @param activity 需要设置的activity + * @param statusBarAlpha 状态栏透明度 + */ + public static void setTranslucentForCoordinatorLayout(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + transparentStatusBar(activity); + addTranslucentView(activity, statusBarAlpha); + } + + /** + * 设置状态栏全透明 + * + * @param activity 需要设置的activity + */ + public static void setTransparent(Activity activity) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + transparentStatusBar(activity); + setRootView(activity); + } + + /** + * 使状态栏透明(5.0以上半透明效果,不建议使用) + *

+ * 适用于图片作为背景的界面,此时需要图片填充到状态栏 + * + * @param activity 需要设置的activity + */ + @Deprecated + public static void setTranslucentDiff(Activity activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + // 设置状态栏透明 + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + setRootView(activity); + } + } + + /** + * 为DrawerLayout 布局设置状态栏变色 + * + * @param activity 需要设置的activity + * @param drawerLayout DrawerLayout + * @param color 状态栏颜色值 + */ + public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) { + setColorForDrawerLayout(activity, drawerLayout, color, DEFAULT_STATUS_BAR_ALPHA); + } + + /** + * 为DrawerLayout 布局设置状态栏颜色,纯色 + * + * @param activity 需要设置的activity + * @param drawerLayout DrawerLayout + * @param color 状态栏颜色值 + */ + public static void setColorNoTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) { + setColorForDrawerLayout(activity, drawerLayout, color, 0); + } + + /** + * 为DrawerLayout 布局设置状态栏变色 + * + * @param activity 需要设置的activity + * @param drawerLayout DrawerLayout + * @param color 状态栏颜色值 + * @param statusBarAlpha 状态栏透明度 + */ + public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color, + @IntRange(from = 0, to = 255) int statusBarAlpha) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + activity.getWindow().setStatusBarColor(Color.TRANSPARENT); + } else { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + } + // 生成一个状态栏大小的矩形 + // 添加 statusBarView 到布局中 + ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); + View fakeStatusBarView = contentLayout.findViewById(FAKE_STATUS_BAR_VIEW_ID); + if (fakeStatusBarView != null) { + if (fakeStatusBarView.getVisibility() == View.GONE) { + fakeStatusBarView.setVisibility(View.VISIBLE); + } + fakeStatusBarView.setBackgroundColor(color); + } else { + contentLayout.addView(createStatusBarView(activity, color), 0); + } + // 内容布局不是 LinearLayout 时,设置padding top + if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) { + contentLayout.getChildAt(1) + .setPadding(contentLayout.getPaddingLeft(), getStatusBarHeight(activity) + contentLayout.getPaddingTop(), + contentLayout.getPaddingRight(), contentLayout.getPaddingBottom()); + } + // 设置属性 + setDrawerLayoutProperty(drawerLayout, contentLayout); + addTranslucentView(activity, statusBarAlpha); + } + + /** + * 设置 DrawerLayout 属性 + * + * @param drawerLayout DrawerLayout + * @param drawerLayoutContentLayout DrawerLayout 的内容布局 + */ + private static void setDrawerLayoutProperty(DrawerLayout drawerLayout, ViewGroup drawerLayoutContentLayout) { + ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1); + drawerLayout.setFitsSystemWindows(false); + drawerLayoutContentLayout.setFitsSystemWindows(false); + drawerLayoutContentLayout.setClipToPadding(true); + drawer.setFitsSystemWindows(false); + } + + /** + * 为DrawerLayout 布局设置状态栏变色(5.0以下无半透明效果,不建议使用) + * + * @param activity 需要设置的activity + * @param drawerLayout DrawerLayout + * @param color 状态栏颜色值 + */ + @Deprecated + public static void setColorForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + // 生成一个状态栏大小的矩形 + ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); + View fakeStatusBarView = contentLayout.findViewById(FAKE_STATUS_BAR_VIEW_ID); + if (fakeStatusBarView != null) { + if (fakeStatusBarView.getVisibility() == View.GONE) { + fakeStatusBarView.setVisibility(View.VISIBLE); + } + fakeStatusBarView.setBackgroundColor(calculateStatusColor(color, DEFAULT_STATUS_BAR_ALPHA)); + } else { + // 添加 statusBarView 到布局中 + contentLayout.addView(createStatusBarView(activity, color), 0); + } + // 内容布局不是 LinearLayout 时,设置padding top + if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) { + contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0); + } + // 设置属性 + setDrawerLayoutProperty(drawerLayout, contentLayout); + } + } + + /** + * 为 DrawerLayout 布局设置状态栏透明 + * + * @param activity 需要设置的activity + * @param drawerLayout DrawerLayout + */ + public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) { + setTranslucentForDrawerLayout(activity, drawerLayout, DEFAULT_STATUS_BAR_ALPHA); + } + + /** + * 为 DrawerLayout 布局设置状态栏透明 + * + * @param activity 需要设置的activity + * @param drawerLayout DrawerLayout + */ + public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout, + @IntRange(from = 0, to = 255) int statusBarAlpha) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + setTransparentForDrawerLayout(activity, drawerLayout); + addTranslucentView(activity, statusBarAlpha); + } + + /** + * 为 DrawerLayout 布局设置状态栏透明 + * + * @param activity 需要设置的activity + * @param drawerLayout DrawerLayout + */ + public static void setTransparentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + activity.getWindow().setStatusBarColor(Color.TRANSPARENT); + } else { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + } + + ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); + // 内容布局不是 LinearLayout 时,设置padding top + if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) { + contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0); + } + + // 设置属性 + setDrawerLayoutProperty(drawerLayout, contentLayout); + } + + /** + * 为 DrawerLayout 布局设置状态栏透明(5.0以上半透明效果,不建议使用) + * + * @param activity 需要设置的activity + * @param drawerLayout DrawerLayout + */ + @Deprecated + public static void setTranslucentForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + // 设置状态栏透明 + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + // 设置内容布局属性 + ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); + contentLayout.setFitsSystemWindows(true); + contentLayout.setClipToPadding(true); + // 设置抽屉布局属性 + ViewGroup vg = (ViewGroup) drawerLayout.getChildAt(1); + vg.setFitsSystemWindows(false); + // 设置 DrawerLayout 属性 + drawerLayout.setFitsSystemWindows(false); + } + } + + /** + * 为头部是 ImageView 的界面设置状态栏全透明 + * + * @param activity 需要设置的activity + * @param needOffsetView 需要向下偏移的 View + */ + public static void setTransparentForImageView(Activity activity, View needOffsetView) { + setTranslucentForImageView(activity, 0, needOffsetView); + } + + /** + * 为头部是 ImageView 的界面设置状态栏透明(使用默认透明度) + * + * @param activity 需要设置的activity + * @param needOffsetView 需要向下偏移的 View + */ + public static void setTranslucentForImageView(Activity activity, View needOffsetView) { + setTranslucentForImageView(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView); + } + + /** + * 为头部是 ImageView 的界面设置状态栏透明 + * + * @param activity 需要设置的activity + * @param statusBarAlpha 状态栏透明度 + * @param needOffsetView 需要向下偏移的 View + */ + public static void setTranslucentForImageView(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha, + View needOffsetView) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + setTransparentForWindow(activity); + addTranslucentView(activity, statusBarAlpha); + if (needOffsetView != null) { + Object haveSetOffset = needOffsetView.getTag(TAG_KEY_HAVE_SET_OFFSET); + if (haveSetOffset != null && (Boolean) haveSetOffset) { + return; + } + ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) needOffsetView.getLayoutParams(); + layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin + getStatusBarHeight(activity), + layoutParams.rightMargin, layoutParams.bottomMargin); + needOffsetView.setTag(TAG_KEY_HAVE_SET_OFFSET, true); + } + } + + /** + * 为 fragment 头部是 ImageView 的设置状态栏透明 + * + * @param activity fragment 对应的 activity + * @param needOffsetView 需要向下偏移的 View + */ + public static void setTranslucentForImageViewInFragment(Activity activity, View needOffsetView) { + setTranslucentForImageViewInFragment(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView); + } + + /** + * 为 fragment 头部是 ImageView 的设置状态栏透明 + * + * @param activity fragment 对应的 activity + * @param needOffsetView 需要向下偏移的 View + */ + public static void setTransparentForImageViewInFragment(Activity activity, View needOffsetView) { + setTranslucentForImageViewInFragment(activity, 0, needOffsetView); + } + + /** + * 为 fragment 头部是 ImageView 的设置状态栏透明 + * + * @param activity fragment 对应的 activity + * @param statusBarAlpha 状态栏透明度 + * @param needOffsetView 需要向下偏移的 View + */ + public static void setTranslucentForImageViewInFragment(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha, + View needOffsetView) { + setTranslucentForImageView(activity, statusBarAlpha, needOffsetView); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + clearPreviousSetting(activity); + } + } + + /** + * 隐藏伪状态栏 View + * + * @param activity 调用的 Activity + */ + public static void hideFakeStatusBarView(Activity activity) { + ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); + View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID); + if (fakeStatusBarView != null) { + fakeStatusBarView.setVisibility(View.GONE); + } + View fakeTranslucentView = decorView.findViewById(FAKE_TRANSLUCENT_VIEW_ID); + if (fakeTranslucentView != null) { + fakeTranslucentView.setVisibility(View.GONE); + } + } + + @TargetApi(Build.VERSION_CODES.M) + public static void setLightMode(Activity activity) { + setMIUIStatusBarDarkIcon(activity, true); + setMeizuStatusBarDarkIcon(activity, true); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + } + } + + @TargetApi(Build.VERSION_CODES.M) + public static void setDarkMode(Activity activity) { + setMIUIStatusBarDarkIcon(activity, false); + setMeizuStatusBarDarkIcon(activity, false); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + } + } + + /** + * 修改 MIUI V6 以上状态栏颜色 + */ + private static void setMIUIStatusBarDarkIcon(@NonNull Activity activity, boolean darkIcon) { + Class clazz = activity.getWindow().getClass(); + try { + Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams"); + Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE"); + int darkModeFlag = field.getInt(layoutParams); + Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class); + extraFlagField.invoke(activity.getWindow(), darkIcon ? darkModeFlag : 0, darkModeFlag); + } catch (Exception e) { + //e.printStackTrace(); + } + } + + /** + * 修改魅族状态栏字体颜色 Flyme 4.0 + */ + private static void setMeizuStatusBarDarkIcon(@NonNull Activity activity, boolean darkIcon) { + try { + WindowManager.LayoutParams lp = activity.getWindow().getAttributes(); + Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON"); + Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags"); + darkFlag.setAccessible(true); + meizuFlags.setAccessible(true); + int bit = darkFlag.getInt(null); + int value = meizuFlags.getInt(lp); + if (darkIcon) { + value |= bit; + } else { + value &= ~bit; + } + meizuFlags.setInt(lp, value); + activity.getWindow().setAttributes(lp); + } catch (Exception e) { + //e.printStackTrace(); + } + } + + /////////////////////////////////////////////////////////////////////////////////// + + @TargetApi(Build.VERSION_CODES.KITKAT) + private static void clearPreviousSetting(Activity activity) { + ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); + View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID); + if (fakeStatusBarView != null) { + decorView.removeView(fakeStatusBarView); + ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0); + rootView.setPadding(0, 0, 0, 0); + } + } + + /** + * 添加半透明矩形条 + * + * @param activity 需要设置的 activity + * @param statusBarAlpha 透明值 + */ + private static void addTranslucentView(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha) { + ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content); + View fakeTranslucentView = contentView.findViewById(FAKE_TRANSLUCENT_VIEW_ID); + if (fakeTranslucentView != null) { + if (fakeTranslucentView.getVisibility() == View.GONE) { + fakeTranslucentView.setVisibility(View.VISIBLE); + } + fakeTranslucentView.setBackgroundColor(Color.argb(statusBarAlpha, 0, 0, 0)); + } else { + contentView.addView(createTranslucentStatusBarView(activity, statusBarAlpha)); + } + } + + /** + * 生成一个和状态栏大小相同的彩色矩形条 + * + * @param activity 需要设置的 activity + * @param color 状态栏颜色值 + * @return 状态栏矩形条 + */ + private static View createStatusBarView(Activity activity, @ColorInt int color) { + return createStatusBarView(activity, color, 0); + } + + /** + * 生成一个和状态栏大小相同的半透明矩形条 + * + * @param activity 需要设置的activity + * @param color 状态栏颜色值 + * @param alpha 透明值 + * @return 状态栏矩形条 + */ + private static View createStatusBarView(Activity activity, @ColorInt int color, int alpha) { + // 绘制一个和状态栏一样高的矩形 + View statusBarView = new View(activity); + LinearLayout.LayoutParams params = + new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); + statusBarView.setLayoutParams(params); + statusBarView.setBackgroundColor(calculateStatusColor(color, alpha)); + statusBarView.setId(FAKE_STATUS_BAR_VIEW_ID); + return statusBarView; + } + + /** + * 设置根布局参数 + */ + private static void setRootView(Activity activity) { + ViewGroup parent = (ViewGroup) activity.findViewById(android.R.id.content); + for (int i = 0, count = parent.getChildCount(); i < count; i++) { + View childView = parent.getChildAt(i); + if (childView instanceof ViewGroup) { + childView.setFitsSystemWindows(true); + ((ViewGroup) childView).setClipToPadding(true); + } + } + } + + /** + * 设置透明 + */ + private static void setTransparentForWindow(Activity activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activity.getWindow().setStatusBarColor(Color.TRANSPARENT); + activity.getWindow() + .getDecorView() + .setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + activity.getWindow() + .setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + } + } + + /** + * 使状态栏透明 + */ + @TargetApi(Build.VERSION_CODES.KITKAT) + private static void transparentStatusBar(Activity activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + activity.getWindow().setStatusBarColor(Color.TRANSPARENT); + } else { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + } + } + + /** + * 创建半透明矩形 View + * + * @param alpha 透明值 + * @return 半透明 View + */ + private static View createTranslucentStatusBarView(Activity activity, int alpha) { + // 绘制一个和状态栏一样高的矩形 + View statusBarView = new View(activity); + LinearLayout.LayoutParams params = + new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); + statusBarView.setLayoutParams(params); + statusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0)); + statusBarView.setId(FAKE_TRANSLUCENT_VIEW_ID); + return statusBarView; + } + + /** + * 获取状态栏高度 + * + * @param context context + * @return 状态栏高度 + */ + private static int getStatusBarHeight(Context context) { + // 获得状态栏高度 + int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); + return context.getResources().getDimensionPixelSize(resourceId); + } + + /** + * 计算状态栏颜色 + * + * @param color color值 + * @param alpha alpha值 + * @return 最终的状态栏颜色 + */ + private static int calculateStatusColor(@ColorInt int color, int alpha) { + if (alpha == 0) { + return color; + } + float a = 1 - alpha / 255f; + int red = color >> 16 & 0xff; + int green = color >> 8 & 0xff; + int blue = color & 0xff; + red = (int) (red * a + 0.5); + green = (int) (green * a + 0.5); + blue = (int) (blue * a + 0.5); + return 0xff << 24 | red << 16 | green << 8 | blue; + } + /** + * 修改状态栏颜色,支持4.4以上版本 + * @param colorId 颜色 + */ + public static void setStatusBarColor(Activity activity, int colorId) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Window window = activity.getWindow(); + window.setStatusBarColor(colorId); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + //使用SystemBarTintManager,需要先将状态栏设置为透明 + setTranslucentStatus(activity); + SystemBarTintManager systemBarTintManager = new SystemBarTintManager(activity); + systemBarTintManager.setStatusBarTintEnabled(true);//显示状态栏 + systemBarTintManager.setStatusBarTintColor(colorId);//设置状态栏颜色 + } + } + + /** + * 设置状态栏透明 + */ + @TargetApi(19) + public static void setTranslucentStatus(Activity activity) { + // 5.0以上系统状态栏透明 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Window window = activity.getWindow(); + //清除透明状态栏 + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + //设置状态栏颜色必须添加 + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.setStatusBarColor(Color.TRANSPARENT);//设置透明 + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //19 + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + } + } + + /** + * 设置沉浸式状态栏 + * + * @param fontIconDark 状态栏字体和图标颜色是否为深色 + */ + public static void setImmersiveStatusBar(Activity activity, boolean fontIconDark) { + setTranslucentStatus(activity); + if (fontIconDark) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + setStatusBarFontIconDark(activity,TYPE_M); + } else if (OSUtils.ROM.MIUI.equals(OSUtils.getRomType())) { + setStatusBarFontIconDark(activity,TYPE_MIUI); + } else if (OSUtils.ROM.Flyme.equals(OSUtils.getRomType())) { + setStatusBarFontIconDark(activity,TYPE_FLYME); + } else {//其他情况下我们将状态栏设置为灰色,就不会看不见字体 + setStatusBarColor(activity, Color.LTGRAY);//灰色 + } + } + } + + /** + * 设置文字颜色 + */ + public static void setStatusBarFontIconDark(Activity activity, @ViewType int type) { + switch (type) { + case TYPE_MIUI: + setMiuiUI(activity,true); + break; + case TYPE_M: + setCommonUI(activity); + break; + case TYPE_FLYME: + setFlymeUI(activity,true); + break; + default: + break; + } + } + + //设置6.0的字体 + public static void setCommonUI(Activity activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + activity.getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + } + } + + //设置Flyme的字体 + public static void setFlymeUI(Activity activity, boolean dark) { + try { + Window window = activity.getWindow(); + WindowManager.LayoutParams lp = window.getAttributes(); + Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON"); + Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags"); + darkFlag.setAccessible(true); + meizuFlags.setAccessible(true); + int bit = darkFlag.getInt(null); + int value = meizuFlags.getInt(lp); + if (dark) { + value |= bit; + } else { + value &= ~bit; + } + meizuFlags.setInt(lp, value); + window.setAttributes(lp); + } catch (Exception e) { + e.printStackTrace(); + } + } + + //设置MIUI字体 + public static void setMiuiUI(Activity activity, boolean dark) { + try { + Window window = activity.getWindow(); + Class clazz = activity.getWindow().getClass(); + Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams"); + Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE"); + int darkModeFlag = field.getInt(layoutParams); + Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class); + if (dark) { //状态栏亮色且黑色字体 + extraFlagField.invoke(window, darkModeFlag, darkModeFlag); + } else { + extraFlagField.invoke(window, 0, darkModeFlag); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/StringUtils.java b/utill/src/main/java/com/lennon/cn/utill/utill/StringUtils.java new file mode 100644 index 0000000..89eadb5 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/StringUtils.java @@ -0,0 +1,904 @@ +package com.lennon.cn.utill.utill; + +import android.app.AlarmManager; +import android.text.TextUtils; +import cn.droidlover.xdroidmvp.log.XLog; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 字符串操作工具包 + * + * @author liux (http://my.oschina.net/liux) + * @author thanatosx + * @version 2.0 + * Updated 2016-08-11 + */ +public class StringUtils { + public static String getHostName(String urlString) { + String head = ""; + int index = urlString.indexOf("://"); + if (index != -1) { + head = urlString.substring(0, index + 3); + urlString = urlString.substring(index + 3); + } + index = urlString.indexOf("/"); + if (index != -1) { + urlString = urlString.substring(0, index + 1); + } + return head + urlString; + } + + public static String getDataSize(long var0) { + DecimalFormat var2 = new DecimalFormat("###.00"); + return var0 < 1024L ? var0 + "bytes" : (var0 < 1048576L ? var2.format((double) ((float) var0 / 1024.0F)) + + "KB" : (var0 < 1073741824L ? var2.format((double) ((float) var0 / 1024.0F / 1024.0F)) + + "MB" : (var0 < 0L ? var2.format((double) ((float) var0 / 1024.0F / 1024.0F / 1024.0F)) + + "GB" : "error"))); + } + private final static Pattern emailer = Pattern + .compile("\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*"); + + private final static Pattern IMG_URL = Pattern + .compile(".*?(gif|jpeg|png|jpg|bmp)"); + + private final static Pattern URL = Pattern + .compile("^(https|http)://.*?$(net|com|.com.cn|org|me|)"); + + private final static ThreadLocal YYYYMMDDHHMMSS = new ThreadLocal() { + @Override + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); + } + }; + + private final static ThreadLocal YYYYMMDD = new ThreadLocal() { + @Override + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + } + }; + + private final static ThreadLocal YYYYMMDDHHMM = new ThreadLocal() { + @Override + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()); + } + }; + + private static final Pattern UniversalDatePattern = Pattern.compile( + "([0-9]{4})-([0-9]{2})-([0-9]{2})[\\s]+([0-9]{2}):([0-9]{2}):([0-9]{2})" + ); + + /** + * 将字符串转位日期类型 + * + * @param sdate string date that's type like YYYY-MM-DD HH:mm:ss + * @return {@link Date} + */ + public static Date toDate(String sdate) { + return toDate(sdate, YYYYMMDDHHMMSS.get()); + } + + public static Date toDate(String sdate, SimpleDateFormat formatter) { + try { + return formatter.parse(sdate); + } catch (Exception e) { + return null; + } + } + + /** + * YYYY-MM-DD HH:mm:ss格式的时间字符串转换为{@link Calendar}类型 + * + * @param str YYYY-MM-DD HH:mm:ss格式字符串 + * @return {@link Calendar} + */ + public static Calendar parseCalendar(String str) { + Matcher matcher = UniversalDatePattern.matcher(str); + Calendar calendar = Calendar.getInstance(); + if (!matcher.find()) { + return null; + } + calendar.set( + matcher.group(1) == null ? 0 : toInt(matcher.group(1)), + matcher.group(2) == null ? 0 : toInt(matcher.group(2)) - 1, + matcher.group(3) == null ? 0 : toInt(matcher.group(3)), + matcher.group(4) == null ? 0 : toInt(matcher.group(4)), + matcher.group(5) == null ? 0 : toInt(matcher.group(5)), + matcher.group(6) == null ? 0 : toInt(matcher.group(6)) + ); + return calendar; + } + + /** + * transform date to string that's type like YYYY-MM-DD HH:mm:ss + * + * @param date {@link Date} + * @return + */ + public static String getDateString(Date date) { + return YYYYMMDDHHMMSS.get().format(date); + } + + /** + * transform date to string that's type like YYYY-MM-DD HH:mm + * + * @param sdate + * @return + */ + public static String getDateString(String sdate) { + if (TextUtils.isEmpty(sdate)) { + return ""; + } + return YYYYMMDDHHMM.get().format(toDate(sdate)); + } + + + public static String formatYearMonthDay(String st) { + if (TextUtils.isEmpty(st)) { + return ""; + } + Matcher matcher = UniversalDatePattern.matcher(st); + if (!matcher.find()) { + return st; + } + return String.format("%s年%s月%s日", + matcher.group(1) == null ? 0 : matcher.group(1), + matcher.group(2) == null ? 0 : matcher.group(2), + matcher.group(3) == null ? 0 : matcher.group(3)); + } + + public static String formatYearMonthDayNew(String st) { + if (TextUtils.isEmpty(st)) { + return ""; + } + Matcher matcher = UniversalDatePattern.matcher(st); + if (!matcher.find()) { + return st; + } + return String.format("%s/%s/%s", + matcher.group(1) == null ? 0 : matcher.group(1), + matcher.group(2) == null ? 0 : matcher.group(2), + matcher.group(3) == null ? 0 : matcher.group(3)); + } + + /** + * format time friendly + * + * @param sdate YYYY-MM-DD HH:mm:ss + * @return n分钟前, n小时前, 昨天, 前天, n天前, n个月前 + */ + public static String formatSomeAgo(String sdate) { + if (sdate == null) { + return ""; + } + Calendar calendar = parseCalendar(sdate); + if (calendar == null) { + return sdate; + } + + Calendar mCurrentDate = Calendar.getInstance(); + long crim = mCurrentDate.getTimeInMillis(); // current + long trim = calendar.getTimeInMillis(); // target + long diff = crim - trim; + + int year = mCurrentDate.get(Calendar.YEAR); + int month = mCurrentDate.get(Calendar.MONTH); + int day = mCurrentDate.get(Calendar.DATE); + + if (diff < 60 * 1000) { + return "刚刚"; + } + if (diff >= 60 * 1000 && diff < AlarmManager.INTERVAL_HOUR) { + return String.format("%s分钟前", diff / 60 / 1000); + } + mCurrentDate.set(year, month, day, 0, 0, 0); + if (trim >= mCurrentDate.getTimeInMillis()) { + return String.format("%s小时前", diff / AlarmManager.INTERVAL_HOUR); + } + mCurrentDate.set(year, month, day - 1, 0, 0, 0); + if (trim >= mCurrentDate.getTimeInMillis()) { + return "昨天"; + } + mCurrentDate.set(year, month, day - 2, 0, 0, 0); + if (trim >= mCurrentDate.getTimeInMillis()) { + return "前天"; + } + if (diff < AlarmManager.INTERVAL_DAY * 30) { + return String.format("%s天前", diff / AlarmManager.INTERVAL_DAY); + } + if (diff < AlarmManager.INTERVAL_DAY * 30 * 12) { + return String.format("%s月前", diff / (AlarmManager.INTERVAL_DAY * 30)); + } + return String.format("%s年前", mCurrentDate.get(Calendar.YEAR) - calendar.get(Calendar.YEAR)); + } + + /** + * @param str YYYY-MM-DD HH:mm:ss string + * @return 今天, 昨天, 前天, n天前 + */ + public static String formatSomeDay(String str) { + return formatSomeDay(parseCalendar(str)); + } + + /** + * @param calendar {@link Calendar} + * @return 今天, 昨天, 前天, n天前 + */ + public static String formatSomeDay(Calendar calendar) { + if (calendar == null) { + return "?天前"; + } + Calendar mCurrentDate = Calendar.getInstance(); + long crim = mCurrentDate.getTimeInMillis(); // current + long trim = calendar.getTimeInMillis(); // target + long diff = crim - trim; + + int year = mCurrentDate.get(Calendar.YEAR); + int month = mCurrentDate.get(Calendar.MONTH); + int day = mCurrentDate.get(Calendar.DATE); + + mCurrentDate.set(year, month, day, 0, 0, 0); + if (trim >= mCurrentDate.getTimeInMillis()) { + return "今天"; + } + mCurrentDate.set(year, month, day - 1, 0, 0, 0); + if (trim >= mCurrentDate.getTimeInMillis()) { + return "昨天"; + } + mCurrentDate.set(year, month, day - 2, 0, 0, 0); + if (trim >= mCurrentDate.getTimeInMillis()) { + return "前天"; + } + return String.format("%s天前", diff / AlarmManager.INTERVAL_DAY); + } + + /** + * @param calendar {@link Calendar} + * @return 星期n + */ + public static String formatWeek(Calendar calendar) { + if (calendar == null) { + return "星期?"; + } + return new String[]{"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"}[calendar.get(Calendar.DAY_OF_WEEK) - 1]; + } + + /** + * @param str YYYY-MM-DD HH:mm:ss string + * @return 星期n + */ + public static String formatWeek(String str) { + return formatWeek(parseCalendar(str)); + } + + /** + * @param sdate YYYY-MM-DD HH:mm:ss string + * @return + */ + public static String formatDayWeek(String sdate) { + Calendar calendar = parseCalendar(sdate); + if (calendar == null) { + return "??/?? 星期?"; + } + Calendar mCurrentDate = Calendar.getInstance(); + String ws = formatWeek(calendar); + int diff = mCurrentDate.get(Calendar.DATE) - calendar.get(Calendar.DATE); + if (diff == 0) { + return "今天 / " + ws; + } + if (diff == 1) { + return "昨天 / " + ws; + } + int m = calendar.get(Calendar.MONTH); + int d = calendar.get(Calendar.DATE); + return String.format("%s/%s / %s", formatInt(m), formatInt(d), ws); + } + + /** + * format to HH + * + * @param i integer + * @return HH + */ + public static String formatInt(int i) { + return (i < 10 ? "0" : "") + i; + } + + + /** + * 智能格式化 + */ + public static String friendly_time3(String sdate) { + Calendar calendar = parseCalendar(sdate); + if (calendar == null) { + return sdate; + } + + Calendar mCurrentDate = Calendar.getInstance(); + SimpleDateFormat formatter = YYYYMMDDHHMMSS.get(); + + int hour = calendar.get(Calendar.HOUR_OF_DAY); + String s = hour >= 0 && hour < 12 ? "上午" : "下午"; + s += " HH:mm"; + + if (mCurrentDate.get(Calendar.DATE) == calendar.get(Calendar.DATE)) { + formatter.applyPattern(s); + } else if (mCurrentDate.get(Calendar.DATE) - calendar.get(Calendar.DATE) == 1) { + formatter.applyPattern("昨天 " + s); + } else if (mCurrentDate.get(Calendar.YEAR) == calendar.get(Calendar.YEAR)) { + formatter.applyPattern("MM-dd " + s); + } else { + formatter.applyPattern("YYYY-MM-dd " + s); + } + return formatter.format(calendar.getTime()); + } + + + /** + * 判断给定字符串时间是否为今日 + * + * @param sdate + * @return boolean + */ + public static boolean isToday(String sdate) { + Date time = toDate(sdate); + Date today = new Date(); + if (time != null) { + String nowDate = YYYYMMDD.get().format(today); + String timeDate = YYYYMMDD.get().format(time); + if (nowDate.equals(timeDate)) { + return true; + } + } + return false; + } + + /** + * 是否是相同的一天 + * + * @param sdate1 sdate1 + * @param sdate2 sdate2 + * @return + */ + public static boolean isSameDay(String sdate1, String sdate2) { + if (TextUtils.isEmpty(sdate1) || TextUtils.isEmpty(sdate2)) { + return false; + } + Date date1 = toDate(sdate1); + Date date2 = toDate(sdate2); + if (date1 != null && date2 != null) { + String d1 = YYYYMMDD.get().format(date1); + String d2 = YYYYMMDD.get().format(date2); + if (d1.equals(d2)) { + return true; + } + } + return false; + } + + public static String getCurrentTimeStr() { + return YYYYMMDDHHMMSS.get().format(new Date()); + } + + /*** + * 计算两个时间差,返回的是的秒s + * + * @param date1 + * @param date2 + * @return + * @author 火蚁 2015-2-9 下午4:50:06 + */ + public static long calDateDifferent(String date1, String date2) { + try { + Date d1 = YYYYMMDDHHMMSS.get().parse(date1); + Date d2 = YYYYMMDDHHMMSS.get().parse(date2); + // 毫秒ms + long diff = d2.getTime() - d1.getTime(); + return diff / 1000; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 判断给定字符串是否空白串。 空白串是指由空格、制表符、回车符、换行符组成的字符串 若输入字符串为null或空字符串,返回true + * + * @param input + * @return boolean + */ + @Deprecated + public static boolean isEmpty(String input) { + if (input == null || "".equals(input)) { + return true; + } + + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c != ' ' && c != '\t' && c != '\r' && c != '\n') { + return false; + } + } + return true; + } + + /** + * 判断一个url是否为图片url + * + * @param url + * @return + */ + public static boolean isImgUrl(String url) { + if (url == null || url.trim().length() == 0) { + return false; + } + return IMG_URL.matcher(url).matches(); + } + + /** + * 判断是否为一个合法的url地址 + * + * @param str + * @return + */ + public static boolean isUrl(String str) { + if (str == null || str.trim().length() == 0) { + return false; + } + return URL.matcher(str).matches(); + } + + /** + * 字符串转整数 + * + * @param str + * @param defValue + * @return + */ + public static int toInt(String str, int defValue) { + try { + return Integer.parseInt(str); + } catch (Exception e) { + XLog.d("oschina "+ e.getMessage()); + } + return defValue; + } + + /** + * 对象转整数 + * + * @param obj + * @return 转换异常返回 0 + */ + public static int toInt(Object obj) { + if (obj == null) { + return 0; + } + return toInt(obj.toString(), 0); + } + + /** + * 对象转整数 + * + * @param obj + * @return 转换异常返回 0 + */ + public static long toLong(String obj) { + try { + return Long.parseLong(obj); + } catch (Exception e) { + } + return 0; + } + + /** + * 字符串转布尔值 + * + * @param b + * @return 转换异常返回 false + */ + public static boolean toBool(String b) { + try { + return Boolean.parseBoolean(b); + } catch (Exception e) { + } + return false; + } + + public static String getString(String s) { + return s == null ? "" : s; + } + + /** + * 将一个InputStream流转换成字符串 + * + * @param is + * @return + */ + public static String toConvertString(InputStream is) { + StringBuilder res = new StringBuilder(); + BufferedReader read = new BufferedReader(new InputStreamReader(is)); + try { + String line; + while ((line = read.readLine()) != null) { + res.append(line).append("
"); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + read.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return res.toString(); + } + + /*** + * 截取字符串 + * + * @param start 从那里开始,0算起 + * @param num 截取多少个 + * @param str 截取的字符串 + * @return + */ + public static String getSubString(int start, int num, String str) { + if (str == null) { + return ""; + } + int length = str.length(); + if (start < 0) { + start = 0; + } + if (start > length) { + start = length; + } + if (num < 0) { + num = 1; + } + int end = start + num; + if (end > length) { + end = length; + } + return str.substring(start, end); + } + + /** + * 获取当前时间为每年第几周 + * + * @return + */ + public static int getWeekOfYear() { + return getWeekOfYear(new Date()); + } + + /** + * 获取当前时间为每年第几周 + * + * @param date + * @return + */ + public static int getWeekOfYear(Date date) { + Calendar c = Calendar.getInstance(); + c.setFirstDayOfWeek(Calendar.MONDAY); + c.setTime(date); + int week = c.get(Calendar.WEEK_OF_YEAR) - 1; + week = week == 0 ? 52 : week; + return week > 0 ? week : 1; + } + + public static int[] getCurrentDate() { + int[] dateBundle = new int[3]; + String[] temp = getDataTime("yyyy-MM-dd").split("-"); + + for (int i = 0; i < 3; i++) { + try { + dateBundle[i] = Integer.parseInt(temp[i]); + } catch (Exception e) { + dateBundle[i] = 0; + } + } + return dateBundle; + } + + /** + * 返回当前系统时间 + */ + public static String getDataTime(String format) { + return new SimpleDateFormat(format, Locale.getDefault()).format(new Date()); + } + + public static boolean isNotEmpty(String value) { + return !isEmpty(value); + } + + /** + * 检查对象是否为数字型字符串,包含负数开头的。 + * + * @param target + * @return + */ + public static boolean isNumeric(String target) { + if (target == null) { + return false; + } + char[] chars = target.toString().toCharArray(); + int length = chars.length; + if (length < 1) { + return false; + } + + int i = 0; + if (length > 1 && chars[0] == '-') { + i = 1; + } + + for (; i < length; i++) { + if (!Character.isDigit(chars[i])) { + return false; + } + } + return true; + } + + /** + * 判断传入字符串是否为数字类型 + * + * @param target + * @param isRadix + * 是否小数 + * @return + */ + public static boolean isNumeric(String target, boolean isRadix) { + if (null == target || "".equals(target.trim())) { + return false; + } + if (isRadix) { + if (target.matches("[0-9]+.[0-9]+")) { + return true; + } + } + else { + if (target.matches("[0-9]+")) { + return true; + } + } + + return false; + } + + /** + * 把通用字符编码的字符串转化为汉字编码。 + */ + public static String unicodeToChinese(String unicode) { + StringBuilder out = new StringBuilder(); + if (!isEmpty(unicode)) { + for (int i = 0; i < unicode.length(); i++) { + out.append(unicode.charAt(i)); + } + } + return out.toString(); + } + + public static String toUnderlineStyle(String name) { + StringBuilder newName = new StringBuilder(); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if (Character.isUpperCase(c)) { + if (i > 0) { + newName.append("_"); + } + newName.append(Character.toLowerCase(c)); + } + else { + newName.append(c); + } + } + return newName.toString(); + } + + public static String convertString(byte[] data, int offset, int length) { + if (data == null) { + return null; + } + else { + try { + return new String(data, offset, length, CHARSET_UTF8); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + public static byte[] convertBytes(String data) { + if (data == null) { + return null; + } + else { + try { + return data.getBytes(CHARSET_UTF8); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + public static String numberFormater(int number, int length) { + NumberFormat formatter = NumberFormat.getNumberInstance(); + formatter.setMinimumIntegerDigits(length); + formatter.setGroupingUsed(false); + return formatter.format(number); + } + + static final Pattern reUnicode = Pattern.compile("\\\\u([0-9a-zA-Z]{4})"); + + public static String decode1(String s) { + Matcher m = reUnicode.matcher(s); + StringBuffer sb = new StringBuffer(s.length()); + while (m.find()) { + m.appendReplacement(sb, Character.toString((char) Integer.parseInt(m.group(1), 16))); + } + m.appendTail(sb); + return sb.toString(); + } + + public static String paddingLeft(String src, int len, String value) { + + while (src.length() < len) { + src = value + src; + } + return src; + } + + public static String paddingRight(String src, int len, String value) { + StringBuilder srcBuilder = new StringBuilder(src); + while (srcBuilder.length() < len) { + srcBuilder.append(value); + } + src = srcBuilder.toString(); + return src; + } + + /** + * + * @param value + * @param regex1 + * @param regex2 + * @return + */ + public static Map split(String value, String regex1, String regex2) { + + Map map = new HashMap(); + if (isEmpty(value)) { + return map; + } + + String[] tmp = value.split(regex1); + + for (String v : tmp) { + String[] tmp1 = v.split(regex2); + if (tmp1.length == 2) { + map.put(tmp1[0], tmp1[1]); + } + else if (tmp1.length > 2) { + map.put(tmp1[0], v.substring(v.indexOf(regex2))); + } + else { + System.out.println("数据格式错误:" + v); + } + } + + return map; + } + + public static boolean isEmail(String value) { + if (value == null || (value.length() == 0)) { + return false; + } + + return value.indexOf("@") > -1; + } + + public static boolean isCell(String value) { + + if (value == null || (value.length() == 0)) { + return false; + } + // 1开头、数字、11位 + return value.startsWith("1") && isNumeric(value) && (value.length() == 11); + } + + public static boolean isXml(String value) { + + return value.startsWith(" String2Map(String target, final String split) { + if (target == null || target == "") { + return null; + } + + Map map = new HashMap(); + String[] res = target.split(split); + String[] kv; + for (int i = 0; i < res.length; i++) { + if (res[i] == null || res[i] == "") { + continue; + } + kv = res[i].split("="); + if (kv.length == 2) { + if (!map.containsKey(kv[0])) { + map.put(kv[0], kv[1]); + } + } + else if (kv.length > 2) { + if (!map.containsKey(kv[0])) { + map.put(kv[0], res[i].substring(res[i].indexOf("="))); + } + } + else { + + } + } + return map; + } + + /** + * 将16位byte[] 转换为32位String + * + * @param buffer + * @return String + */ + public static String toHex(byte buffer[]) { + StringBuffer sb = new StringBuffer(buffer.length * 2); + for (int i = 0; i < buffer.length; i++) { + sb.append(Character.forDigit((buffer[i] & 240) >> 4, 16)); + sb.append(Character.forDigit(buffer[i] & 15, 16)); + } + + return sb.toString(); + } + + public static byte[] fromHex(String hex) { + byte[] res = new byte[hex.length() / 2]; + char[] chs = hex.toCharArray(); + for (int i = 0, c = 0; i < chs.length; i += 2, c++) { + res[c] = (byte) (Integer.parseInt(new String(chs, i, 2), 16)); + } + + return res; + } + + private static Pattern ChinesePattern = Pattern.compile("[\u4e00-\u9fa5]"); + + /** + * 判断字符串是否包含中文字符 + * + * @param target + * @return + */ + public static boolean hasChinese(String target) { + return ChinesePattern.matcher(target).find(); + } + + public static String substring(String src, int start, int length) { + + return src.substring(start, start + length); + } + + static String CHARSET_UTF8 = "UTF-8"; +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/SystemBarTintManager.java b/utill/src/main/java/com/lennon/cn/utill/utill/SystemBarTintManager.java new file mode 100644 index 0000000..52e6f8c --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/SystemBarTintManager.java @@ -0,0 +1,97 @@ +package com.lennon.cn.utill.utill; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.res.TypedArray; +import android.os.Build; +import android.view.*; +import android.widget.FrameLayout; + +public class SystemBarTintManager { + public static final int DEFAULT_TINT_COLOR = 0x99000000; + private boolean mStatusBarAvailable; + private boolean mStatusBarTintEnabled; + private View mStatusBarTintView; + + @TargetApi(19) + public SystemBarTintManager(Activity activity) { + + Window win = activity.getWindow(); + //获取DecorView + ViewGroup decorViewGroup = (ViewGroup) win.getDecorView(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + // 检查主题中是否有透明的状态栏 + int[] attrs = {android.R.attr.windowTranslucentStatus}; + TypedArray a = activity.obtainStyledAttributes(attrs); + try { + mStatusBarAvailable = a.getBoolean(0, false); + } finally { + a.recycle(); + } + WindowManager.LayoutParams winParams = win.getAttributes(); + int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;//状态栏透明 + if ((winParams.flags & bits) != 0) { + mStatusBarAvailable = true; + } + } + if (mStatusBarAvailable) { + setupStatusBarView(activity, decorViewGroup); + } + } + + /** + * 初始化状态栏 + * + * @param context + * @param decorViewGroup + */ + private void setupStatusBarView(Activity context, ViewGroup decorViewGroup) { + mStatusBarTintView = new View(context); + //设置高度为Statusbar的高度 + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, getStatusBarHeight(context)); + params.gravity = Gravity.TOP; + mStatusBarTintView.setLayoutParams(params); + mStatusBarTintView.setBackgroundColor(DEFAULT_TINT_COLOR); + //默认不显示 + mStatusBarTintView.setVisibility(View.GONE); + //decorView添加状态栏高度的View + decorViewGroup.addView(mStatusBarTintView); + } + + /** + * 获取状态栏高度 + * + * @param activity + * @return + */ + private int getStatusBarHeight(Activity activity) { + int statusBarHeight = 0; + int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + statusBarHeight = activity.getResources().getDimensionPixelSize(resourceId); + } + return statusBarHeight; + } + + /** + * 显示状态栏 + */ + public void setStatusBarTintEnabled(boolean enabled) { + mStatusBarTintEnabled = enabled; + if (mStatusBarAvailable) { + mStatusBarTintView.setVisibility(enabled ? View.VISIBLE : View.GONE); + } + } + + /** + * 设置状态栏颜色 + * + * @param color + */ + public void setStatusBarTintColor(int color) { + if (mStatusBarAvailable) { + mStatusBarTintView.setBackgroundColor(color); + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/TeminalDevice.java b/utill/src/main/java/com/lennon/cn/utill/utill/TeminalDevice.java new file mode 100644 index 0000000..0579e47 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/TeminalDevice.java @@ -0,0 +1,346 @@ +package com.lennon.cn.utill.utill; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.*; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.Uri; +import android.os.Build; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.DisplayMetrics; +import android.view.View; +import android.view.Window; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import com.lennon.cn.utill.base.BaseApplication; + +import java.io.File; +import java.util.List; + +/** + * @author dingyi + */ +@SuppressLint("MissingPermission") +public class TeminalDevice { + /** + * 将dip或dp值转换为px值,保证尺寸大小不变 + * + * @param dipValue + * @return + */ + public static int dip2px(Context context, float dipValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (dipValue * scale + 0.5f); + } + + public static float dp2px(float dp) { + return dp * getDisplayMetrics().density; + } + + public static float px2dp(float px) { + return px / getDisplayMetrics().density; + } + + public static DisplayMetrics getDisplayMetrics() { + return BaseApplication.Companion.context().getResources().getDisplayMetrics(); + } + + public static float getScreenHeight() { + return getDisplayMetrics().heightPixels; + } + + public static float getScreenWidth() { + return getDisplayMetrics().widthPixels; + } + + public static int getStatusBarHeight(Activity context) { + Rect rectangle = new Rect(); + Window window = context.getWindow(); + window.getDecorView().getWindowVisibleDisplayFrame(rectangle); + return rectangle.top; + } + + public static boolean hasInternet() { + ConnectivityManager cm = (ConnectivityManager) BaseApplication.Companion.context() + .getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo info = cm.getActiveNetworkInfo(); + return info != null && info.isAvailable() && info.isConnected(); + } + + public static boolean isPortrait() { + return BaseApplication.Companion.context().getResources().getConfiguration() + .orientation == Configuration.ORIENTATION_PORTRAIT; + } + + /** + * 打开或关闭键盘 + */ + public static void startOrCloseKeyboard(View view) { + InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + // 得到InputMethodManager的实例 + if (imm.isActive()) { + // 如果开启 + imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, + InputMethodManager.HIDE_NOT_ALWAYS); + } + } + + public static void closeKeyboard(EditText view) { + view.clearFocus(); + InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN); + } + + public static void openKeyboard(View view) { + InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); + } + + /** + * 平板电脑? + * + * @return ?? + */ + public static boolean isTablet() { + int s = BaseApplication.Companion.context().getResources().getConfiguration().screenLayout; + s &= Configuration.SCREENLAYOUT_SIZE_MASK; + return s >= Configuration.SCREENLAYOUT_SIZE_LARGE; + } + + public static void hideSoftKeyboard(View view) { + if (view == null) { + return; + } + View mFocusView = view; + + Context context = view.getContext(); + if (context != null && context instanceof Activity) { + Activity activity = ((Activity) context); + mFocusView = activity.getCurrentFocus(); + } + if (mFocusView == null) { + return; + } + mFocusView.clearFocus(); + InputMethodManager manager = (InputMethodManager) mFocusView.getContext() + .getSystemService(Context.INPUT_METHOD_SERVICE); + manager.hideSoftInputFromWindow(mFocusView.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } + + public static void showSoftKeyboard(View view) { + if (view == null) { + return; + } + view.setFocusable(true); + view.setFocusableInTouchMode(true); + if (!view.isFocused()) { + view.requestFocus(); + } + + InputMethodManager inputMethodManager = (InputMethodManager) view.getContext() + .getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.showSoftInput(view, 0); + } + + public static void gotoMarket(Context context, String pck) { + if (!isHaveMarket(context)) { + //AppContext.showToast("你手机中没有安装应用市场!"); + return; + } + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse("market://details?id=" + pck)); + if (intent.resolveActivity(context.getPackageManager()) != null) { + context.startActivity(intent); + } + } + + public static boolean isHaveMarket(Context context) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_APP_MARKET); + PackageManager pm = context.getPackageManager(); + List infos = pm.queryIntentActivities(intent, 0); + return infos.size() > 0; + } + + public static void openAppInMarket(Context context) { + if (context == null) { + return; + } + String pckName = context.getPackageName(); + gotoMarket(context, pckName); + } + + public static int getVersionCode() { + return getVersionCode(BaseApplication.Companion.context().getPackageName()); + } + + public static int getVersionCode(String packageName) { + try { + return BaseApplication.Companion.context() + .getPackageManager() + .getPackageInfo(packageName, 0) + .versionCode; + } catch (PackageManager.NameNotFoundException ex) { + return 0; + } + } + + public static String getVersionName() { + try { + return BaseApplication + .Companion + .context() + .getPackageManager() + .getPackageInfo(BaseApplication.Companion.context().getPackageName(), 0) + .versionName; + } catch (PackageManager.NameNotFoundException ex) { + return "undefined version name"; + } + } + + //版本名 + public static String getVersionName(Context context) { + return getPackageInfo(context).versionName; + } + + //版本号 + public static int getVersionCode(Context context) { + return getPackageInfo(context).versionCode; + } + + private static PackageInfo getPackageInfo(Context context) { + PackageInfo pi = null; + + try { + PackageManager pm = context.getPackageManager(); + pi = pm.getPackageInfo(context.getPackageName(), + PackageManager.GET_CONFIGURATIONS); + + return pi; + } catch (Exception e) { + e.printStackTrace(); + } + + return pi; + } + + public static void installAPK(Context context, File file) { + if (file == null || !file.exists()) { + return; + } + Intent intent = new Intent(); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setAction(Intent.ACTION_VIEW); + intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); + context.startActivity(intent); + } + + public static boolean openAppActivity(Context context, + String packageName, + String activityName) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + ComponentName cn = new ComponentName(packageName, activityName); + intent.setComponent(cn); + try { + context.startActivity(intent); + return true; + } catch (ActivityNotFoundException e) { + return false; + } + } + + public static boolean isWifiOpen() { + ConnectivityManager cm = (ConnectivityManager) BaseApplication + .Companion.context().getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo info = cm.getActiveNetworkInfo(); + if (info == null) { + return false; + } + if (!info.isAvailable() || !info.isConnected()) { + return false; + } + if (info.getType() != ConnectivityManager.TYPE_WIFI) { + return false; + } + return true; + } + + @SuppressWarnings("deprecation") + public static void copyTextToBoard(String string) { + if (TextUtils.isEmpty(string)) { + return; + } + ClipboardManager clip = (ClipboardManager) BaseApplication.Companion.context() + .getSystemService(Context.CLIPBOARD_SERVICE); + clip.setText(string); + //AppContext.showToast(R.string.copy_success); + } + + /** + * 调用系统安装了的应用分享 + * + * @param context + * @param title + * @param url + */ + public static void showSystemShareOption(Activity context, + final String title, final String url) { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_SUBJECT, "分享:" + title); + intent.putExtra(Intent.EXTRA_TEXT, title + " " + url); + context.startActivity(Intent.createChooser(intent, "选择分享")); + } + + /** + * 获取设备ID + * + * @return + */ + @SuppressLint("MissingPermission") + public static String getDeviceId() { + TelephonyManager tm = (TelephonyManager) BaseApplication.Companion.context().getSystemService(Context.TELEPHONY_SERVICE); + return tm.getDeviceId(); + } + + public static String getHandSetInfo() { + String handSetInfo = "手机型号:" + Build.MODEL + + "\n系统版本:" + Build.VERSION.RELEASE + + "\n产品型号:" + Build.PRODUCT + + "\n版本显示:" + Build.DISPLAY + + "\n系统定制商:" + Build.BRAND + + "\n设备参数:" + Build.DEVICE + + "\n开发代号:" + Build.VERSION.CODENAME + + "\nSDK版本号:" + Build.VERSION.SDK_INT + + "\nCPU类型:" + Build.CPU_ABI + + "\n硬件类型:" + Build.HARDWARE + + "\n主机:" + Build.HOST + + "\n生产ID:" + Build.ID + + "\nROM制造商:" + Build.MANUFACTURER // 这行返回的是rom定制商的名称 + ; + return handSetInfo; + } + + public static String getPhoneInfo() { + String os = "android," + Build.VERSION.RELEASE; + String model = Build.MODEL.toLowerCase(); + String brand = Build.BRAND; + if (null == brand) { + brand = ""; + } + if (null == model) { + model = "undefined"; + } + String phoneinfo = os + "," + brand + "," + model.toLowerCase(); + return phoneinfo; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/TimeCountUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/TimeCountUtil.java new file mode 100644 index 0000000..45621da --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/TimeCountUtil.java @@ -0,0 +1,45 @@ +package com.lennon.cn.utill.utill; + + +import android.app.Activity; +import android.os.CountDownTimer; +import android.widget.Button; +import lennon.com.utill.R; + +/** + * Created by Administrator on 2015/8/25. + */ +public class TimeCountUtil extends CountDownTimer { + + private Activity mActivity; + private Button mBtn; + private boolean mIsEnable = false; + + public TimeCountUtil(Activity activity, long millisInFuture, long countDownInterval, Button button) { + super(millisInFuture, countDownInterval); + this.mActivity = activity; + this.mBtn = button; + this.mIsEnable = true; + } + + @Override + public void onFinish() { + mBtn.setText(mActivity.getResources().getString(R.string.reget_sms)); + mBtn.setEnabled(true); + mBtn.setClickable(true); + mBtn.setTextColor(mActivity.getResources().getColor(R.color.color_ffffff)); + mBtn.setBackground(mBtn.getResources().getDrawable(R.drawable.common_button)); + mIsEnable = true; + } + + @Override + public void onTick(long millisUntilFinished) { + if (mIsEnable) { + mBtn.setEnabled(false); + } + mIsEnable = false; + mBtn.setText(millisUntilFinished / 1000 + mActivity.getResources().getString(R.string.resend_sms)); + mBtn.setTextColor(mActivity.getResources().getColor(R.color.color_333333)); + mBtn.setBackground(mBtn.getResources().getDrawable(R.drawable.common_button_fffff)); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/TimeUtill.kt b/utill/src/main/java/com/lennon/cn/utill/utill/TimeUtill.kt new file mode 100644 index 0000000..d6393cc --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/TimeUtill.kt @@ -0,0 +1,34 @@ +package com.lennon.cn.utill.utill + +object TimeUtill { + /** + * @param mss 要转换的毫秒数 + * @return 该毫秒数转换为 * days * hours * minutes * seconds 后的格式 + * @author fy.zhang + */ + fun formatDuring(mss: Long): String { + val days = mss / (1000 * 60 * 60 * 24) + val hours = mss % (1000 * 60 * 60 * 24) / (1000 * 60 * 60) + val minutes = mss % (1000 * 60 * 60) / (1000 * 60) + val seconds = mss % (1000 * 60) / 1000 + val millis = mss and 1000 + var s = "" + if (days > 0) { + s += days.toString() + "天" + } + if (hours > 0) { + s += hours.toString() + "小时" + } + if (minutes > 0) { + s += minutes.toString() + "分钟" + } + if (seconds > 0) { + s += seconds.toString() + "秒" + } + if (millis > 0) { + s += millis.toString() + "毫秒" + } + return s + } + +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/Utill.kt b/utill/src/main/java/com/lennon/cn/utill/utill/Utill.kt new file mode 100644 index 0000000..a24eaaf --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/Utill.kt @@ -0,0 +1,317 @@ +package com.lennon.cn.utill.utill + +import android.app.ActivityManager +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.content.res.Resources +import android.graphics.drawable.Drawable +import android.net.Uri +import android.os.Build +import android.text.TextUtils +import android.util.DisplayMetrics +import android.view.WindowManager +import com.lennon.cn.utill.dialog.CommonAlertDialog +import com.lennon.cn.utill.dialog.OnAlertDialogListener +import java.io.File +import java.io.UnsupportedEncodingException +import java.util.ArrayList +import com.lennon.cn.utill.conf.Lennon +import android.provider.MediaStore +import android.content.ContentValues +import android.R.layout +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color +import android.opengl.ETC1.getHeight +import android.opengl.ETC1.getWidth +import android.view.View + + +object Utill { + /** + * 产生图片的路径,带文件夹和文件名,文件名为当前毫秒数 + */ + fun generateImgePath(): String { + val f = File(Lennon.getFilePathName() + File.separator + "images") + if (!f.exists()) { + f.mkdirs() + } + return f.absolutePath + File.separator + System.currentTimeMillis().toString() + ".jpg" + } + /** + * view转bitmap + */ + fun viewConversionBitmap(v: View): Bitmap { + val w = v.getWidth() + val h = v.getHeight() + + val bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888) + val c = Canvas(bmp) + +// c.drawColor(Color.WHITE) + /** 如果不设置canvas画布为白色,则生成透明 */ + + v.layout(0, 0, w, h) + v.draw(c) + + return bmp + } + fun getImageContentUri(context: Context, imageFile: File): Uri? { + val filePath = imageFile.absolutePath + val cursor = context.contentResolver.query( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + arrayOf(MediaStore.Images.Media._ID), MediaStore.Images.Media.DATA + "=? ", + arrayOf(filePath), null + ) + if (cursor != null && cursor.moveToFirst()) { + val id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID)) + val baseUri = Uri.parse("content://media/external/images/media") + return Uri.withAppendedPath(baseUri, "" + id) + } else { + if (imageFile.exists()) { + val values = ContentValues() + values.put(MediaStore.Images.Media.DATA, filePath) + return context.contentResolver.insert( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + values + ) + } else { + return null + } + } + } + + /** + * 获取当前应用程序的包名 + * @param context 上下文对象 + * @return 返回包名 + */ + fun getAppProcessName(context: Context): String { + //当前应用pid + val pid = android.os.Process.myPid() + //任务管理类 + val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + //遍历所有应用 + val infos = manager.getRunningAppProcesses() + for (info in infos) { + if (info.pid == pid)//得到当前应用 + return info.processName//返回包名 + } + return "" + } + + + /** + * 获取程序 图标 + * @param context + * @param packname 应用包名 + * @return + */ + fun getAppIcon(context: Context, packname: String): Drawable? { + try { + //包管理操作管理类 + val pm = context.getPackageManager() + //获取到应用信息 + val info = pm.getApplicationInfo(packname, 0) + return info.loadIcon(pm) + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } + return null + } + + /** + * 获取程序的版本号 + * @param context + * @param packname + * @return + */ + fun getAppVersion(context: Context, packname: String): String { + //包管理操作管理类 + val pm = context.getPackageManager() + try { + val packinfo = pm.getPackageInfo(packname, 0) + return packinfo.versionName + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + + } + return packname + } + + + /** + * 获取程序的名字 + * @param context + * @param packname + * @return + */ + fun getAppName(context: Context, packname: String): String { + //包管理操作管理类 + val pm = context.getPackageManager() + try { + val info = pm.getApplicationInfo(packname, 0) + return info.loadLabel(pm).toString() + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + + } + return packname + } + + /* + * 获取程序的权限 + */ + fun getAllPermissions(context: Context, packname: String): Array? { + try { + //包管理操作管理类 + val pm = context.getPackageManager() + val packinfo = pm.getPackageInfo(packname, PackageManager.GET_PERMISSIONS) + //获取到所有的权限 + return packinfo.requestedPermissions + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + + } + return null + } + + + /** + * 获取程序的签名 + * @param context + * @param packname + * @return + */ + fun getAppSignature(context: Context, packname: String): String { + try { + //包管理操作管理类 + val pm = context.getPackageManager() + val packinfo = pm.getPackageInfo(packname, PackageManager.GET_SIGNATURES) + //获取当前应用签名 + return packinfo.signatures[0].toCharsString() + + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + + } + return packname + } + + /** + * 获取当前展示 的Activity名称 + * @return + */ +// fun getCurrentActivityName(context: Context): String? { +// val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager +// val runningActivity = activityManager.getRunningTasks(1).get(0).topActivity?.getClassName() +// return runningActivity +// } + + fun makeDir(dirPath: String) { + val file = File(dirPath) + if (!file.exists()) { + file.mkdirs() + } + } + + /** + * 判断是否为平板 + * + * @return + */ + fun isPad(context: Context): Boolean { + val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + val display = wm.defaultDisplay + // 屏幕宽度 + val screenWidth = display.width.toFloat() + // 屏幕高度 + val screenHeight = display.height.toFloat() + val dm = DisplayMetrics() + display.getMetrics(dm) + val x = Math.pow((dm.widthPixels / dm.xdpi).toDouble(), 2.0) + val y = Math.pow((dm.heightPixels / dm.ydpi).toDouble(), 2.0) + // 屏幕尺寸 + val screenInches = Math.sqrt(x + y) + // 大于6尺寸则为Pad + return screenInches >= 8.0 + } + + fun toUtf8(str: String): String? { + var result: String? = null + try { + result = String(str.toByteArray(charset("UTF-8")), charset("UTF-8")) + } catch (e: UnsupportedEncodingException) { + e.printStackTrace() + } + + return result + } + + fun getFileName(url: String): String { + val s = url.split("/".toRegex()) + .dropLastWhile { it.isEmpty() } + .toTypedArray() + return s[s.size - 1] + } + + fun StringToStringArray(random: String): List { + val l = ArrayList() + val chars = random.toCharArray() + for (i in chars.indices) { + l.add(chars[i] + "") + } + return l + } + + @Suppress("DEPRECATION") + fun getColor(res: Resources, color: Int): Int { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return res.getColor(color, res.newTheme()) + } + return res.getColor(color) + } + + fun showTel(context: Context, t: String) { + showTel(context, t, "4001006501") + } + + fun showTel(context: Context, title: String, cell: String) { + val mAlertDialog = CommonAlertDialog(context) + mAlertDialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + mAlertDialog.dismiss() + val intent = Intent() + intent.action = Intent.ACTION_CALL + intent.data = Uri.parse("tel:$cell") + context.startActivity(intent) + } + + override fun onCancle() { + super.onCancle() + mAlertDialog.dismiss() + } + }) + mAlertDialog.setTitle( + if (TextUtils.isEmpty(title)) { + "联系客服" + } else { + title + } + ) + mAlertDialog.setSureMsg("立即拨号") + mAlertDialog.setMsg("电话:$cell") + mAlertDialog.show() + } + + + fun isMobileNO(mobiles: String): Boolean { + // mobiles = mobiles.replace(" ", "") + val telRegex = "[1]\\d{10}" //"[1]"代表第1位为数字1,"\\d{9}"代表后面是可以是0~9的数字,有10位。 + return if (TextUtils.isEmpty(mobiles)) false + else mobiles.matches(telRegex.toRegex()) + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/UtillString.java b/utill/src/main/java/com/lennon/cn/utill/utill/UtillString.java new file mode 100644 index 0000000..e5285fd --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/UtillString.java @@ -0,0 +1,9 @@ +package com.lennon.cn.utill.utill; + +/** + * Created by lennon on 2017/5/5. + */ + +public class UtillString { + public static final String SAMPLE_DIR_NAME = "hmgj"; +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/VersionUtill.java b/utill/src/main/java/com/lennon/cn/utill/utill/VersionUtill.java new file mode 100644 index 0000000..1a6732b --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/VersionUtill.java @@ -0,0 +1,131 @@ +package com.lennon.cn.utill.utill; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.provider.Settings; +import android.telephony.TelephonyManager; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.UUID; + +import androidx.core.app.ActivityCompat; + +/** + * 作者:11361 on 2019/1/24 10:07 + *

+ * 邮箱:1136160757@qq.com + */ +public class VersionUtill { + @SuppressLint({"WifiManagerLeak", "MissingPermission", "HardwareIds"}) + public static String getAndroidId(Activity activity) { + int REQUEST_EXTERNAL_STORAGE = 1; + String[] PERMISSIONS_STORAGE = { + Manifest.permission.BLUETOOTH, + Manifest.permission.ACCESS_WIFI_STATE, + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + }; + int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE); + + if (permission != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(activity, + Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(activity, + Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(activity, + Manifest.permission.ACCESS_WIFI_STATE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(activity, + Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED) { + // We don't have permission so prompt the user + ActivityCompat.requestPermissions( + activity, + PERMISSIONS_STORAGE, + REQUEST_EXTERNAL_STORAGE + ); + } + BluetoothAdapter m_BluetoothAdapter = null; // Local Bluetooth adapter + m_BluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + String m_szBTMAC = ""; + if (m_BluetoothAdapter != null) { + m_szBTMAC = m_BluetoothAdapter.getAddress();//蓝牙MAC地址 + } + WifiManager wm = (WifiManager) activity.getSystemService(Context.WIFI_SERVICE); + String m_szWLANMAC = wm.getConnectionInfo().getMacAddress();//wifi MAC地址 + String m_szDevIDShort = "35" + //we make this look like a valid IMEI + Build.BOARD.length() % 10 +//主板编号 + Build.BRAND.length() % 10 +//系统定制商 + Build.CPU_ABI.length() % 10 +//cpu指令集 + Build.DEVICE.length() % 10 +//设备参数 + Build.DISPLAY.length() % 10 +//显示屏参数 + Build.HOST.length() % 10 + + Build.ID.length() % 10 +//修订版本列表 + Build.MANUFACTURER.length() % 10 +//硬件制造商 + Build.MODEL.length() % 10 +//版本即最终用户可见的名称 + Build.PRODUCT.length() % 10 +//整个产品的名称 + Build.TAGS.length() % 10 +//描述build的标签,如未签名,debug等等 + Build.TYPE.length() % 10 +//build的类型 + Build.USER.length() % 10; //13 digits + String m_szAndroidID = Settings.Secure.getString(activity.getContentResolver(), Settings.Secure.ANDROID_ID); + TelephonyManager TelephonyMgr = (TelephonyManager) activity.getSystemService(Activity.TELEPHONY_SERVICE); + String m_szImei = TelephonyMgr.getDeviceId(); + String m_szLongID = m_szImei + m_szDevIDShort + + m_szAndroidID + m_szWLANMAC + m_szBTMAC; +// compute md5 + MessageDigest m = null; + try { + m = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + m.update(m_szLongID.getBytes(), 0, m_szLongID.length()); +// get md5 bytes + byte p_md5Data[] = m.digest(); +// create a hex string + StringBuilder m_szUniqueID = new StringBuilder(); + for (int i = 0; i < p_md5Data.length; i++) { + int b = (0xFF & p_md5Data[i]); +// if it is a single digit, make sure it have 0 in front (proper padding) + if (b <= 0xF) { + m_szUniqueID.append("0"); + } +// add number to string + m_szUniqueID.append(Integer.toHexString(b)); + } // hex string to uppercase + m_szUniqueID = new StringBuilder(m_szUniqueID.toString().toUpperCase()); + return m_szUniqueID.toString(); + } + + @SuppressLint("MissingPermission") + public static String getUUID() { + return UUID.randomUUID().toString().replace("-", ""); + } + + + //版本名 + public static String getVersionName(Context context) { + return getPackageInfo(context).versionName; + } + + //版本号 + public static int getVersionCode(Context context) { + return getPackageInfo(context).versionCode; + } + + private static PackageInfo getPackageInfo(Context context) { + PackageInfo pi = null; + try { + PackageManager pm = context.getPackageManager(); + pi = pm.getPackageInfo(context.getPackageName(), + PackageManager.GET_CONFIGURATIONS); + return pi; + } catch (Exception e) { + e.printStackTrace(); + } + return pi; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/ZipUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/ZipUtil.java new file mode 100644 index 0000000..e16c7bb --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/ZipUtil.java @@ -0,0 +1,166 @@ +package com.lennon.cn.utill.utill; + +import java.io.*; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +public class ZipUtil { + public static File zip(String src, String dest) throws IOException { + //提供了一个数据项压缩成一个ZIP归档输出流 + ZipOutputStream out = null; + try { + + File outFile = new File(dest);//源文件或者目录 + File fileOrDirectory = new File(src);//压缩文件路径 + out = new ZipOutputStream(new FileOutputStream(outFile)); + //如果此文件是一个文件,否则为false。 + if (fileOrDirectory.isFile()) { + zipFileOrDirectory(out, fileOrDirectory, ""); + } else { + //返回一个文件或空阵列。 + File[] entries = fileOrDirectory.listFiles(); + for (int i = 0; i < entries.length; i++) { + // 递归压缩,更新curPaths + zipFileOrDirectory(out, entries[i], ""); + } + } + return outFile; + } catch (IOException ex) { + ex.printStackTrace(); + } finally { + //关闭输出流 + if (out != null) { + try { + out.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + } + return null; + } + + private static void zipFileOrDirectory(ZipOutputStream out, + File fileOrDirectory, String curPath) throws IOException { + //从文件中读取字节的输入流 + FileInputStream in = null; + try { + //如果此文件是一个目录,否则返回false。 + if (!fileOrDirectory.isDirectory()) { + // 压缩文件 + byte[] buffer = new byte[4096]; + int bytes_read; + in = new FileInputStream(fileOrDirectory); + //实例代表一个条目内的ZIP归档 + ZipEntry entry = new ZipEntry(curPath + + fileOrDirectory.getName()); + //条目的信息写入底层流 + out.putNextEntry(entry); + while ((bytes_read = in.read(buffer)) != -1) { + out.write(buffer, 0, bytes_read); + } + out.closeEntry(); + } else { + // 压缩目录 + File[] entries = fileOrDirectory.listFiles(); + for (int i = 0; i < entries.length; i++) { + // 递归压缩,更新curPaths + zipFileOrDirectory(out, entries[i], curPath + + fileOrDirectory.getName() + "/"); + } + } + } catch (IOException ex) { + ex.printStackTrace(); + // throw ex; + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + } + } + + @SuppressWarnings("unchecked") + public static void unzip(String zipFileName, String outputDirectory) + throws IOException { + ZipFile zipFile = null; + try { + zipFile = new ZipFile(zipFileName); + Enumeration e = zipFile.entries(); + ZipEntry zipEntry = null; + File dest = new File(outputDirectory); + dest.mkdirs(); + while (e.hasMoreElements()) { + zipEntry = (ZipEntry) e.nextElement(); + String entryName = zipEntry.getName(); + InputStream in = null; + FileOutputStream out = null; + try { + if (zipEntry.isDirectory()) { + String name = zipEntry.getName(); + name = name.substring(0, name.length() - 1); + File f = new File(outputDirectory + File.separator + + name); + f.mkdirs(); + } else { + int index = entryName.lastIndexOf("\\"); + if (index != -1) { + File df = new File(outputDirectory + File.separator + + entryName.substring(0, index)); + df.mkdirs(); + } + index = entryName.lastIndexOf("/"); + if (index != -1) { + File df = new File(outputDirectory + File.separator + + entryName.substring(0, index)); + df.mkdirs(); + } + File f = new File(outputDirectory + File.separator + + zipEntry.getName()); + // f.createNewFile(); + in = zipFile.getInputStream(zipEntry); + out = new FileOutputStream(f); + int c; + byte[] by = new byte[1024]; + while ((c = in.read(by)) != -1) { + out.write(by, 0, c); + } + out.flush(); + } + } catch (IOException ex) { + ex.printStackTrace(); + throw new IOException("解压失败:" + ex.toString()); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException ex) { + } + } + if (out != null) { + try { + out.close(); + } catch (IOException ex) { + } + } + } + } + } catch (IOException ex) { + ex.printStackTrace(); + throw new IOException("解压失败:" + ex.toString()); + } finally { + if (zipFile != null) { + try { + zipFile.close(); + } catch (IOException ex) { + } + } + } + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/photo/PhotoString.java b/utill/src/main/java/com/lennon/cn/utill/utill/photo/PhotoString.java new file mode 100644 index 0000000..605464d --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/photo/PhotoString.java @@ -0,0 +1,20 @@ +package com.lennon.cn.utill.utill.photo; + +import com.lennon.cn.utill.bean.StringBean; + +public class PhotoString implements StringBean { + private String name; + + @Override + public String getItemString() { + return name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/photo/SelectPicUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/photo/SelectPicUtil.java new file mode 100644 index 0000000..f33b98b --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/photo/SelectPicUtil.java @@ -0,0 +1,254 @@ +package com.lennon.cn.utill.utill.photo; + +import android.app.Activity; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.MediaStore; +import android.text.TextUtils; +import android.widget.Toast; + + +import androidx.core.content.FileProvider; + +import com.lennon.cn.utill.conf.Lennon; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +public class SelectPicUtil { + + private static final String temp = Environment.getExternalStorageDirectory().getAbsolutePath() + "/temp.jpg"; + + public static final int GET_BY_ALBUM = 801;// 打开相册 + public static final int GET_BY_CAMERA = 802;// 打开相机 + public static final int CROP = 803;// 裁剪图片 + + public static void getByAlbum(Activity act) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + Intent intent = new Intent(Intent.ACTION_PICK, + android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); + intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); + act.startActivityForResult(intent, GET_BY_ALBUM); + } else { + Intent intentFromGallery = new Intent(); + intentFromGallery.setType("image/*"); + intentFromGallery.setAction(Intent.ACTION_GET_CONTENT); + act.startActivityForResult(intentFromGallery, GET_BY_ALBUM); + } + } + + public static void getByCamera(Activity act) { + getByCamera(act, temp, GET_BY_CAMERA); + } + + // 给其他地方用 + public static void getByCamera(Activity act, String path, int requestCode) { + String state = Environment.getExternalStorageState(); + if (state.equals(Environment.MEDIA_MOUNTED)) { + Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + Uri uri = getUri(act, new File(path)); + takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + takePictureIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); + act.startActivityForResult(takePictureIntent, requestCode); + } else { + Toast.makeText(act, "请安装sd卡", Toast.LENGTH_SHORT).show(); + } + } + + public static Uri onActivityResult(Activity act, int requestCode, + int resultCode, Intent data) { + return onActivityResult(act, requestCode, resultCode, data, 0, 0, 0, 0, false); + } + + public static Uri onActivityResult(Activity act, int requestCode, + int resultCode, Intent data, int w, int h, int aspectX, int aspectY) { + return onActivityResult(act, requestCode, resultCode, data, w, h, aspectX, aspectY, true); + } + + public static Uri onActivityResult(Activity act, int requestCode, + int resultCode, Intent data, int outputX, int outputY, int aspectX, int aspectY, boolean isCut) { + if (resultCode == Activity.RESULT_OK) { + Uri uri = null; + switch (requestCode) { + case GET_BY_ALBUM: + if (isCut) { + if (null == data) return null; + String path = getImageAbsolutePath(act, data.getData()); + if (TextUtils.isEmpty(path)) return null; + uri = getUri(act, new File(path)); + } else { + return data.getData(); + } + break; + case GET_BY_CAMERA: + if (isCut) { + uri = getUri(act, new File(temp)); + } else { + return Uri.parse(temp); + } + break; + case CROP: +// return getUri(act, new File(temp)); + return Uri.fromFile(new File(temp)); + } + if (isCut && null != uri) { + crop(act, uri, outputX, outputY, aspectX, aspectY); + return null; + } + } + return null; + } + + public static void crop(Activity act, Uri uri, int outputX, int outputY, int aspectX, int aspectY) { + if (outputX == 0 && outputY == 0) { + outputX = outputY = 480; + } + if (aspectX == 0 && aspectY == 0) { + aspectX = aspectY = 1; + } + Intent intent = new Intent("com.android.camera.action.CROP"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + //对目标应用临时授权该Uri所代表的文件 + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + } + intent.setDataAndType(uri, "image/*"); + intent.putExtra("crop", "true"); + intent.putExtra("aspectX", aspectX); + intent.putExtra("aspectY", aspectY); + intent.putExtra("outputX", outputX); + intent.putExtra("outputY", outputY); + +// intent.putExtra(MediaStore.EXTRA_OUTPUT, getUri(act, new File(temp))); + intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(temp))); + + intent.putExtra("outputFormat", "JPEG"); + + intent.putExtra("noFaceDetection", true); + intent.putExtra("return-data", false); + act.startActivityForResult(intent, CROP); + } + + public static String getImageAbsolutePath(Activity context, Uri uri) { + if (null == uri) + return null; + final String scheme = uri.getScheme(); + String data = null; + if (scheme == null) + data = uri.getPath(); + else if (ContentResolver.SCHEME_FILE.equals(scheme)) { + data = uri.getPath(); + } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) { + Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, + null, null); + if (null != cursor) { + if (cursor.moveToFirst()) { + int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); + if (index > -1) { + data = cursor.getString(index); + } + } + cursor.close(); + } + } + return data; + } + + + public static Uri getUri(Context context, File file) { + Uri fileUri; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + // BuildConfig 是工程包路径下的:如:com.yingyou.demo.BuildConfig + fileUri = FileProvider.getUriForFile(context, Lennon.Companion.getFileProvide(), file); + } else { + fileUri = Uri.fromFile(file); + } + + return fileUri; + } + + public static Bitmap getImageThumbnail(String imagePath, int width, + int height) { + Bitmap bitmap = null; + BitmapFactory.Options options = new BitmapFactory.Options(); + // Options中有个属性inJustDecodeBounds。我们可以充分利用它,来避免大图片的溢出问题 + options.inJustDecodeBounds = true;// 设置为true可以不加载到内存,直接获取Bitmap宽高 + // 获取这个图片的宽和高,注意此处的bitmap为null + bitmap = BitmapFactory.decodeFile(imagePath, options); + if (bitmap == null) { + // 计算缩放比 + int h = options.outHeight;// 获取Bitmap的实际高度 + int w = options.outWidth;// 获取Bitmap的实际宽度 + + int beWidth = w / width; + int beHeight = h / height; + int rate = 1; + if (beWidth < beHeight) { + rate = beWidth; + } else { + rate = beHeight; + } + if (rate <= 0) {// 图片实际大小小于缩略图,不缩放 + rate = 1; + } + options.inSampleSize = rate;// rate就是压缩的比例 + options.inJustDecodeBounds = false; + // 重新读入图片,读取缩放后的bitmap,注意这次要把options.inJustDecodeBounds 设为 false + bitmap = BitmapFactory.decodeFile(imagePath, options);// 获取压缩后的图片 + } + return bitmap; + } + + public static Bitmap getBitmapFormUri(Activity ac, Uri uri) { + InputStream input; + try { + input = ac.getContentResolver().openInputStream(uri); + BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options(); + onlyBoundsOptions.inJustDecodeBounds = true; + onlyBoundsOptions.inDither = true;// optional + onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;// optional + BitmapFactory.decodeStream(input, null, onlyBoundsOptions); + input.close(); + int originalWidth = onlyBoundsOptions.outWidth; + int originalHeight = onlyBoundsOptions.outHeight; + if ((originalWidth == -1) || (originalHeight == -1)) + return null; + // 图片分辨率以480x800为标准 + float hh = 800f;// 这里设置高度为800f + float ww = 480f;// 这里设置宽度为480f + // 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可 + int be = 1;// be=1表示不缩放 + if (originalWidth > originalHeight && originalWidth > ww) {// 如果宽度大的话根据宽度固定大小缩放 + be = (int) (originalWidth / ww); + } else if (originalWidth < originalHeight && originalHeight > hh) {// 如果高度高的话根据宽度固定大小缩放 + be = (int) (originalHeight / hh); + } + if (be <= 0) + be = 1; + // 比例压缩 + BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); + bitmapOptions.inSampleSize = be;// 设置缩放比例 + bitmapOptions.inDither = true;// optional + bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;// optional + input = ac.getContentResolver().openInputStream(uri); + Bitmap bitmap = BitmapFactory.decodeStream(input, null, + bitmapOptions); + input.close(); + return bitmap;// 再进行质量压缩 + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSACoder.java b/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSACoder.java new file mode 100644 index 0000000..e417a10 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSACoder.java @@ -0,0 +1,258 @@ +package com.lennon.cn.utill.utill.rsa; + +import android.util.Base64; + +import javax.crypto.Cipher; +import java.security.*; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashMap; +import java.util.Map; + + +/** + * Created by humf.需要依赖 commons-codec 包 + */ +public class RSACoder { + public static final String KEY_ALGORITHM = "RSA"; + public static final String SIGNATURE_ALGORITHM = "MD5withRSA"; + + private static final String PUBLIC_KEY = "RSAPublicKey"; + private static final String PRIVATE_KEY = "RSAPrivateKey"; + + public static byte[] decryptBASE64(String key) { + return Base64.decode(key, Base64.DEFAULT); + } + + public static String encryptBASE64(byte[] bytes) { + return Base64.encodeToString(bytes, Base64.DEFAULT); + } + + /** + * 用私钥对信息生成数字签名 + * + * @param data 加密数据 + * @param privateKey 私钥 + * @return + * @throws Exception + */ + public static String sign(byte[] data, String privateKey) throws Exception { + // 解密由base64编码的私钥 + byte[] keyBytes = decryptBASE64(privateKey); + // 构造PKCS8EncodedKeySpec对象 + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + // KEY_ALGORITHM 指定的加密算法 + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + // 取私钥匙对象 + PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec); + // 用私钥对信息生成数字签名 + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + signature.initSign(priKey); + signature.update(data); + return encryptBASE64(signature.sign()); + } + + /** + * 校验数字签名 + * + * @param data 加密数据 + * @param publicKey 公钥 + * @param sign 数字签名 + * @return 校验成功返回true 失败返回false + * @throws Exception + */ + public static boolean verify(byte[] data, String publicKey, String sign) + throws Exception { + // 解密由base64编码的公钥 + byte[] keyBytes = decryptBASE64(publicKey); + // 构造X509EncodedKeySpec对象 + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); + // KEY_ALGORITHM 指定的加密算法 + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + // 取公钥匙对象 + PublicKey pubKey = keyFactory.generatePublic(keySpec); + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + signature.initVerify(pubKey); + signature.update(data); + // 验证签名是否正常 + return signature.verify(decryptBASE64(sign)); + } + + /** + * 私钥解密密文 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception { + // 对密钥解密 + byte[] keyBytes = decryptBASE64(key); + // 取得私钥 + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec); + // 对数据解密 + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + return cipher.doFinal(data); + } + + /** + * 解密
+ * 用私钥解密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static byte[] decryptByPrivateKey(String data, String key) + throws Exception { + return decryptByPrivateKey(decryptBASE64(data), key); + } + + /** + * 解密
+ * 用公钥解密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static byte[] decryptByPublicKey(byte[] data, String key) + throws Exception { + // 对密钥解密 + byte[] keyBytes = decryptBASE64(key); + // 取得公钥 + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key publicKey = keyFactory.generatePublic(x509KeySpec); + // 对数据解密 + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.DECRYPT_MODE, publicKey); + return cipher.doFinal(data); + } + + /** + * 加密
+ * 用公钥加密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static byte[] encryptByPublicKey(String data, String key) + throws Exception { + // 对公钥解密 + byte[] keyBytes = decryptBASE64(key); + // 取得公钥 + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key publicKey = keyFactory.generatePublic(x509KeySpec); + // 对数据加密 + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + return cipher.doFinal(data.getBytes()); + } + + /** + * 加密
+ * 用私钥加密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static byte[] encryptByPrivateKey(byte[] data, String key) + throws Exception { + // 对密钥解密 + byte[] keyBytes = decryptBASE64(key); + // 取得私钥 + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec); + // 对数据加密 + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.ENCRYPT_MODE, privateKey); + return cipher.doFinal(data); + } + + /** + * 取得私钥 + * + * @param keyMap + * @return + * @throws Exception + */ + public static String getPrivateKey(Map keyMap) + throws Exception { + Key key = (Key) keyMap.get(PRIVATE_KEY); + return encryptBASE64(key.getEncoded()); + } + + /** + * 取得公钥 + * + * @param keyMap + * @return + * @throws Exception + */ + public static String getPublicKey(Map keyMap) + throws Exception { + Key key = keyMap.get(PUBLIC_KEY); + return encryptBASE64(key.getEncoded()); + } + + /** + * 初始化密钥 + * + * @return + * @throws Exception + */ + public static Map initKey() throws Exception { + KeyPairGenerator keyPairGen = KeyPairGenerator + .getInstance(KEY_ALGORITHM); + keyPairGen.initialize(1024); + KeyPair keyPair = keyPairGen.generateKeyPair(); + Map keyMap = new HashMap(2); + keyMap.put(PUBLIC_KEY, keyPair.getPublic());// 公钥 + keyMap.put(PRIVATE_KEY, keyPair.getPrivate());// 私钥 + return keyMap; + } + + public static void main(String[] args) throws Exception { + Map keyMap = initKey(); + String publicKey = getPublicKey(keyMap); + String privateKey = getPrivateKey(keyMap); + + System.out.println(keyMap); + System.out.println("-----------------------------------"); + System.out.println(publicKey); + System.out.println("-----------------------------------"); + System.out.println(privateKey); + System.out.println("-----------------------------------"); + byte[] encryptByPrivateKey = encryptByPrivateKey("123456".getBytes(), privateKey); + byte[] encryptByPublicKey = encryptByPublicKey("123456", publicKey); + System.out.println(new String(encryptByPrivateKey)); + System.out.println("-----------------------------------"); + System.out.println(new String(encryptByPublicKey)); + System.out.println("-----------------------------------"); + String sign = sign(encryptByPrivateKey, privateKey); + System.out.println(sign); + System.out.println("-----------------------------------"); + boolean verify = verify(encryptByPrivateKey, publicKey, sign); + System.out.println(verify); + System.out.println("-----------------------------------"); + byte[] decryptByPublicKey = decryptByPublicKey(encryptByPrivateKey, publicKey); + byte[] decryptByPrivateKey = decryptByPrivateKey(encryptByPublicKey, privateKey); + System.out.println(new String(decryptByPublicKey)); + System.out.println("-----------------------------------"); + System.out.println(new String(decryptByPrivateKey)); + + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSAKeyUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSAKeyUtil.java new file mode 100644 index 0000000..d4a9199 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSAKeyUtil.java @@ -0,0 +1,92 @@ +package com.lennon.cn.utill.utill.rsa; + +import android.os.Build; +import android.util.Base64; + +import java.security.*; +import java.util.Arrays; + +import androidx.annotation.RequiresApi; + +public class RSAKeyUtil { + + @RequiresApi(api = Build.VERSION_CODES.FROYO) + public static RSAKey createKeyPairsStr() throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(512, new SecureRandom()); + KeyPair pair = generator.generateKeyPair(); + PublicKey pubKey = pair.getPublic(); + PrivateKey privKey = pair.getPrivate(); + byte[] publicKeyByte = pubKey.getEncoded(); + byte[] privateKeyByte = privKey.getEncoded(); + String publicKeyStr = Base64.encodeToString(publicKeyByte, Base64.DEFAULT); + String privateKeyStr = Base64.encodeToString(privateKeyByte, Base64.DEFAULT); + + System.out.println("公钥:" + Arrays.toString(publicKeyByte)); + System.out.println("私钥:" + Arrays.toString(privateKeyByte)); + System.out.println("公钥Base64编码:" + publicKeyStr); + System.out.println("私钥Base64编码:" + privateKeyStr); + + return RSAKey.builder().addPublicKeyStr(publicKeyStr).addPrivateKeyStr(privateKeyStr).build(); + } + + public static class RSAKey { + private String publicKeyStr; + private String privateKeyStr; + + private RSAKey(RSAKeyBuilder rSAKeyBuilder) { + this.publicKeyStr = rSAKeyBuilder.publicKeyStr; + this.privateKeyStr = rSAKeyBuilder.privateKeyStr; + } + + public String getPublicKeyStr() { + return publicKeyStr; + } + + public void setPublicKeyStr(String publicKeyStr) { + this.publicKeyStr = publicKeyStr; + } + + public String getPrivateKeyStr() { + return privateKeyStr; + } + + public void setPrivateKeyStr(String privateKeyStr) { + this.privateKeyStr = privateKeyStr; + } + + /** + * Creates builder to build {@link RSAKey}. + * + * @return created builder + */ + public static RSAKeyBuilder builder() { + return new RSAKeyBuilder(); + } + + /** + * Builder to build {@link RSAKey}. + */ + public static final class RSAKeyBuilder { + private String publicKeyStr; + private String privateKeyStr; + + private RSAKeyBuilder() { + } + + public RSAKeyBuilder addPublicKeyStr(String publicKeyStr) { + this.publicKeyStr = publicKeyStr; + return this; + } + + public RSAKeyBuilder addPrivateKeyStr(String privateKeyStr) { + this.privateKeyStr = privateKeyStr; + return this; + } + + public RSAKey build() { + return new RSAKey(this); + } + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSAUtils.java b/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSAUtils.java new file mode 100644 index 0000000..0c5fc2e --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSAUtils.java @@ -0,0 +1,373 @@ +package com.lennon.cn.utill.utill.rsa; + +import android.util.Base64; + +import javax.crypto.Cipher; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + + +public class RSAUtils { + private static final String ALGORITHM = "RSA"; + private static final String TRANSFORMATION = "RSA"; + + /** + * 从文件中输入流中加载公钥 + * + * @param in 公钥输入流 + * @throws Exception 加载公钥时产生的异常 + */ + public static RSAPublicKey loadPublicKey(InputStream in) throws Exception { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + String readLine = null; + StringBuilder sb = new StringBuilder(); + while ((readLine = br.readLine()) != null) { + if (readLine.charAt(0) == '-') { + continue; + } else { + sb.append(readLine); + sb.append('\r'); + } + } + return loadPublicKey(sb.toString()); + } catch (IOException e) { + throw new Exception("公钥数据流读取错误"); + } catch (NullPointerException e) { + throw new Exception("公钥输入流为空"); + } + } + + /** + * 从字符串中加载公钥 + * + * @param publicKeyStr 公钥数据字符串 + * @return + * @throws Exception 加载公钥时产生的异常 + */ + public static RSAPublicKey loadPublicKey(String publicKeyStr) + throws Exception { + try { + byte[] buffer = Base64.decode(publicKeyStr, Base64.DEFAULT); + KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer); + return (RSAPublicKey) keyFactory.generatePublic(keySpec); + } catch (NoSuchAlgorithmException e) { + throw new Exception("无此算法"); + } catch (InvalidKeySpecException e) { + throw new Exception("公钥非法"); + } catch (NullPointerException e) { + throw new Exception("公钥数据为空"); + } + } + + /** + * 从文件中加载私钥 + * + * @param in 私钥输入流 + * @return + * @throws Exception + */ + public static RSAPrivateKey loadPrivateKey(InputStream in) throws Exception { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + String readLine = null; + StringBuilder sb = new StringBuilder(); + while ((readLine = br.readLine()) != null) { + if (readLine.charAt(0) == '-') { + continue; + } else { + sb.append(readLine); + sb.append('\r'); + } + } + return loadPrivateKey(sb.toString()); + } catch (IOException e) { + throw new Exception("私钥数据读取错误"); + } catch (NullPointerException e) { + throw new Exception("私钥输入流为空"); + } + } + + /** + * 从字符串中加载私钥 + * + * @param privateKeyStr 私钥字符串 + * @return + * @throws Exception + * @desc + */ + public static RSAPrivateKey loadPrivateKey(String privateKeyStr) + throws Exception { + try { + byte[] buffer = Base64.decode(privateKeyStr, Base64.DEFAULT); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer); + KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); + return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); + } catch (NoSuchAlgorithmException e) { + throw new Exception("无此算法"); + } catch (InvalidKeySpecException e) { + throw new Exception("私钥非法"); + } catch (NullPointerException e) { + throw new Exception("私钥数据为空"); + } + } + + /** + * 公钥加密 + * + * @param data + * @param publicKey + * @return + * @throws Exception + */ + public static String encryptByPublicKey(String data, RSAPublicKey publicKey) + throws Exception { + // 模长 + int key_len = publicKey.getModulus().bitLength() / 8; + // 加密数据长度 <= 模长-11 + String[] datas = splitString(data, key_len - 11); + String mi = ""; + // 如果明文长度大于模长-11则要分组加密 + for (String s : datas) { + mi += bcd2Str(encryptByPublicKey(s.getBytes(), publicKey)); + } + return mi; + } + + /** + * 公钥加密 + * + * @param data + * @param publicKey + * @return + * @throws Exception + * @desc + */ + public static byte[] encryptByPublicKey(byte[] data, RSAPublicKey publicKey) + throws Exception { + Cipher cipher = Cipher.getInstance(TRANSFORMATION); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + return cipher.doFinal(data); + } + + /** + * 私钥加密 + * + * @param data + * @param privateKey + * @return + * @throws Exception + * @desc + */ + public static byte[] encryptByPrivateKey(byte[] data, + RSAPrivateKey privateKey) throws Exception { + Cipher cipher = Cipher.getInstance(TRANSFORMATION); + cipher.init(Cipher.ENCRYPT_MODE, privateKey); + return cipher.doFinal(data); + } + + + /** + * 私钥加密 + * + * @param data + * @param privateKey + * @return + * @throws Exception + * @desc + */ + public static String encryptByPrivateKey(String data, + RSAPrivateKey privateKey) throws Exception { + // 模长 + int key_len = privateKey.getModulus().bitLength() / 8; + // 加密数据长度 <= 模长-11 + String[] datas = splitString(data, key_len - 11); + String mi = ""; + // 如果明文长度大于模长-11则要分组加密 + for (String s : datas) { + mi += bcd2Str(encryptByPrivateKey(s.getBytes(), privateKey)); + } + return mi; + } + + /** + * 私钥解密 + * + * @param data + * @param privateKey + * @return + * @throws Exception + */ + public static String decryptByPrivateKey(String data, + RSAPrivateKey privateKey) throws Exception { + // 模长 + int key_len = privateKey.getModulus().bitLength() / 8; + byte[] bytes = data.getBytes(); + byte[] bcd = ASCII_To_BCD(bytes, bytes.length); + // 如果密文长度大于模长则要分组解密 + String ming = ""; + byte[][] arrays = splitArray(bcd, key_len); + for (byte[] arr : arrays) { + ming += new String(decryptByPrivateKey(arr, privateKey)); + } + return ming; + } + + /** + * 私钥解密 + * + * @param data + * @param privateKey + * @return + * @throws Exception + * @desc + */ + public static byte[] decryptByPrivateKey(byte[] data, + RSAPrivateKey privateKey) throws Exception { + Cipher cipher = Cipher.getInstance(TRANSFORMATION); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + return cipher.doFinal(data); + } + + /** + * 公钥解密 + * + * @param data + * @param publicKey + * @return + * @throws Exception + * @desc + */ + public static String decryptByPublicKey(String data, + RSAPublicKey publicKey) throws Exception { + // 模长 + int key_len = publicKey.getModulus().bitLength() / 8; + byte[] bytes = data.getBytes(); + byte[] bcd = ASCII_To_BCD(bytes, bytes.length); + // 如果密文长度大于模长则要分组解密 + String ming = ""; + byte[][] arrays = splitArray(bcd, key_len); + for (byte[] arr : arrays) { + ming += new String(decryptByPublicKey(arr, publicKey)); + } + return ming; + } + + /** + * 公钥解密 + * + * @param data + * @param publicKey + * @return + * @throws Exception + * @desc + */ + public static byte[] decryptByPublicKey(byte[] data, + RSAPublicKey publicKey) throws Exception { + Cipher cipher = Cipher.getInstance(TRANSFORMATION); + cipher.init(Cipher.DECRYPT_MODE, publicKey); + return cipher.doFinal(data); + } + + /** + * ASCII码转BCD码 + */ + private static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) { + byte[] bcd = new byte[asc_len / 2]; + int j = 0; + for (int i = 0; i < (asc_len + 1) / 2; i++) { + bcd[i] = asc_to_bcd(ascii[j++]); + bcd[i] = (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++])) + (bcd[i] << 4)); + } + return bcd; + } + + private static byte asc_to_bcd(byte asc) { + byte bcd; + + if ((asc >= '0') && (asc <= '9')) { + bcd = (byte) (asc - '0'); + } else if ((asc >= 'A') && (asc <= 'F')) { + bcd = (byte) (asc - 'A' + 10); + } else if ((asc >= 'a') && (asc <= 'f')) { + bcd = (byte) (asc - 'a' + 10); + } else { + bcd = (byte) (asc - 48); + } + return bcd; + } + + /** + * BCD转字符串 + */ + private static String bcd2Str(byte[] bytes) { + char temp[] = new char[bytes.length * 2], val; + + for (int i = 0; i < bytes.length; i++) { + val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f); + temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0'); + + val = (char) (bytes[i] & 0x0f); + temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0'); + } + return new String(temp); + } + + /** + * 拆分字符串 + */ + private static String[] splitString(String string, int len) { + int x = string.length() / len; + int y = string.length() % len; + int z = 0; + if (y != 0) { + z = 1; + } + String[] strings = new String[x + z]; + String str = ""; + for (int i = 0; i < x + z; i++) { + if (i == x + z - 1 && y != 0) { + str = string.substring(i * len, i * len + y); + } else { + str = string.substring(i * len, i * len + len); + } + strings[i] = str; + } + return strings; + } + + /** + * 拆分数组 + */ + private static byte[][] splitArray(byte[] data, int len) { + int x = data.length / len; + int y = data.length % len; + int z = 0; + if (y != 0) { + z = 1; + } + byte[][] arrays = new byte[x + z][]; + byte[] arr; + for (int i = 0; i < x + z; i++) { + arr = new byte[len]; + if (i == x + z - 1 && y != 0) { + System.arraycopy(data, i * len, arr, 0, y); + } else { + System.arraycopy(data, i * len, arr, 0, len); + } + arrays[i] = arr; + } + return arrays; + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/rx/RxTool.java b/utill/src/main/java/com/lennon/cn/utill/utill/rx/RxTool.java new file mode 100644 index 0000000..63a108e --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/rx/RxTool.java @@ -0,0 +1,68 @@ +package com.lennon.cn.utill.utill.rx; + +import cn.droidlover.xdroidmvp.net.NetError; +import org.reactivestreams.Publisher; + +import java.util.concurrent.Callable; + +import io.reactivex.BackpressureStrategy; +import io.reactivex.Flowable; +import io.reactivex.FlowableEmitter; +import io.reactivex.FlowableOnSubscribe; +import io.reactivex.FlowableTransformer; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.functions.Function; +import io.reactivex.schedulers.Schedulers; + +public class RxTool { + /** + * 线程切换 + * + * @return + */ + public static FlowableTransformer getScheduler() { + return new FlowableTransformer() { + @Override + public Publisher apply(Flowable upstream) { + return upstream.subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .unsubscribeOn(AndroidSchedulers.mainThread()); + } + }; + } + + /** + * 异常处理变换 + * + * @return + */ + public static FlowableTransformer getApiTransformer() { + + return new FlowableTransformer() { + @Override + public Publisher apply(Flowable upstream) { + return upstream.flatMap(new Function>() { + @Override + public Publisher apply(T model) throws Exception { + if (model == null ) { + return Flowable.error(new NetError("无数据", NetError.NoDataError)); + } else { + return Flowable.just(model); + } + } + }); + } + }; + } + public static Flowable makeFlowable(final Callable func) { + return Flowable.create(new FlowableOnSubscribe() { + @Override + public void subscribe(FlowableEmitter e) throws Exception { + try { + e.onNext(func.call()); + } catch (Exception e1) { + } + } + }, BackpressureStrategy.ERROR); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/time/DateStyle.java b/utill/src/main/java/com/lennon/cn/utill/utill/time/DateStyle.java new file mode 100644 index 0000000..233a75e --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/time/DateStyle.java @@ -0,0 +1,57 @@ +package com.lennon.cn.utill.utill.time; + +/** + * Created by lennon on 2017/8/29. + */ + +public enum DateStyle { + MMDD("MMdd"), + YYYYMM("yyyyMM"), + YYYYMMDD("yyyyMMdd"), + + MM_DD("MM-dd"), + YYYY_MM("yyyy-MM"), + YYYY_MM_DD("yyyy-MM-dd"), + MM_DD_HH_MM("MM-dd HH:mm"), + MM_DD_HH_MM_SS("MM-dd HH:mm:ss"), + YYYY_MM_DD_HH_MM("yyyy-MM-dd HH:mm"), + YYYY_MM_DD_HH_MM_SS("yyyy-MM-dd HH:mm:ss"), + + MM_DD_EN("MM/dd"), + YYYY_MM_EN("yyyy/MM"), + YYYY_MM_DD_EN("yyyy/MM/dd"), + MM_DD_HH_MM_EN("MM/dd HH:mm"), + MM_DD_HH_MM_SS_EN("MM/dd HH:mm:ss"), + YYYY_MM_DD_HH_MM_EN("yyyy/MM/dd HH:mm"), + YYYY_MM_DD_HH_MM_SS_EN("yyyy/MM/dd HH:mm:ss"), + + MM_DD_CN("MM月dd日"), + YYYY_MM_CN("yyyy年MM月"), + YYYY_MM_DD_CN("yyyy年MM月dd日"), + MM_DD_HH_MM_CN("MM月dd日 HH:mm"), + MM_DD_HH_MM_SS_CN("MM月dd日 HH:mm:ss"), + YYYY_MM_DD_HH_MM_CN("yyyy年MM月dd日 HH:mm"), + YYYY_MM_DD_HH_MM_SS_CN("yyyy年MM月dd日 HH:mm:ss"), + + HH_MM("HH:mm"), + HH_MM_SS("HH:mm:ss"), + + MM_DD_P("MM.dd"), + YYYY_MM_P("yyyy.MM"), + YYYY_MM_DD_P("yyyy.MM.dd"), + MM_DD_HH_MM_P("MM.dd HH:mm"), + MM_DD_HH_MM_SS_P("MM.dd HH:mm:ss"), + YYYY_MM_DD_HH_MM_P("yyyy.MM.dd HH:mm"), + YYYY_MM_DD_HH_MM_SS_P("yyyy.MM.dd HH:mm:ss"); + + + private String value; + + DateStyle(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/time/DateUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/time/DateUtil.java new file mode 100644 index 0000000..6bd09b2 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/time/DateUtil.java @@ -0,0 +1,771 @@ +package com.lennon.cn.utill.utill.time; + +import org.jetbrains.annotations.NotNull; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by lennon on 2017/8/29. + */ + +public class DateUtil { + /** + * 获取SimpleDateFormat + * + * @param parttern 日期格式 + * @return SimpleDateFormat对象 + * @throws RuntimeException 异常:非法日期格式 + */ + private static SimpleDateFormat getDateFormat(String parttern) throws RuntimeException { + return new SimpleDateFormat(parttern); + } + + /** + * 获取日期中的某数值。如获取月份 + * + * @param date 日期 + * @param dateType 日期格式 + * @return 数值 + */ + private static int getInteger(Date date, int dateType) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(dateType); + } + + /** + * 增加日期中某类型的某数值。如增加日期 + * + * @param date 日期字符串 + * @param dateType 类型 + * @param amount 数值 + * @return 计算后日期字符串 + */ + private static String addInteger(String date, int dateType, int amount) { + String dateString = null; + DateStyle dateStyle = getDateStyle(date); + if (dateStyle != null) { + Date myDate = StringToDate(date, dateStyle); + myDate = addInteger(myDate, dateType, amount); + dateString = DateToString(myDate, dateStyle); + } + return dateString; + } + + /** + * 增加日期中某类型的某数值。如增加日期 + * + * @param date 日期 + * @param dateType 类型 + * @param amount 数值 + * @return 计算后日期 + */ + private static Date addInteger(Date date, int dateType, int amount) { + Date myDate = null; + if (date != null) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.add(dateType, amount); + myDate = calendar.getTime(); + } + return myDate; + } + + /** + * 获取精确的日期 + * + * @param timestamps 时间long集合 + * @return 日期 + */ + private static Date getAccurateDate(List timestamps) { + Date date = null; + long timestamp = 0; + Map map = new HashMap(); + List absoluteValues = new ArrayList(); + + if (timestamps != null && timestamps.size() > 0) { + if (timestamps.size() > 1) { + for (int i = 0; i < timestamps.size(); i++) { + for (int j = i + 1; j < timestamps.size(); j++) { + long absoluteValue = Math.abs(timestamps.get(i) - timestamps.get(j)); + absoluteValues.add(absoluteValue); + long[] timestampTmp = {timestamps.get(i), timestamps.get(j)}; + map.put(absoluteValue, timestampTmp); + } + } + + // 有可能有相等的情况。如2012-11和2012-11-01。时间戳是相等的 + long minAbsoluteValue = -1; + if (!absoluteValues.isEmpty()) { + // 如果timestamps的size为2,这是差值只有一个,因此要给默认值 + minAbsoluteValue = absoluteValues.get(0); + } + for (int i = 0; i < absoluteValues.size(); i++) { + for (int j = i + 1; j < absoluteValues.size(); j++) { + if (absoluteValues.get(i) > absoluteValues.get(j)) { + minAbsoluteValue = absoluteValues.get(j); + } else { + minAbsoluteValue = absoluteValues.get(i); + } + } + } + + if (minAbsoluteValue != -1) { + long[] timestampsLastTmp = map.get(minAbsoluteValue); + if (absoluteValues.size() > 1) { + timestamp = Math.max(timestampsLastTmp[0], timestampsLastTmp[1]); + } else if (absoluteValues.size() == 1) { + // 当timestamps的size为2,需要与当前时间作为参照 + long dateOne = timestampsLastTmp[0]; + long dateTwo = timestampsLastTmp[1]; + if ((Math.abs(dateOne - dateTwo)) < 100000000000L) { + timestamp = Math.max(timestampsLastTmp[0], timestampsLastTmp[1]); + } else { + long now = System.currentTimeMillis(); + if (Math.abs(dateOne - now) <= Math.abs(dateTwo - now)) { + timestamp = dateOne; + } else { + timestamp = dateTwo; + } + } + } + } + } else { + timestamp = timestamps.get(0); + } + } + + if (timestamp != 0) { + date = new Date(timestamp); + } + return date; + } + + /** + * 判断字符串是否为日期字符串 + * + * @param date 日期字符串 + * @return true or false + */ + public static boolean isDate(String date) { + boolean isDate = false; + if (date != null) { + if (StringToDate(date) != null) { + isDate = true; + } + } + return isDate; + } + + /** + * 获取日期字符串的日期风格。失敗返回null。 + * + * @param date 日期字符串 + * @return 日期风格 + */ + public static DateStyle getDateStyle(String date) { + DateStyle dateStyle = null; + Map map = new HashMap(); + List timestamps = new ArrayList(); + for (DateStyle style : DateStyle.values()) { + Date dateTmp = StringToDate(date, style.getValue()); + if (dateTmp != null) { + timestamps.add(dateTmp.getTime()); + map.put(dateTmp.getTime(), style); + } + } + dateStyle = map.get(getAccurateDate(timestamps).getTime()); + return dateStyle; + } + + /** + * 将日期字符串转化为日期。失败返回null。 + * + * @param date 日期字符串 + * @return 日期 + */ + public static Date StringToDate(String date) { + DateStyle dateStyle = null; + return StringToDate(date, dateStyle); + } + + /** + * 将日期字符串转化为日期。失败返回null。 + * + * @param date 日期字符串 + * @param parttern 日期格式 + * @return 日期 + */ + public static Date StringToDate(String date, String parttern) { + Date myDate = null; + if (date != null) { + try { + myDate = getDateFormat(parttern).parse(date); + } catch (Exception e) { + } + } + return myDate; + } + + /** + * 将日期字符串转化为日期。失败返回null。 + * + * @param date 日期字符串 + * @param dateStyle 日期风格 + * @return 日期 + */ + public static Date StringToDate(String date, DateStyle dateStyle) { + Date myDate = null; + if (dateStyle == null) { + List timestamps = new ArrayList(); + for (DateStyle style : DateStyle.values()) { + Date dateTmp = StringToDate(date, style.getValue()); + if (dateTmp != null) { + timestamps.add(dateTmp.getTime()); + } + } + myDate = getAccurateDate(timestamps); + } else { + myDate = StringToDate(date, dateStyle.getValue()); + } + return myDate; + } + + /** + * 将日期转化为日期字符串。失败返回null。 + * + * @param date 日期 + * @param parttern 日期格式 + * @return 日期字符串 + */ + public static String DateToString(Date date, String parttern) { + String dateString = null; + if (date != null) { + try { + dateString = getDateFormat(parttern).format(date); + } catch (Exception e) { + } + } + return dateString; + } + + /** + * 将日期转化为日期字符串。失败返回null。 + * + * @param date 日期 + * @param dateStyle 日期风格 + * @return 日期字符串 + */ + public static String DateToString(Date date, DateStyle dateStyle) { + String dateString = null; + if (dateStyle != null) { + dateString = DateToString(date, dateStyle.getValue()); + } + return dateString; + } + + /** + * 将日期字符串转化为另一日期字符串。失败返回null。 + * + * @param date 旧日期字符串 + * @param parttern 新日期格式 + * @return 新日期字符串 + */ + public static String StringToString(String date, String parttern) { + return StringToString(date, null, parttern); + } + + /** + * 将日期字符串转化为另一日期字符串。失败返回null。 + * + * @param date 旧日期字符串 + * @param dateStyle 新日期风格 + * @return 新日期字符串 + */ + public static String StringToString(String date, DateStyle dateStyle) { + return StringToString(date, null, dateStyle); + } + + /** + * 将日期字符串转化为另一日期字符串。失败返回null。 + * + * @param date 旧日期字符串 + * @param olddParttern 旧日期格式 + * @param newParttern 新日期格式 + * @return 新日期字符串 + */ + public static String StringToString(String date, String olddParttern, String newParttern) { + String dateString = null; + if (olddParttern == null) { + DateStyle style = getDateStyle(date); + if (style != null) { + Date myDate = StringToDate(date, style.getValue()); + dateString = DateToString(myDate, newParttern); + } + } else { + Date myDate = StringToDate(date, olddParttern); + dateString = DateToString(myDate, newParttern); + } + return dateString; + } + + /** + * 将日期字符串转化为另一日期字符串。失败返回null。 + * + * @param date 旧日期字符串 + * @param olddDteStyle 旧日期风格 + * @param newDateStyle 新日期风格 + * @return 新日期字符串 + */ + public static String StringToString(String date, DateStyle olddDteStyle, DateStyle newDateStyle) { + String dateString = null; + if (olddDteStyle == null) { + DateStyle style = getDateStyle(date); + dateString = StringToString(date, style.getValue(), newDateStyle.getValue()); + } else { + dateString = StringToString(date, olddDteStyle.getValue(), newDateStyle.getValue()); + } + return dateString; + } + + /** + * 增加日期的年份。失败返回null。 + * + * @param date 日期 + * @param yearAmount 增加数量。可为负数 + * @return 增加年份后的日期字符串 + */ + public static String addYear(String date, int yearAmount) { + return addInteger(date, Calendar.YEAR, yearAmount); + } + + /** + * 增加日期的年份。失败返回null。 + * + * @param date 日期 + * @param yearAmount 增加数量。可为负数 + * @return 增加年份后的日期 + */ + public static Date addYear(Date date, int yearAmount) { + return addInteger(date, Calendar.YEAR, yearAmount); + } + + /** + * 增加日期的月份。失败返回null。 + * + * @param date 日期 + * @param yearAmount 增加数量。可为负数 + * @return 增加月份后的日期字符串 + */ + public static String addMonth(String date, int yearAmount) { + return addInteger(date, Calendar.MONTH, yearAmount); + } + + /** + * 增加日期的月份。失败返回null。 + * + * @param date 日期 + * @param yearAmount 增加数量。可为负数 + * @return 增加月份后的日期 + */ + public static Date addMonth(Date date, int yearAmount) { + return addInteger(date, Calendar.MONTH, yearAmount); + } + + /** + * 增加日期的天数。失败返回null。 + * + * @param date 日期字符串 + * @param dayAmount 增加数量。可为负数 + * @return 增加天数后的日期字符串 + */ + public static String addDay(String date, int dayAmount) { + return addInteger(date, Calendar.DATE, dayAmount); + } + + /** + * 增加日期的天数。失败返回null。 + * + * @param date 日期 + * @param dayAmount 增加数量。可为负数 + * @return 增加天数后的日期 + */ + public static Date addDay(Date date, int dayAmount) { + return addInteger(date, Calendar.DATE, dayAmount); + } + + /** + * 增加日期的小时。失败返回null。 + * + * @param date 日期字符串 + * @param hourAmount 增加数量。可为负数 + * @return 增加小时后的日期字符串 + */ + public static String addHour(String date, int hourAmount) { + return addInteger(date, Calendar.HOUR_OF_DAY, hourAmount); + } + + /** + * 增加日期的小时。失败返回null。 + * + * @param date 日期 + * @param hourAmount 增加数量。可为负数 + * @return 增加小时后的日期 + */ + public static Date addHour(Date date, int hourAmount) { + return addInteger(date, Calendar.HOUR_OF_DAY, hourAmount); + } + + /** + * 增加日期的分钟。失败返回null。 + * + * @param date 日期字符串 + * @param hourAmount 增加数量。可为负数 + * @return 增加分钟后的日期字符串 + */ + public static String addMinute(String date, int hourAmount) { + return addInteger(date, Calendar.MINUTE, hourAmount); + } + + /** + * 增加日期的分钟。失败返回null。 + * + * @param date 日期 + * @param hourAmount 增加数量。可为负数 + * @return 增加分钟后的日期 + */ + public static Date addMinute(Date date, int hourAmount) { + return addInteger(date, Calendar.MINUTE, hourAmount); + } + + /** + * 增加日期的秒钟。失败返回null。 + * + * @param date 日期字符串 + * @param hourAmount 增加数量。可为负数 + * @return 增加秒钟后的日期字符串 + */ + public static String addSecond(String date, int hourAmount) { + return addInteger(date, Calendar.SECOND, hourAmount); + } + + /** + * 增加日期的秒钟。失败返回null。 + * + * @param date 日期 + * @param hourAmount 增加数量。可为负数 + * @return 增加秒钟后的日期 + */ + public static Date addSecond(Date date, int hourAmount) { + return addInteger(date, Calendar.SECOND, hourAmount); + } + + /** + * 获取日期的年份。失败返回0。 + * + * @param date 日期字符串 + * @return 年份 + */ + public static int getYear(String date) { + return getYear(StringToDate(date)); + } + + /** + * 获取日期的年份。失败返回0。 + * + * @param date 日期 + * @return 年份 + */ + public static int getYear(Date date) { + return getInteger(date, Calendar.YEAR); + } + + /** + * 获取日期的月份。失败返回0。 + * + * @param date 日期字符串 + * @return 月份 + */ + public static int getMonth(String date) { + return getMonth(StringToDate(date)); + } + + /** + * 获取日期的月份。失败返回0。 + * + * @param date 日期 + * @return 月份 + */ + public static int getMonth(Date date) { + return getInteger(date, Calendar.MONTH); + } + + /** + * 获取日期的天数。失败返回0。 + * + * @param date 日期字符串 + * @return 天 + */ + public static int getDay(String date) { + return getDay(StringToDate(date)); + } + + /** + * 获取日期的天数。失败返回0。 + * + * @param date 日期 + * @return 天 + */ + public static int getDay(Date date) { + return getInteger(date, Calendar.DATE); + } + + /** + * 获取日期的小时。失败返回0。 + * + * @param date 日期字符串 + * @return 小时 + */ + public static int getHour(String date) { + return getHour(StringToDate(date)); + } + + /** + * 获取日期的小时。失败返回0。 + * + * @param date 日期 + * @return 小时 + */ + public static int getHour(Date date) { + return getInteger(date, Calendar.HOUR_OF_DAY); + } + + /** + * 获取日期的分钟。失败返回0。 + * + * @param date 日期字符串 + * @return 分钟 + */ + public static int getMinute(String date) { + return getMinute(StringToDate(date)); + } + + /** + * 获取日期的分钟。失败返回0。 + * + * @param date 日期 + * @return 分钟 + */ + public static int getMinute(Date date) { + return getInteger(date, Calendar.MINUTE); + } + + /** + * 获取日期的秒钟。失败返回0。 + * + * @param date 日期字符串 + * @return 秒钟 + */ + public static int getSecond(String date) { + return getSecond(StringToDate(date)); + } + + /** + * 获取日期的秒钟。失败返回0。 + * + * @param date 日期 + * @return 秒钟 + */ + public static int getSecond(Date date) { + return getInteger(date, Calendar.SECOND); + } + + /** + * 获取日期 。默认yyyy-MM-dd格式。失败返回null。 + * + * @param date 日期字符串 + * @return 日期 + */ + public static String getDate(String date) { + return StringToString(date, DateStyle.YYYY_MM_DD); + } + + /** + * 获取日期。默认yyyy-MM-dd格式。失败返回null。 + * + * @param date 日期 + * @return 日期 + */ + public static String getDate(Date date) { + return DateToString(date, DateStyle.YYYY_MM_DD); + } + + /** + * 获取日期的时间。默认HH:mm:ss格式。失败返回null。 + * + * @param date 日期字符串 + * @return 时间 + */ + public static String getTime(String date) { + return StringToString(date, DateStyle.HH_MM_SS); + } + + /** + * 获取日期的时间。默认HH:mm:ss格式。失败返回null。 + * + * @param date 日期 + * @return 时间 + */ + public static String getTime(Date date) { + return DateToString(date, DateStyle.HH_MM_SS); + } + + /** + * 获取日期的星期。失败返回null。 + * + * @param date 日期字符串 + * @return 星期 + */ + public static Week getWeek(String date) { + Week week = null; + DateStyle dateStyle = getDateStyle(date); + if (dateStyle != null) { + Date myDate = StringToDate(date, dateStyle); + week = getWeek(myDate); + } + return week; + } + + /** + * 获取日期的星期。失败返回null。 + * + * @param date 日期 + * @return 星期 + */ + public static Week getWeek(Date date) { + Week week = null; + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int weekNumber = calendar.get(Calendar.DAY_OF_WEEK) - 1; + switch (weekNumber) { + case 0: + week = Week.SUNDAY; + break; + case 1: + week = Week.MONDAY; + break; + case 2: + week = Week.TUESDAY; + break; + case 3: + week = Week.WEDNESDAY; + break; + case 4: + week = Week.THURSDAY; + break; + case 5: + week = Week.FRIDAY; + break; + case 6: + week = Week.SATURDAY; + break; + default: + break; + } + return week; + } + + /** + * 获取两个日期相差的天数 + * + * @param date 日期字符串 + * @param otherDate 另一个日期字符串 + * @return 相差天数 + */ + public static int getIntervalDays(String date, String otherDate) { + return getIntervalDays(StringToDate(date), StringToDate(otherDate)); + } + + /** + * @param date 日期 + * @param otherDate 另一个日期 + * @return 相差天数 + */ + public static int getIntervalDays(Date date, Date otherDate) { + date = DateUtil.StringToDate(DateUtil.getDate(date)); + long time = Math.abs(date.getTime() - otherDate.getTime()); + return (int) time / (24 * 60 * 60 * 1000); + } + + /** + * 时间戳转换成日期格式字符串 + * + * @param seconds 精确到毫秒的字符串 + * @return + */ + public static String StampToDate(String seconds, DateStyle format) { + if (seconds == null || seconds.isEmpty() || "null".equals(seconds)) { + return ""; + } + SimpleDateFormat sdf = new SimpleDateFormat(format.getValue()); + return sdf.format(new Date(Long.valueOf(seconds))); + } + + /** + * 日期格式字符串转换成时间戳 + * + * @param date_str 字符串日期 + * @param format 如:yyyy-MM-dd HH:mm:ss + * @return + */ + public static String DateToTimeStamp(String date_str, DateStyle format) { + try { + SimpleDateFormat sdf = new SimpleDateFormat(format.getValue()); + return String.valueOf(sdf.parse(date_str).getTime() / 1000); + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + @NotNull + public static Date getDateFromNow(int timeType, int timenum) { + Calendar cld = Calendar.getInstance(); + cld.set(timeType, cld.get(timeType) + timenum); + return cld.getTime(); + } + + /** + * 比较两个日期的大小,日期格式为yyyy-MM-dd + * + * @param endTime the first date + * @param startTime the second date + * @return true
false + */ + public static boolean isDate2Bigger(Date endTime, Date startTime) { + return isDate2Bigger(endTime, startTime, true); + } + + public static boolean isDate2Bigger(Date endTime, Date startTime, boolean equals) { + boolean isBigger; + if (endTime.getTime() > startTime.getTime()) { + isBigger = false; + } else if (endTime.getTime() < startTime.getTime()) { + isBigger = true; + } else { + isBigger = equals; + } + return isBigger; + } + + public static Date getNowDate() { + return new Date(); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/time/Week.java b/utill/src/main/java/com/lennon/cn/utill/utill/time/Week.java new file mode 100644 index 0000000..c861623 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/time/Week.java @@ -0,0 +1,43 @@ +package com.lennon.cn.utill.utill.time; + +/** + * Created by lennon on 2017/8/29. + */ + +public enum Week { + MONDAY("星期一", "Monday", "Mon.", 1), + TUESDAY("星期二", "Tuesday", "Tues.", 2), + WEDNESDAY("星期三", "Wednesday", "Wed.", 3), + THURSDAY("星期四", "Thursday", "Thur.", 4), + FRIDAY("星期五", "Friday", "Fri.", 5), + SATURDAY("星期六", "Saturday", "Sat.", 6), + SUNDAY("星期日", "Sunday", "Sun.", 7); + + String name_cn; + String name_en; + String name_enShort; + int number; + + Week(String name_cn, String name_en, String name_enShort, int number) { + this.name_cn = name_cn; + this.name_en = name_en; + this.name_enShort = name_enShort; + this.number = number; + } + + public String getChineseName() { + return name_cn; + } + + public String getName() { + return name_en; + } + + public String getShortName() { + return name_enShort; + } + + public int getNumber() { + return number; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/version/DownLoad.java b/utill/src/main/java/com/lennon/cn/utill/version/DownLoad.java new file mode 100644 index 0000000..4c304b0 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/version/DownLoad.java @@ -0,0 +1,109 @@ +package com.lennon.cn.utill.version; + +import android.app.Activity; +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +import androidx.annotation.Nullable; +import cn.droidlover.xdroidmvp.log.XLog; +import com.lennon.cn.utill.base.BaseApplication; +import com.lennon.cn.utill.bean.Download; +import com.lennon.cn.utill.utill.StringUtils; +import com.lennon.cn.utill.utill.Utill; +import com.lennon.cn.utill.version.download.DownloadProgressListener; +import io.reactivex.functions.Consumer; + +import java.io.File; + + +/** + * Created by lennon on 2017/5/25. + */ + +public class DownLoad extends Service { + private String url; + private DownLoadListener listener; + int downloadCount = 0; + private File outputFile; + private boolean isReDown; + private Activity activity; + + public DownLoad(Activity activity, String url, boolean isReDown, DownLoadListener listener) { + this.url = url; + this.listener = listener; + this.isReDown = isReDown; + this.activity = activity; + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + DownloadProgressListener listener = new DownloadProgressListener() { + @Override + public void update(long bytesRead, long contentLength, boolean done) { + int progress = (int) ((bytesRead * 100) / contentLength); + if ((downloadCount == 0) || progress > downloadCount) { + final Download download = new Download(); + download.setTotalFileSize(contentLength); + download.setCurrentFileSize(bytesRead); + download.setProgress(progress); + downloadCount = progress; + if (DownLoad.this.listener != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + DownLoad.this.listener.update(download); + } + }); + } + } + + } + }; + outputFile = new File(BaseApplication.Companion.getDataFile(), Utill.INSTANCE.getFileName(url)); + + if (outputFile.exists()) { + if (isReDown) { + outputFile.delete(); + } else { + this.listener.downloadCompleted(outputFile); + return null; + } + } + + + String baseUrl = StringUtils.getHostName(url); + + new DownloadAPI(baseUrl, listener).downloadAPK(url, outputFile, new Consumer() { + + @Override + public void accept(Object o) throws Exception { + if (DownLoad.this.listener != null) { + DownLoad.this.listener.downloadCompleted(outputFile); + } + } + },new Consumer(){ + + @Override + public void accept(Throwable e) throws Exception { + e.printStackTrace(); + XLog.e("onError: " + e.getMessage()); + outputFile.delete(); + if (DownLoad.this.listener != null) { + DownLoad.this.listener.downloadError(outputFile); + } + XLog.e("onError: " + e.getMessage()); + } + }); + return null; + } + + public interface DownLoadListener { + void update(Download download); + + void downloadCompleted(File outputFile); + + void downloadError(File outputFile); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/version/DownloadAPI.java b/utill/src/main/java/com/lennon/cn/utill/version/DownloadAPI.java new file mode 100644 index 0000000..01ad688 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/version/DownloadAPI.java @@ -0,0 +1,76 @@ +package com.lennon.cn.utill.version; + +import androidx.annotation.NonNull; +import cn.droidlover.xdroidmvp.log.XLog; +import com.lennon.cn.utill.utill.FileUtil; +import com.lennon.cn.utill.version.download.DownloadProgressInterceptor; +import com.lennon.cn.utill.version.download.DownloadProgressListener; +import com.lennon.cn.utill.version.exception.CustomizeException; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.functions.Consumer; +import io.reactivex.functions.Function; +import io.reactivex.schedulers.Schedulers; +import okhttp3.OkHttpClient; +import okhttp3.ResponseBody; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.TimeUnit; + +/** + * Created by JokAr on 16/7/5. + */ +public class DownloadAPI { + private static final String TAG = "DownloadAPI"; + private static final int DEFAULT_TIMEOUT = 15; + public Retrofit retrofit; + + + public DownloadAPI(String url, DownloadProgressListener listener) { + DownloadProgressInterceptor interceptor = new DownloadProgressInterceptor(listener); + + OkHttpClient client = new OkHttpClient.Builder() + .addInterceptor(interceptor) + .retryOnConnectionFailure(true) + .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) + .build(); + + + retrofit = new Retrofit.Builder() + .baseUrl(url) + .client(client) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .build(); + } + + public void downloadAPK(@NonNull String url, final File file, Consumer consumer, Consumer e) { + XLog.d(TAG+" downloadAPK: " + url); + retrofit.create(DownloadService.class) + .download(url) + .subscribeOn(Schedulers.io()) + .unsubscribeOn(Schedulers.io()) + .map(new Function() { + @Override + public InputStream apply(ResponseBody responseBody) throws Exception { + return responseBody.byteStream(); + } + }) + .observeOn(Schedulers.io()) + .doOnNext(new Consumer() { + @Override + public void accept(InputStream inputStream) throws Exception { + try { + FileUtil.writeFile(inputStream, file); + } catch (IOException e) { + e.printStackTrace(); + throw new CustomizeException(e.getMessage(), e); + } + } + }) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(consumer,e); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/version/DownloadService.java b/utill/src/main/java/com/lennon/cn/utill/version/DownloadService.java new file mode 100644 index 0000000..f73b3aa --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/version/DownloadService.java @@ -0,0 +1,18 @@ +package com.lennon.cn.utill.version; + +import io.reactivex.Observable; +import okhttp3.ResponseBody; +import retrofit2.http.GET; +import retrofit2.http.Streaming; +import retrofit2.http.Url; + +/** + * Created by JokAr on 16/7/5. + */ +public interface DownloadService { + + + @Streaming + @GET + Observable download(@Url String url); +} diff --git a/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressInterceptor.java b/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressInterceptor.java new file mode 100644 index 0000000..a62cedf --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressInterceptor.java @@ -0,0 +1,28 @@ +package com.lennon.cn.utill.version.download; + +import okhttp3.Interceptor; +import okhttp3.Response; + +import java.io.IOException; + +/** + * Interceptor for download + * Created by JokAr on 16/5/11. + */ +public class DownloadProgressInterceptor implements Interceptor { + + private DownloadProgressListener listener; + + public DownloadProgressInterceptor(DownloadProgressListener listener) { + this.listener = listener; + } + + @Override + public Response intercept(Chain chain) throws IOException { + Response originalResponse = chain.proceed(chain.request()); + + return originalResponse.newBuilder() + .body(new DownloadProgressResponseBody(originalResponse.body(), listener)) + .build(); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressListener.java b/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressListener.java new file mode 100644 index 0000000..954ffce --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressListener.java @@ -0,0 +1,9 @@ +package com.lennon.cn.utill.version.download; + +/** + * 下载进度listener + * Created by JokAr on 16/5/11. + */ +public interface DownloadProgressListener { + void update(long bytesRead, long contentLength, boolean done); +} diff --git a/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressResponseBody.java b/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressResponseBody.java new file mode 100644 index 0000000..51e2538 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressResponseBody.java @@ -0,0 +1,61 @@ +package com.lennon.cn.utill.version.download; + +import okhttp3.MediaType; +import okhttp3.ResponseBody; +import okio.*; + +import java.io.IOException; + +/** + * ResponseBody for download + * Created by JokAr on 16/5/11. + */ +public class DownloadProgressResponseBody extends ResponseBody { + + private ResponseBody responseBody; + private DownloadProgressListener progressListener; + private BufferedSource bufferedSource; + + public DownloadProgressResponseBody(ResponseBody responseBody, + DownloadProgressListener progressListener) { + this.responseBody = responseBody; + this.progressListener = progressListener; + } + + @Override + public MediaType contentType() { + return responseBody.contentType(); + } + + @Override + public long contentLength() { + return responseBody.contentLength(); + } + + @Override + public BufferedSource source() { + if (bufferedSource == null) { + bufferedSource = Okio.buffer(source(responseBody.source())); + } + return bufferedSource; + } + + private Source source(Source source) { + return new ForwardingSource(source) { + long totalBytesRead = 0L; + + @Override + public long read(Buffer sink, long byteCount) throws IOException { + long bytesRead = super.read(sink, byteCount); + // read() returns the number of bytes read, or -1 if this source is exhausted. + totalBytesRead += bytesRead != -1 ? bytesRead : 0; + + if (null != progressListener) { + progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1); + } + return bytesRead; + } + }; + + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/version/exception/CustomizeException.java b/utill/src/main/java/com/lennon/cn/utill/version/exception/CustomizeException.java new file mode 100644 index 0000000..4be62f7 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/version/exception/CustomizeException.java @@ -0,0 +1,11 @@ +package com.lennon.cn.utill.version.exception; + +/** + * Created by JokAr on 16/7/5. + */ +public class CustomizeException extends RuntimeException { + + public CustomizeException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/AutoLineFeedLayoutManager.java b/utill/src/main/java/com/lennon/cn/utill/widget/AutoLineFeedLayoutManager.java new file mode 100644 index 0000000..ad39c49 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/AutoLineFeedLayoutManager.java @@ -0,0 +1,53 @@ +package com.lennon.cn.utill.widget; + +import android.view.View; +import androidx.recyclerview.widget.RecyclerView; + +/** + * @author 机械革命 + */ +public class AutoLineFeedLayoutManager extends RecyclerView.LayoutManager { + @Override + public RecyclerView.LayoutParams generateDefaultLayoutParams() { + return new RecyclerView.LayoutParams( + RecyclerView.LayoutParams.WRAP_CONTENT, + RecyclerView.LayoutParams.WRAP_CONTENT); + } + + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + detachAndScrapAttachedViews(recycler); + + int sumWidth = getWidth(); + + int curLineWidth = 0, curLineTop = 0; + int lastLineMaxHeight = 0; + for (int i = 0; i < getItemCount(); i++) { + View view = recycler.getViewForPosition(i); + + addView(view); + measureChildWithMargins(view, 0, 0); + int width = getDecoratedMeasuredWidth(view); + int height = getDecoratedMeasuredHeight(view); + + curLineWidth += width; + //不需要换行 + if (curLineWidth <= sumWidth) { + layoutDecorated(view, curLineWidth - width, curLineTop, curLineWidth, curLineTop + height); + //比较当前行多有item的最大高度 + lastLineMaxHeight = Math.max(lastLineMaxHeight, height); + } else {//换行 + curLineWidth = width; + if (lastLineMaxHeight == 0) { + lastLineMaxHeight = height; + } + //记录当前行top + curLineTop += lastLineMaxHeight; + + layoutDecorated(view, 0, curLineTop, width, curLineTop + height); + lastLineMaxHeight = height; + } + } + + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/ClearWithSpaceEditText.java b/utill/src/main/java/com/lennon/cn/utill/widget/ClearWithSpaceEditText.java new file mode 100644 index 0000000..6c26e6b --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/ClearWithSpaceEditText.java @@ -0,0 +1,234 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.text.Editable; +import android.text.InputFilter; +import android.text.InputType; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.method.DigitsKeyListener; +import android.util.AttributeSet; +import android.widget.EditText; +import android.widget.Toast; +import androidx.appcompat.widget.AppCompatEditText; +import lennon.com.utill.R; + +/** + * 项目名称:android-pay + * 类描述: + * 创建人:LeoPoldCrossing + * 创建时间:16/10/28 + */ + +public class ClearWithSpaceEditText extends AppCompatEditText { + + private int contentType; + public static final int TYPE_PHONE = 0; + public static final int TYPE_CARD = 1; + public static final int TYPE_IDCARD = 2; + private int maxLength = 100; + private int start, count,before; + private String digits; + + public ClearWithSpaceEditText(Context context) { + this(context, null); + } + + public ClearWithSpaceEditText(Context context, AttributeSet attrs) { + super(context, attrs); + parseAttributeSet(context, attrs); + } + + public ClearWithSpaceEditText(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + parseAttributeSet(context, attrs); + } + + private void parseAttributeSet(Context context, AttributeSet attrs) { + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.clear_edit_attrs, 0, 0); + contentType = ta.getInt(R.styleable.clear_edit_attrs_input_type, 0); + ta.recycle(); + initType(); + setSingleLine(); + addTextChangedListener(watcher); + } + + private void initType(){ + if (contentType == TYPE_PHONE) { + maxLength = 13; + digits = "0123456789 "; + setInputType(InputType.TYPE_CLASS_NUMBER); + } else if (contentType == TYPE_CARD) { + maxLength = 31; + digits = "0123456789 "; + setInputType(InputType.TYPE_CLASS_NUMBER); + } else if (contentType == TYPE_IDCARD) { + maxLength = 21; + digits = null; + setInputType(InputType.TYPE_CLASS_TEXT); + } + setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)}); + } + + @Override + public void setInputType(int type) { + if (contentType == TYPE_PHONE || contentType == TYPE_CARD) { + type = InputType.TYPE_CLASS_NUMBER; + }else if(contentType == TYPE_IDCARD){ + type = InputType.TYPE_CLASS_TEXT; + } + super.setInputType(type); + /* 非常重要:setKeyListener要在setInputType后面调用,否则无效。*/ + if(!TextUtils.isEmpty(digits)) { + setKeyListener(DigitsKeyListener.getInstance(digits)); + } + } + + public void setContentType(int contentType) { + this.contentType = contentType; + initType(); + } + + private TextWatcher watcher = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + ClearWithSpaceEditText.this.start = start; + ClearWithSpaceEditText.this.before = before; + ClearWithSpaceEditText.this.count = count; + } + + @Override + public void afterTextChanged(Editable s) { + if (s == null) { + return; + } + //判断是否是在中间输入,需要重新计算 + boolean isMiddle = (start + count) < (s.length()); + //在末尾输入时,是否需要加入空格 + boolean isNeedSpace = false; + if (!isMiddle && isSpace(s.length())) { + isNeedSpace = true; + } + if (isMiddle || isNeedSpace || count > 1) { + String newStr = s.toString(); + newStr = newStr.replace(" ", ""); + StringBuilder sb = new StringBuilder(); + int spaceCount = 0; + for (int i = 0; i < newStr.length(); i++) { + sb.append(newStr.substring(i, i+1)); + //如果当前输入的字符下一位为空格(i+1+1+spaceCount),因为i是从0开始计算的,所以一开始的时候需要先加1 + if(isSpace(i + 2 + spaceCount)){ + sb.append(" "); + spaceCount += 1; + } + } + removeTextChangedListener(watcher); + /* 该处原来是调用setText(sb)。 + * 但是如果调用该语句的话,在身份证输入框的情况下(允许字母和数字),当调用setText时,会导致输入法的跳转 + * 参照网上解决方法,将该句话替换成s.replace(...) + * 该种方法不会导致输入法的跳转。 + * 造成输入法跳转的原因可能是setText会重新唤起输入法控件*/ + s.replace(0, s.length(),sb); + //如果是在末尾的话,或者加入的字符个数大于零的话(输入或者粘贴) + if (!isMiddle || count > 1) { + setSelection(s.length() <= maxLength ? s.length() : maxLength); + } else if (isMiddle) { + //如果是删除 + if (count == 0) { + //如果删除时,光标停留在空格的前面,光标则要往前移一位 + if (isSpace(start - before + 1)) { + setSelection((start - before) > 0 ? start - before : 0); + } else { + setSelection((start - before + 1) > s.length() ? s.length() : (start - before + 1)); + } + } + //如果是增加 + else { + if (isSpace(start - before + count)) { + setSelection((start + count - before + 1) < s.length() ? (start + count - before + 1) : s.length()); + } else { + setSelection(start + count - before); + } + } + } + addTextChangedListener(watcher); + } + } + }; + + /** + * 获取无空格的字符串 + * @return + */ + public String getNoSpaceText(){ + String newStr = this.getText().toString(); + if (!TextUtils.isEmpty(newStr)){ + String strNOSpace = newStr.replace(" ", ""); + if(!TextUtils.isEmpty(strNOSpace)){ + strNOSpace = strNOSpace.replace(" ", ""); + } + return strNOSpace; + } + return ""; + } + + public boolean checkTextRight(){ + String text = getNoSpaceText(); + //这里做个简单的内容判断 + if (contentType == TYPE_PHONE) { + if (TextUtils.isEmpty(text)) { + Toast.makeText(getContext(), "手机号不能为空,请输入正确的手机号", Toast.LENGTH_SHORT); + } else if (text.length() < 11) { + Toast.makeText(getContext(), "手机号不足11位,请输入正确的手机号", Toast.LENGTH_SHORT); + } else { + return true; + } + } else if (contentType == TYPE_CARD) { + if (TextUtils.isEmpty(text)) { + Toast.makeText(getContext(), "银行卡号不能为空,请输入正确的银行卡号", Toast.LENGTH_SHORT); + } else if (text.length() < 14) { + Toast.makeText(getContext(), "银行卡号位数不正确,请输入正确的银行卡号", Toast.LENGTH_SHORT); + } else { + return true; + } + } else if (contentType == TYPE_IDCARD) { + if (TextUtils.isEmpty(text)) { + Toast.makeText(getContext(), "身份证号不能为空,请输入正确的身份证号", Toast.LENGTH_SHORT); + } else if (text.length() < 18) { + Toast.makeText(getContext(), "身份证号不正确,请输入正确的身份证号", Toast.LENGTH_SHORT); + } else { + return true; + } + } + return false; + } + + private boolean isSpace(int length) { + if (contentType == TYPE_PHONE) { + return isSpacePhone(length); + } else if (contentType == TYPE_CARD) { + return isSpaceCard(length); + } else if (contentType == TYPE_IDCARD) { + return isSpaceIDCard(length); + } + return false; + } + + private boolean isSpacePhone(int length) { + return length >= 4 && (length == 4 || (length + 1) % 5 == 0); + } + + private boolean isSpaceCard(int length) { + return length % 5 == 0; + } + + private boolean isSpaceIDCard(int length) { + return length > 6 && (length == 7 || (length - 2) % 5 == 0); + } + +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/ContainsEmojiEditText.java b/utill/src/main/java/com/lennon/cn/utill/widget/ContainsEmojiEditText.java new file mode 100644 index 0000000..00d22a0 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/ContainsEmojiEditText.java @@ -0,0 +1,155 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.text.*; +import android.util.AttributeSet; +import androidx.appcompat.widget.AppCompatEditText; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created by lennon on 2017/9/4. + */ + +public class ContainsEmojiEditText extends AppCompatEditText { + //输入表情前的光标位置 + private int cursorPos; + //输入表情前EditText中的文本 + private String inputAfterText; + //是否重置了EditText的内容 + private boolean resetText; + + private Context mContext; + + public ContainsEmojiEditText(Context context) { + super(context); + this.mContext = context; + initEditText(); + } + + public ContainsEmojiEditText(Context context, AttributeSet attrs) { + super(context, attrs); + this.mContext = context; + initEditText(); + } + + public ContainsEmojiEditText(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + this.mContext = context; + initEditText(); + } + + // 初始化edittext 控件 + private void initEditText() { + setEditTextInhibitInputSpeChat(); + addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int before, int count) { + if (!resetText) { + cursorPos = getSelectionEnd(); + // 这里用s.toString()而不直接用s是因为如果用s, + // 那么,inputAfterText和s在内存中指向的是同一个地址,s改变了, + // inputAfterText也就改变了,那么表情过滤就失败了 + inputAfterText = s.toString(); + } + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (!resetText) { + if (count >= 2) {//表情符号的字符长度最小为2 + CharSequence input = s.subSequence(start, start + count); + if (containsEmoji(input.toString())) { + resetText = true; +// Toast.makeText(mContext, "不支持输入Emoji表情符号", Toast.LENGTH_SHORT).show(); + //是表情符号就将文本还原为输入表情符号之前的内容 + setText(inputAfterText); + CharSequence text = getText(); + if (text instanceof Spannable) { + Spannable spanText = (Spannable) text; + Selection.setSelection(spanText, text.length()); + } + } + } + } else { + resetText = false; + } + } + + @Override + public void afterTextChanged(Editable editable) { + + } + }); + } + + + /** + * 检测是否有emoji表情 + * + * @param source + * @return + */ + public static boolean containsEmoji(String source) { + int len = source.length(); + for (int i = 0; i < len; i++) { + char codePoint = source.charAt(i); + if (!isEmojiCharacter(codePoint)) { //如果不能匹配,则该字符是Emoji表情 + return true; + } + } + return false; + } + + /** + * 判断是否是Emoji + * + * @param codePoint 比较的单个字符 + * @return + */ + private static boolean isEmojiCharacter(char codePoint) { + return (codePoint == 0x0) || (codePoint == 0x9) || (codePoint == 0xA) || + (codePoint == 0xD) || ((codePoint >= 0x20) && (codePoint <= 0xD7FF)) || + ((codePoint >= 0xE000) && (codePoint <= 0xFFFD)) || ((codePoint >= 0x10000) + && (codePoint <= 0x10FFFF)); + } + + public static boolean isEmoji(String string) { + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Pattern p = Pattern.compile("/[\u1F60-\u1F64]|[\u2702-\u27B0]|[\u1F68-\u1F6C]|[\u1F30-\u1F70]|[\u2600-\u26ff]/g"); + Matcher m = p.matcher(string); + return m.matches(); + } + + public boolean isEmoji2(String string) { + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Pattern p = Pattern.compile("[\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]", + Pattern.UNICODE_CASE | Pattern.CASE_INSENSITIVE); + Matcher m = p.matcher(string); + return m.find(); + } + + public void allowEditTextInputSpeChat() { + setFilters(new InputFilter[0]); + } + + /** + * 禁止EditText输入特殊字符 + */ + private void setEditTextInhibitInputSpeChat() { + InputFilter filter = new InputFilter() { + @Override + public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { + String speChat = "[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]"; + Pattern pattern = Pattern.compile(speChat); + Matcher matcher = pattern.matcher(source.toString()); + if (matcher.find()) { + return ""; + } else { + return null; + } + } + }; + setFilters(new InputFilter[]{filter}); + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/DrawableTextView.java b/utill/src/main/java/com/lennon/cn/utill/widget/DrawableTextView.java new file mode 100644 index 0000000..644a946 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/DrawableTextView.java @@ -0,0 +1,89 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import androidx.appcompat.widget.AppCompatTextView; +import lennon.com.utill.R; + +/** + * Created by dingyi on 2016/11/17. + */ + +public class DrawableTextView extends AppCompatTextView { + + public DrawableTextView(Context context) { + super(context); + + } + + public DrawableTextView(Context context, AttributeSet attrs) { + super(context, attrs); + /** + * 取得自定义属性值 + */ + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.DrawableTextView); + int drawableWidth = ta.getDimensionPixelSize(R.styleable.DrawableTextView_drawableWidth, -1); + int drawableHeight = ta.getDimensionPixelSize(R.styleable.DrawableTextView_drawableHeight, -1); + /** + * 取得TextView的Drawable(左上右下四个组成的数组值) + */ + Drawable[] drawables = getCompoundDrawables(); + Drawable textDrawable = null; + for (Drawable drawable : drawables) { + if (drawable != null) { + textDrawable = drawable; + } + } + /** + * 设置宽高 + */ + if (textDrawable != null && drawableWidth != -1 && drawableHeight != -1) { + textDrawable.setBounds(0, 0, drawableWidth, drawableHeight); + } + /** + * 设置给TextView + */ + setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawables[3]); + /** + * 回收ta + */ + ta.recycle(); + } + + public DrawableTextView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + /** + * 取得自定义属性值 + */ + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.DrawableTextView); + int drawableWidth = ta.getDimensionPixelSize(R.styleable.DrawableTextView_drawableWidth, -1); + int drawableHeight = ta.getDimensionPixelSize(R.styleable.DrawableTextView_drawableHeight, -1); + /** + * 取得TextView的Drawable(左上右下四个组成的数组值) + */ + Drawable[] drawables = getCompoundDrawables(); + Drawable textDrawable = null; + for (Drawable drawable : drawables) { + if (drawable != null) { + textDrawable = drawable; + } + } + /** + * 设置宽高 + */ + if (textDrawable != null && drawableWidth != -1 && drawableHeight != -1) { + textDrawable.setBounds(0, 0, drawableWidth, drawableHeight); + } + /** + * 设置给TextView + */ + setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawables[3]); + /** + * 回收ta + */ + ta.recycle(); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/FlowLayoutManager.java b/utill/src/main/java/com/lennon/cn/utill/widget/FlowLayoutManager.java new file mode 100644 index 0000000..3f28321 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/FlowLayoutManager.java @@ -0,0 +1,157 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.graphics.Rect; +import android.util.SparseArray; +import android.view.View; +import androidx.recyclerview.widget.RecyclerView; +import cn.droidlover.xdroidmvp.log.XLog; + +public class FlowLayoutManager extends RecyclerView.LayoutManager { + private final String TAG = this.getClass().getName(); + private SparseArray cachedViews = new SparseArray(); + private SparseArray layoutPoints = new SparseArray<>(); + private int totalWidth; + private int totalHeight; + private int mContentHeight; + private int mOffset; + private boolean mIsFullyLayout; + + public FlowLayoutManager(Context context, boolean isFullyLayout) { + mIsFullyLayout = isFullyLayout; + } + + @Override + public boolean supportsPredictiveItemAnimations() { + return true; + } + + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + for (int i = 0; i < getItemCount(); ++i) { + View v = cachedViews.get(i); + Rect rect = layoutPoints.get(i); + layoutDecorated(v, rect.left, rect.top, rect.right, rect.bottom); + } + + } + + @Override + public RecyclerView.LayoutParams generateDefaultLayoutParams() { + return new RecyclerView.LayoutParams(RecyclerView.LayoutParams.WRAP_CONTENT, RecyclerView.LayoutParams.WRAP_CONTENT); + } + + @Override + public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { + return dx; + } + + @Override + public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { + int shouldOffset = 0; + if (mContentHeight - totalHeight > 0) { + int targetOffset = mOffset + dy; + if (targetOffset < 0) { + targetOffset = 0; + } else if (targetOffset > (mContentHeight - totalHeight)) { + targetOffset = (mContentHeight - totalHeight); + } + shouldOffset = targetOffset - mOffset; + offsetChildrenVertical(-shouldOffset); + mOffset = targetOffset; + } + + if (mIsFullyLayout) { + shouldOffset = dy; + } + return shouldOffset; + } + + @Override + public boolean canScrollHorizontally() { + return true; + } + + @Override + public boolean canScrollVertically() { + return true; + } + + @Override + public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) { + removeAllViews(); + } + + @Override + public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { + super.onMeasure(recycler, state, widthSpec, heightSpec); + final int widthMode = View.MeasureSpec.getMode(widthSpec); + final int heightMode = View.MeasureSpec.getMode(heightSpec); + final int widthSize = View.MeasureSpec.getSize(widthSpec); + final int heightSize = View.MeasureSpec.getSize(heightSpec); + int height; + switch (widthMode) { + case View.MeasureSpec.UNSPECIFIED: + XLog.d(TAG, "WidthMode is unspecified."); + break; + case View.MeasureSpec.AT_MOST: + break; + case View.MeasureSpec.EXACTLY: + break; + default: + break; + } + removeAndRecycleAllViews(recycler); + recycler.clear(); + cachedViews.clear(); + mContentHeight = 0; + totalWidth = widthSize - getPaddingRight() - getPaddingLeft(); + int left = getPaddingLeft(); + int top = getPaddingTop(); + int maxTop = top; + for (int i = 0; i < getItemCount(); ++i) { + View v = recycler.getViewForPosition(i); + addView(v); + measureChildWithMargins(v, 0, 0); + cachedViews.put(i, v); + } + for (int i = 0; i < getItemCount(); ++i) { + View v = cachedViews.get(i); + int w = getDecoratedMeasuredWidth(v); + int h = getDecoratedMeasuredHeight(v); + if (w > totalWidth - left) { + left = getPaddingLeft(); + top = maxTop; + } + Rect rect = new Rect(left, top, left + w, top + h); + layoutPoints.put(i, rect); + left = left + w; + if (top + h >= maxTop) { + maxTop = top + h; + } + } + + mContentHeight = maxTop - getPaddingTop(); + + height = mContentHeight + getPaddingTop() + getPaddingBottom(); + + switch (heightMode) { + case View.MeasureSpec.EXACTLY: + height = heightSize; + break; + case View.MeasureSpec.AT_MOST: + if (height > heightSize) { + height = heightSize; + } + break; + case View.MeasureSpec.UNSPECIFIED: + break; + default: + break; + } + + totalHeight = height - getPaddingTop() - getPaddingBottom(); + + setMeasuredDimension(widthSize, height); + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/HeadBar.java b/utill/src/main/java/com/lennon/cn/utill/widget/HeadBar.java new file mode 100644 index 0000000..8b77e7c --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/HeadBar.java @@ -0,0 +1,175 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import com.lennon.cn.utill.widget.interf.OnHeadBarListener; +import lennon.com.utill.R; + +/** + * Created by dingyi on 2016/11/15. + */ + +public class HeadBar extends LinearLayout { + + TextView mLeftTv; + TextView mMidTv; + TextView mRightTv; + ImageView mRightIgv; + + + private Context mContext; + private OnHeadBarListener mOnHeadBarListener; + + public HeadBar(Context context) { + super(context); + } + + public HeadBar(Context context, AttributeSet attrs) { + super(context, attrs); + this.mContext = context; + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.HeadBar); + LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.headbar, this); + inintView(view); + setDefault(typedArray); + } + + public HeadBar(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + public void setStatusBar(int bar) { + ViewGroup.LayoutParams params = getLayoutParams(); + params.height += bar; + setLayoutParams(params); + setPadding(0, bar, 0, 0); + } + + /** + * 设置监听器 + */ + public void setOnHeadBarListener(OnHeadBarListener listener) { + this.mOnHeadBarListener = listener; + } + + public void setTextColor(int color) { + mLeftTv.setTextColor(color); + mRightTv.setTextColor(color); + mMidTv.setTextColor(color); + } + + /** + * 设置最左边显示栏位提示信息 + */ + public void setLeftMsg(String msg) { + setLeftMsg(R.mipmap.back, msg); + } + + public void setLeftMsg(int id, String msg) { + mLeftTv.setText(msg); + if (id == -1) { + return; + } + Drawable drawable = getResources().getDrawable(id); + drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight()); + mLeftTv.setCompoundDrawables(drawable, null, null, null); + } + + /** + * 设置HeadBar标题 + */ + public void setMidMsg(String msg) { + mMidTv.setVisibility(View.VISIBLE); + mMidTv.setText(msg); + } + + /** + * 设置最右边文字提示信息 + */ + public void setRightMsg(String msg) { + mRightTv.setVisibility(View.VISIBLE); + mRightTv.setText(msg); + mRightTv.setDrawingCacheEnabled(false); + mRightIgv.setVisibility(View.GONE); + } + + public void setRightMsg(int drawableId, String msg) { + mRightTv.setVisibility(View.VISIBLE); + mRightTv.setText(msg); + mRightTv.setDrawingCacheEnabled(true); + mRightIgv.setVisibility(VISIBLE); + mRightIgv.setImageDrawable(getResources().getDrawable(drawableId)); + } + + public void setRightIgv(int drawableId) { + mRightIgv.setVisibility(VISIBLE); + mRightIgv.setImageDrawable(getResources().getDrawable(drawableId)); + } + + private void setDefault(TypedArray typedArray) { + mLeftTv.setText(""); + mLeftTv.setDrawingCacheEnabled(false); + mMidTv.setText(""); + setRightMsg(""); + if (typedArray != null) { + mLeftTv.setTextColor(typedArray.getColor(R.styleable.HeadBar_text_color, Color.WHITE)); + mRightTv.setTextColor(typedArray.getColor(R.styleable.HeadBar_text_color, Color.WHITE)); + mMidTv.setTextColor(typedArray.getColor(R.styleable.HeadBar_text_color, Color.WHITE)); + } + mRightTv.setVisibility(GONE); + mRightIgv.setVisibility(GONE); + } + + private void inintView(View v) { + mLeftTv = (TextView) v.findViewById(R.id.headbar_left_tv); + mLeftTv.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (null != mOnHeadBarListener) { + mOnHeadBarListener.onLeft(); + } + } + }); + mMidTv = (TextView) v.findViewById(R.id.headbar_mid_tv); + mMidTv.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (null != mOnHeadBarListener) { + mOnHeadBarListener.onMid(); + } + } + }); + mRightTv = (TextView) v.findViewById(R.id.headbar_right_tv); + mRightTv.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (null != mOnHeadBarListener) { + mOnHeadBarListener.onRight(); + } + } + }); + mRightIgv = (ImageView) v.findViewById(R.id.headbar_right_igv); + mRightIgv.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (null != mOnHeadBarListener) { + mOnHeadBarListener.onRight(); + } + } + }); + } + + public void onRight() { + if (null != mOnHeadBarListener) { + mOnHeadBarListener.onRight(); + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/MyRatingBar.java b/utill/src/main/java/com/lennon/cn/utill/widget/MyRatingBar.java new file mode 100644 index 0000000..f7cc6f7 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/MyRatingBar.java @@ -0,0 +1,244 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import lennon.com.utill.R; + + +/** + * Created by fulianchen on 24/7/13. + */ +public class MyRatingBar extends View { + //星星水平排列 + public static final int HORIZONTAL = 0; + //星星垂直排列 + public static final int VERTICAL = 1; + //实心图片 + private Bitmap mSolidBitmap; + //空心图片 + private Bitmap mHollowBitmap; + private Context context; + //最大的数量 + private int starMaxNumber; + private float starRating; + private Paint paint; + private int mSpaceWidth;//星星间隔 + private int mStarWidth;//星星宽度 + private int mStarHeight;//星星高度 + private boolean isIndicator;//是否是一个指示器(用户无法进行更改) + private int mOrientation; + + public MyRatingBar(Context context, AttributeSet attrs) { + super(context, attrs); + paint = new Paint(); + this.context = context; + TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.MyRatingBar); + mSpaceWidth = a.getDimensionPixelSize(R.styleable.MyRatingBar_space_width, 0); + mStarWidth = a.getDimensionPixelSize(R.styleable.MyRatingBar_star_width, 0); + mStarHeight = a.getDimensionPixelSize(R.styleable.MyRatingBar_star_height, 0); + starMaxNumber = a.getInt(R.styleable.MyRatingBar_star_max, 0); + starRating = a.getFloat(R.styleable.MyRatingBar_star_rating, 0); + isIndicator = a.getBoolean(R.styleable.MyRatingBar_star_isIndicator, true); + mSolidBitmap = getZoomBitmap(BitmapFactory.decodeResource(context.getResources(), a.getResourceId(R.styleable.MyRatingBar_star_solid, 0))); + mHollowBitmap = getZoomBitmap(BitmapFactory.decodeResource(context.getResources(),a.getResourceId(R.styleable.MyRatingBar_star_hollow, 0))); + mOrientation = a.getInt(R.styleable.MyRatingBar_star_orientation, HORIZONTAL); + a.recycle(); + } + + public MyRatingBar(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + paint = new Paint(); + this.context = context; + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyRatingBar, defStyle, 0); + mSpaceWidth = a.getDimensionPixelSize(R.styleable.MyRatingBar_space_width, 0); + mStarWidth = a.getDimensionPixelSize(R.styleable.MyRatingBar_star_width, 0); + mStarHeight = a.getDimensionPixelSize(R.styleable.MyRatingBar_star_height, 0); + starMaxNumber = a.getInt(R.styleable.MyRatingBar_star_max, 0); + starRating = a.getFloat(R.styleable.MyRatingBar_star_rating, 0); + mSolidBitmap = getZoomBitmap(BitmapFactory.decodeResource(context.getResources(), a.getResourceId(R.styleable.MyRatingBar_star_solid, 0))); + mHollowBitmap = getZoomBitmap(BitmapFactory.decodeResource(context.getResources(),a.getResourceId(R.styleable.MyRatingBar_star_hollow, 0))); + mOrientation = a.getInt(R.styleable.MyRatingBar_star_orientation, HORIZONTAL); + a.recycle(); + } + + @Override + protected void onDraw(Canvas canvas) { + if(mHollowBitmap ==null|| mSolidBitmap ==null){ + return; + } + //绘制实心进度 + int solidStarNum = (int)starRating; + //绘制实心的起点位置 + int solidStartPoint = 0; + for(int i=1;i<=solidStarNum;i++){ + canvas.drawBitmap(mSolidBitmap, solidStartPoint, 0, paint); + solidStartPoint = solidStartPoint + mSpaceWidth + mHollowBitmap.getWidth(); + } + //虚心开始位置 + int hollowStartPoint = solidStartPoint; + //多出的实心部分起点 + int extraSolidStarPoint = hollowStartPoint; + //虚心数量 + int hollowStarNum = starMaxNumber - solidStarNum; + for(int j = 1;j<=hollowStarNum;j++){ + canvas.drawBitmap(mHollowBitmap, hollowStartPoint, 0, paint); + hollowStartPoint = hollowStartPoint + mSpaceWidth + mSolidBitmap.getWidth(); + } + //多出的实心长度 + int extraSolidLength = (int)((starRating - solidStarNum)* mHollowBitmap.getWidth()); + Rect rectSrc = new Rect(0,0,extraSolidLength, mHollowBitmap.getHeight()); + Rect dstF = new Rect(extraSolidStarPoint,0,extraSolidStarPoint+extraSolidLength, mHollowBitmap.getHeight()); + canvas.drawBitmap(mSolidBitmap, rectSrc, dstF, paint); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if(!isIndicator){ + switch (event.getAction()){ + case MotionEvent.ACTION_DOWN: + float starTotalWidth = starMaxNumber *(mStarWidth+mSpaceWidth)-mSpaceWidth; + if(event.getX()<=starTotalWidth){ + float newStarRating = (int)event.getX()/(mStarWidth+mSpaceWidth)+1; + setStarRating(newStarRating); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + + break; + } + } + return super.onTouchEvent(event); + } + + /** + * 设置星星的进度 + * @param starRating + */ + public void setStarRating(float starRating){ + this.starRating = starRating; + invalidate(); + } + + public float getStarRating() { + return starRating; + } + + + /** + * 获取缩放图片 + * @param bitmap + * @return + */ + public Bitmap getZoomBitmap(Bitmap bitmap){ + if(mStarWidth==0||mStarHeight==0){ + return bitmap; + } + // 获得图片的宽高 + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + // 设置想要的大小 + int newWidth = mStarWidth; + int newHeight = mStarHeight; + // 计算缩放比例 + float scaleWidth = ((float) newWidth) / width; + float scaleHeight = ((float) newHeight) / height; + // 取得想要缩放的matrix参数 + Matrix matrix = new Matrix(); + matrix.postScale(scaleWidth, scaleHeight); + // 得到新的图片 + Bitmap newbm = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, + true); + return newbm; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mOrientation==HORIZONTAL) { + setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec)); + } else { + setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec)); + } + } + + /** + * Determines the width of this view + * + * @param measureSpec + * A measureSpec packed into an int + * @return The width of the view, honoring constraints from measureSpec + */ + private int measureLong(int measureSpec) { + int result; + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + + if ((specMode == MeasureSpec.EXACTLY)) { + //We were told how big to be + result = specSize; + } else { + //Calculate the width according the views count + result = (int)(getPaddingLeft() + getPaddingRight() + + (mSpaceWidth+mStarWidth)*(starMaxNumber)); + //Respect AT_MOST value if that was what is called for by measureSpec + if (specMode == MeasureSpec.AT_MOST) { + result = Math.min(result, specSize); + } + } + return result; + } + + /** + * Determines the height of this view + * + * @param measureSpec + * A measureSpec packed into an int + * @return The height of the view, honoring constraints from measureSpec + */ + private int measureShort(int measureSpec) { + int result; + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + + if (specMode == MeasureSpec.EXACTLY) { + //We were told how big to be + result = specSize; + } else { + //Measure the height + result = (int)(mStarHeight + getPaddingTop() + getPaddingBottom()); + //Respect AT_MOST value if that was what is called for by measureSpec + if (specMode == MeasureSpec.AT_MOST) { + result = Math.min(result, specSize); + } + } + return result; + } + + public int getStarMaxNumber() { + return starMaxNumber; + } + + public void setStarMaxNumber(int starMaxNumber) { + this.starMaxNumber = starMaxNumber; + invalidate(); + } + + public boolean isIndicator() { + return isIndicator; + } + + public void setIsIndicator(boolean isIndicator) { + this.isIndicator = isIndicator; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/SearchScanView.java b/utill/src/main/java/com/lennon/cn/utill/widget/SearchScanView.java new file mode 100644 index 0000000..a2dd481 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/SearchScanView.java @@ -0,0 +1,224 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.*; +import lennon.com.utill.R; + +/** + * Created by dingyi on 2016/11/22. + */ + +public class SearchScanView extends LinearLayout { + + EditText mInputEdt; + ImageView mDeleteIgv; + ListView mLv; + TextView search; + ImageView scanImg; + + private Context mContext; + private SearchViewListener mListener; + + /** + * 提示adapter (推荐adapter) + */ + private ArrayAdapter mHistoryAdapter; + + public SearchScanView(Context context) { + super(context); + } + + public SearchScanView(Context context, AttributeSet attrs) { + super(context, attrs); + this.mContext = context; + View view = LayoutInflater.from(context).inflate(R.layout.search_scan_view_layout, this); + inintView(view); + try { + TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SearchView); + mInputEdt.setHint(array.getString(R.styleable.SearchView_search_hint)); + if (array.getBoolean(R.styleable.SearchView_show_button, false)) { + search.setVisibility(View.VISIBLE); + } else { + search.setVisibility(View.GONE); + } + } catch (Exception e) { + e.printStackTrace(); + } + mInputEdt.addTextChangedListener(new EditChangedListener()); + mInputEdt.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_SEARCH) { + //关闭软键盘 + InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); + mListener.onSearchByButton(getText()); + return true; + } + return false; + } + }); + search.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); + mListener.onSearchByButton(getText()); + } + }); + mDeleteIgv.setVisibility(GONE); + mLv.setOnItemClickListener(mOnItemClickListener); + } + + public SearchScanView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public void setSearchViewListener(SearchViewListener listener) { + this.mListener = listener; + } + + public void setText(String text) { + mInputEdt.setText(text); + } + + public String getText() { + return mInputEdt.getText().toString(); + } + + private void inintView(View v) { + mInputEdt = (EditText) v.findViewById(R.id.searchview_input_edt); + mDeleteIgv = (ImageView) v.findViewById(R.id.search_delete_igv); + search = v.findViewById(R.id.search_search); + mDeleteIgv.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mInputEdt.setText(""); + mDeleteIgv.setVisibility(GONE); + } + }); + scanImg = v.findViewById(R.id.scan_img); + scanImg.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mListener != null) { + mListener.onScan(); + } + } + }); + mLv = (ListView) v.findViewById(R.id.searchview_lv); + } + + public void clearSearch() { + mInputEdt.setText(""); + mDeleteIgv.setVisibility(GONE); + } + + /** + * 通知监听者 进行搜索操作 + * + * @param text + */ + private void notifyStartSearching(String text) { + if (mListener != null) { + mListener.onResult(text); + } + //隐藏软键盘 + mLv.setVisibility(GONE); + InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); + } + + /** + * 设置热搜版提示 adapter + */ + public void setHistoryAdapter(ArrayAdapter adapter) { + this.mHistoryAdapter = adapter; + if (mLv.getAdapter() == null) { + mLv.setAdapter(mHistoryAdapter); + } + } + + private class EditChangedListener implements TextWatcher { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { + + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { + if (!"".equals(charSequence.toString())) { + mDeleteIgv.setVisibility(VISIBLE); + mLv.setVisibility(GONE); + //更新autoComplete数据 + if (mListener != null) { + mListener.onSearch(charSequence + ""); + } + } else { + mDeleteIgv.setVisibility(GONE); + if (mHistoryAdapter != null) { + mLv.setAdapter(mHistoryAdapter); + } + if (null != mListener) { + mListener.onClear(); + } + mLv.setVisibility(VISIBLE); + } + + } + + @Override + public void afterTextChanged(Editable editable) { + } + } + + private AdapterView.OnItemClickListener mOnItemClickListener = new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView adapterView, View view, int i, long l) { + if (i < mHistoryAdapter.getCount() - 1) { + String key = mHistoryAdapter.getItem(i).toString(); + notifyStartSearching(key); + } + } + }; + + /** + * search view回调方法 + */ + public interface SearchViewListener { + + /** + * 清除搜索内容 + */ + void onClear(); + + /** + * 更新自动补全内容 + * + * @param text + */ + void onSearch(String text); + + /** + * 搜索结果 + * + * @param text + */ + void onResult(String text); + + void onSearchByButton(String text); + + void onScan(); + + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/SearchView.java b/utill/src/main/java/com/lennon/cn/utill/widget/SearchView.java new file mode 100644 index 0000000..600f978 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/SearchView.java @@ -0,0 +1,217 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.*; + +import lennon.com.utill.R; + +/** + * Created by dingyi on 2016/11/22. + */ + +public class SearchView extends LinearLayout { + + EditText mInputEdt; + ImageView mDeleteIgv; + ListView mLv; + TextView search; + + private Context mContext; + private SearchViewListener mListener; + + /** + * 提示adapter (推荐adapter) + */ + private ArrayAdapter mHistoryAdapter; + + public SearchView(Context context) { + super(context); + } + + public SearchView(Context context, AttributeSet attrs) { + super(context, attrs); + this.mContext = context; + View view = LayoutInflater.from(context).inflate(R.layout.searchview_layout, this); + inintView(view); + try { + TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SearchView); + mInputEdt.setHint(array.getString(R.styleable.SearchView_search_hint)); + if (array.getBoolean(R.styleable.SearchView_show_button, false)) { + search.setVisibility(View.VISIBLE); + } else { + search.setVisibility(View.GONE); + } + } catch (Exception e) { + e.printStackTrace(); + } + mInputEdt.addTextChangedListener(new EditChangedListener()); + mInputEdt.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_SEARCH) { + //关闭软键盘 + InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); + if (mListener != null) { + mListener.onSearchByButton(getText()); + } + return true; + } + return false; + } + }); + search.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); + if (mListener != null) { + mListener.onSearchByButton(getText()); + } + } + }); + mDeleteIgv.setVisibility(GONE); + mLv.setOnItemClickListener(mOnItemClickListener); + } + + public SearchView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public void setSearchViewListener(SearchViewListener listener) { + this.mListener = listener; + } + + public void setText(String text) { + mInputEdt.setText(text); + } + + public String getText() { + return mInputEdt.getText().toString(); + } + + private void inintView(View v) { + mInputEdt = (EditText) v.findViewById(R.id.searchview_input_edt); + mDeleteIgv = (ImageView) v.findViewById(R.id.search_delete_igv); + search = v.findViewById(R.id.search_search); + mDeleteIgv.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mInputEdt.setText(""); + mDeleteIgv.setVisibility(GONE); + } + }); + mLv = (ListView) v.findViewById(R.id.searchview_lv); + } + + public void clearSearch() { + mInputEdt.setText(""); + mDeleteIgv.setVisibility(GONE); + } + + /** + * 通知监听者 进行搜索操作 + * + * @param text + */ + private void notifyStartSearching(String text) { + if (mListener != null) { + mListener.onResult(text); + } + //隐藏软键盘 + mLv.setVisibility(GONE); + InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); + } + + /** + * 设置热搜版提示 adapter + */ + public void setHistoryAdapter(ArrayAdapter adapter) { + this.mHistoryAdapter = adapter; + if (mLv.getAdapter() == null) { + mLv.setAdapter(mHistoryAdapter); + } + } + + private class EditChangedListener implements TextWatcher { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { + + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { + if (!"".equals(charSequence.toString())) { + mDeleteIgv.setVisibility(VISIBLE); + mLv.setVisibility(GONE); + //更新autoComplete数据 + if (mListener != null) { + mListener.onSearch(charSequence + ""); + } + } else { + mDeleteIgv.setVisibility(GONE); + if (mHistoryAdapter != null) { + mLv.setAdapter(mHistoryAdapter); + } + if (null != mListener) { + mListener.onClear(); + } + mLv.setVisibility(VISIBLE); + } + + } + + @Override + public void afterTextChanged(Editable editable) { + } + } + + private AdapterView.OnItemClickListener mOnItemClickListener = new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView adapterView, View view, int i, long l) { + if (i < mHistoryAdapter.getCount() - 1) { + String key = mHistoryAdapter.getItem(i).toString(); + notifyStartSearching(key); + } + } + }; + + /** + * search view回调方法 + */ + public interface SearchViewListener { + + /** + * 清除搜索内容 + */ + void onClear(); + + /** + * 更新自动补全内容 + * + * @param text + */ + void onSearch(String text); + + /** + * 搜索结果 + * + * @param text + */ + void onResult(String text); + + void onSearchByButton(String text); + + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/Star.java b/utill/src/main/java/com/lennon/cn/utill/widget/Star.java new file mode 100644 index 0000000..3cef6ca --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/Star.java @@ -0,0 +1,178 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import lennon.com.utill.R; + +/** + * 自定义评价星星类(任何形状) + * yueleng on 2017/1/9. + */ + +public class Star extends View { + //星星评分 + private float starMark = 0.0F; + //星星个数 + private int starNum = 5; + //星星高度 + private int starHeight; + //星星宽度 + private int starWidth; + //星星间距 + private int starDistance; + //星星背景 + private Drawable starBackgroundBitmap; + //动态星星 + private Bitmap starDrawDrawable; + //星星变化监听 + private OnStarChangeListener changeListener; + //是否可以点击 + private boolean isClick = true; + //画笔 + private Paint mPaint; + + public Star(Context mContext, AttributeSet attrs) { + super(mContext, attrs); + init(mContext, attrs); + } + + public Star(Context mContext, AttributeSet attrs, int defStyleAttr) { + super(mContext, attrs, defStyleAttr); + init(mContext, attrs); + } + + private void init(Context mContext, AttributeSet attrs) { + + //初始化控件属性 + TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.star); + starNum = typedArray.getInteger(R.styleable.star_starsNum, 5); + starHeight = (int) typedArray.getDimension(R.styleable.star_starHeight, 0); + starWidth = (int) typedArray.getDimension(R.styleable.star_starWidth, 0); + starDistance = (int) typedArray.getDimension(R.styleable.star_starDistance, 0); + isClick = typedArray.getBoolean(R.styleable.star_starClickable, true); + starBackgroundBitmap = typedArray.getDrawable(R.styleable.star_starBackground); + starDrawDrawable = drawableToBitmap(typedArray.getDrawable(R.styleable.star_starDrawBackground)); + typedArray.recycle(); + setClickable(isClick); + //初始化画笔 + mPaint = new Paint(); + //设置抗锯齿 + mPaint.setAntiAlias(true); + mPaint.setShader(new BitmapShader(starDrawDrawable, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); + } + + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + setMeasuredDimension(starWidth * starNum + starDistance * (starNum - 1), starHeight); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (null == starDrawDrawable || null == starBackgroundBitmap) { + return; + } + for (int i = 0; i < starNum; i++) { + starBackgroundBitmap.setBounds(starDistance * i + starWidth * i, 0, starWidth * (i + 1) + starDistance * i, starHeight); + starBackgroundBitmap.draw(canvas); + } + if (starMark > 1) { + canvas.drawRect(0, 0, starWidth, starHeight, mPaint); + if (starMark - (int) (starMark) == 0) { + for (int i = 1; i < starMark; i++) { + canvas.translate(starDistance + starWidth, 0); + canvas.drawRect(0, 0, starWidth, starHeight, mPaint); + } + } else { + for (int i = 1; i < starMark - 1; i++) { + canvas.translate(starDistance + starWidth, 0); + canvas.drawRect(0, 0, starWidth, starHeight, mPaint); + } + canvas.translate(starDistance + starWidth, 0); + canvas.drawRect(0, 0, starWidth * (Math.round((starMark - (int) (starMark)) * 10) * 1.0f / 10), starHeight, mPaint); + } + } else { + canvas.drawRect(0, 0, starWidth * starMark, starHeight, mPaint); + } + + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + int x = (int) event.getX(); + if (x < 0) + x = 0; + if (x > getMeasuredWidth()) + x = getMeasuredWidth(); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + setMark(x * 1.0f / (getMeasuredWidth() * 1.0f / starNum)); + break; + case MotionEvent.ACTION_MOVE: + setMark(x * 1.0f / (getMeasuredWidth() * 1.0f / starNum)); + break; + case MotionEvent.ACTION_UP: + setMark(x * 1.0f / (getMeasuredWidth() * 1.0f / starNum)); + break; + } + return true; + } + + /** + * 设置分数 + */ + public void setMark(Float mark) { + starMark = Math.round(mark * 10) * 1.0f / 10; + if (null != changeListener) { + changeListener.onStarChange(starMark); + } + invalidate(); + } + + /** + * 设置监听 + */ + public void setStarChangeLister(OnStarChangeListener starChangeLister) { + changeListener = starChangeLister; + } + + /** + * 获取分数 + */ + public float getMark() { + return starMark; + } + + /** + * 星星数量变化监听接口 + */ + public interface OnStarChangeListener { + void onStarChange(Float mark); + } + + /** + * drawable转bitmap + */ + private Bitmap drawableToBitmap(Drawable drawable) { + if (drawable == null) { + return null; + } + Bitmap bitmap = Bitmap.createBitmap(starWidth, starHeight, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, starWidth, starHeight); + drawable.draw(canvas); + return bitmap; + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/interf/OnHeadBarListener.java b/utill/src/main/java/com/lennon/cn/utill/widget/interf/OnHeadBarListener.java new file mode 100644 index 0000000..1592ef3 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/interf/OnHeadBarListener.java @@ -0,0 +1,12 @@ +package com.lennon.cn.utill.widget.interf; + +/** + * 头部导航栏监听器 + * Created by dingyi on 2016/11/15. + */ + +public class OnHeadBarListener{ + public void onLeft(){}; + public void onMid(){}; + public void onRight(){}; +} diff --git a/utill/src/main/res/anim/pull_in_left.xml b/utill/src/main/res/anim/pull_in_left.xml new file mode 100644 index 0000000..ddc965d --- /dev/null +++ b/utill/src/main/res/anim/pull_in_left.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/utill/src/main/res/anim/pull_in_right.xml b/utill/src/main/res/anim/pull_in_right.xml new file mode 100644 index 0000000..52e0996 --- /dev/null +++ b/utill/src/main/res/anim/pull_in_right.xml @@ -0,0 +1,10 @@ + + + + diff --git a/utill/src/main/res/anim/push_out_left.xml b/utill/src/main/res/anim/push_out_left.xml new file mode 100644 index 0000000..daad3da --- /dev/null +++ b/utill/src/main/res/anim/push_out_left.xml @@ -0,0 +1,10 @@ + + + + diff --git a/utill/src/main/res/anim/push_out_right.xml b/utill/src/main/res/anim/push_out_right.xml new file mode 100644 index 0000000..699897f --- /dev/null +++ b/utill/src/main/res/anim/push_out_right.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/utill/src/main/res/anim/pw_push_bottom_in.xml b/utill/src/main/res/anim/pw_push_bottom_in.xml new file mode 100644 index 0000000..348e755 --- /dev/null +++ b/utill/src/main/res/anim/pw_push_bottom_in.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/utill/src/main/res/anim/pw_push_bottom_out.xml b/utill/src/main/res/anim/pw_push_bottom_out.xml new file mode 100644 index 0000000..6f72ad6 --- /dev/null +++ b/utill/src/main/res/anim/pw_push_bottom_out.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/utill/src/main/res/anim/scale_in.xml b/utill/src/main/res/anim/scale_in.xml new file mode 100644 index 0000000..55e3d02 --- /dev/null +++ b/utill/src/main/res/anim/scale_in.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/anim/scale_out.xml b/utill/src/main/res/anim/scale_out.xml new file mode 100644 index 0000000..259d515 --- /dev/null +++ b/utill/src/main/res/anim/scale_out.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/btn_ac5454.xml b/utill/src/main/res/drawable/btn_ac5454.xml new file mode 100644 index 0000000..7382b7d --- /dev/null +++ b/utill/src/main/res/drawable/btn_ac5454.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/btn_ac5454_f.xml b/utill/src/main/res/drawable/btn_ac5454_f.xml new file mode 100644 index 0000000..5a9ab46 --- /dev/null +++ b/utill/src/main/res/drawable/btn_ac5454_f.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/btn_bfbfbf_frame.xml b/utill/src/main/res/drawable/btn_bfbfbf_frame.xml new file mode 100644 index 0000000..49e2363 --- /dev/null +++ b/utill/src/main/res/drawable/btn_bfbfbf_frame.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/common_button.xml b/utill/src/main/res/drawable/common_button.xml new file mode 100644 index 0000000..27028bd --- /dev/null +++ b/utill/src/main/res/drawable/common_button.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/common_button_bfbfbf.xml b/utill/src/main/res/drawable/common_button_bfbfbf.xml new file mode 100644 index 0000000..4a359ee --- /dev/null +++ b/utill/src/main/res/drawable/common_button_bfbfbf.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/common_button_fffff.xml b/utill/src/main/res/drawable/common_button_fffff.xml new file mode 100644 index 0000000..17c5c53 --- /dev/null +++ b/utill/src/main/res/drawable/common_button_fffff.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/common_dialog_frame.xml b/utill/src/main/res/drawable/common_dialog_frame.xml new file mode 100644 index 0000000..2830244 --- /dev/null +++ b/utill/src/main/res/drawable/common_dialog_frame.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/conner_f2f2f2.xml b/utill/src/main/res/drawable/conner_f2f2f2.xml new file mode 100644 index 0000000..e88451c --- /dev/null +++ b/utill/src/main/res/drawable/conner_f2f2f2.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/conner_fd0202.xml b/utill/src/main/res/drawable/conner_fd0202.xml new file mode 100644 index 0000000..27028bd --- /dev/null +++ b/utill/src/main/res/drawable/conner_fd0202.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/conner_ffffff.xml b/utill/src/main/res/drawable/conner_ffffff.xml new file mode 100644 index 0000000..0dd9ff4 --- /dev/null +++ b/utill/src/main/res/drawable/conner_ffffff.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/conner_right_fd0202.xml b/utill/src/main/res/drawable/conner_right_fd0202.xml new file mode 100644 index 0000000..96bc3b2 --- /dev/null +++ b/utill/src/main/res/drawable/conner_right_fd0202.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/linearlayout_fffff_frame.xml b/utill/src/main/res/drawable/linearlayout_fffff_frame.xml new file mode 100644 index 0000000..b4eae12 --- /dev/null +++ b/utill/src/main/res/drawable/linearlayout_fffff_frame.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/price_selector.xml b/utill/src/main/res/drawable/price_selector.xml new file mode 100644 index 0000000..4501886 --- /dev/null +++ b/utill/src/main/res/drawable/price_selector.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/table_line.xml b/utill/src/main/res/drawable/table_line.xml new file mode 100644 index 0000000..74576f9 --- /dev/null +++ b/utill/src/main/res/drawable/table_line.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/table_radio.xml b/utill/src/main/res/drawable/table_radio.xml new file mode 100644 index 0000000..62ef00c --- /dev/null +++ b/utill/src/main/res/drawable/table_radio.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/table_select.xml b/utill/src/main/res/drawable/table_select.xml new file mode 100644 index 0000000..6c8fc3d --- /dev/null +++ b/utill/src/main/res/drawable/table_select.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/table_unselect.xml b/utill/src/main/res/drawable/table_unselect.xml new file mode 100644 index 0000000..0c75b99 --- /dev/null +++ b/utill/src/main/res/drawable/table_unselect.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/web_progress.xml b/utill/src/main/res/drawable/web_progress.xml new file mode 100644 index 0000000..4b52c78 --- /dev/null +++ b/utill/src/main/res/drawable/web_progress.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/common_alert_dialog.xml b/utill/src/main/res/layout/common_alert_dialog.xml new file mode 100644 index 0000000..410d110 --- /dev/null +++ b/utill/src/main/res/layout/common_alert_dialog.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/customprogressdialog.xml b/utill/src/main/res/layout/customprogressdialog.xml new file mode 100644 index 0000000..489c8e3 --- /dev/null +++ b/utill/src/main/res/layout/customprogressdialog.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/dialog_bottom_search_select.xml b/utill/src/main/res/layout/dialog_bottom_search_select.xml new file mode 100644 index 0000000..5b456b0 --- /dev/null +++ b/utill/src/main/res/layout/dialog_bottom_search_select.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/dialog_bottom_select.xml b/utill/src/main/res/layout/dialog_bottom_select.xml new file mode 100644 index 0000000..a211c9b --- /dev/null +++ b/utill/src/main/res/layout/dialog_bottom_select.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/headbar.xml b/utill/src/main/res/layout/headbar.xml new file mode 100644 index 0000000..b30362e --- /dev/null +++ b/utill/src/main/res/layout/headbar.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/item_base_view.xml b/utill/src/main/res/layout/item_base_view.xml new file mode 100644 index 0000000..65cc84e --- /dev/null +++ b/utill/src/main/res/layout/item_base_view.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/item_string_item.xml b/utill/src/main/res/layout/item_string_item.xml new file mode 100644 index 0000000..54824a2 --- /dev/null +++ b/utill/src/main/res/layout/item_string_item.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/search_scan_view_layout.xml b/utill/src/main/res/layout/search_scan_view_layout.xml new file mode 100644 index 0000000..9880ebc --- /dev/null +++ b/utill/src/main/res/layout/search_scan_view_layout.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/searchview_layout.xml b/utill/src/main/res/layout/searchview_layout.xml new file mode 100644 index 0000000..1086374 --- /dev/null +++ b/utill/src/main/res/layout/searchview_layout.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/mipmap-hdpi/ic_rating_bar_hollow.png b/utill/src/main/res/mipmap-hdpi/ic_rating_bar_hollow.png new file mode 100644 index 0000000000000000000000000000000000000000..5f7c977d0c01da3501349499fbd81f2295b7cd10 GIT binary patch literal 3162 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 AMgRZ+ literal 0 HcmV?d00001 diff --git a/utill/src/main/res/mipmap-hdpi/ic_rating_bar_solid.png b/utill/src/main/res/mipmap-hdpi/ic_rating_bar_solid.png new file mode 100644 index 0000000000000000000000000000000000000000..b8d646deaeb5325d212474e29ab4c31c038df7a0 GIT binary patch literal 3166 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 literal 0 HcmV?d00001 diff --git a/utill/src/main/res/mipmap-xxhdpi/cha2.png b/utill/src/main/res/mipmap-xxhdpi/cha2.png new file mode 100644 index 0000000000000000000000000000000000000000..5a123ec166a4732bdadbc5669560cc3ae26992a3 GIT binary patch literal 2129 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 literal 0 HcmV?d00001 diff --git a/utill/src/main/res/raw/beep.ogg b/utill/src/main/res/raw/beep.ogg new file mode 100644 index 0000000000000000000000000000000000000000..dc6e719acbcac1807d0c3ea361c2484e6d9fa96a GIT binary patch literal 10185 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 NsdTm#sxklX{{X5U;1vJ> literal 0 HcmV?d00001 diff --git a/utill/src/main/res/values/attrs.xml b/utill/src/main/res/values/attrs.xml new file mode 100644 index 0000000..6832d20 --- /dev/null +++ b/utill/src/main/res/values/attrs.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + //星星数量 + + //星星宽度 + + //星星高度 + + //星星间距 + + //星星背景 + + //星星变化背景 + + //控件是否可以点击 + + + + + + + + + + + + + + + + diff --git a/utill/src/main/res/values/color.xml b/utill/src/main/res/values/color.xml new file mode 100644 index 0000000..dc192bc --- /dev/null +++ b/utill/src/main/res/values/color.xml @@ -0,0 +1,43 @@ + + + #BF0001 + #00000000 + #333333 + #00000000 + #50000000 + #EB0000 + #b5b5b5 + #dcdcdc + #cf333333 + #d3d3d3 + #FFFFFF + #ffffff + #7f000000 + #dddddd + #e6e6e6 + #f2f2f2 + #DBDBDB + #ffcc00 + #dcdcdc + #f20c0c + #bfbfbf + #777777 + #c60c1b + #b22222 + #fd0202 + #f82629 + #ac5454 + #6495ed + #3F51B5 + #0f83d0 + #878787 + #999999 + #CCCCCC + #EAEAEA + #EAEAEA + #DDDDDD + #666666 + #6c6c6c + #d82043 + #737373 + \ No newline at end of file diff --git a/utill/src/main/res/values/dimens.xml b/utill/src/main/res/values/dimens.xml new file mode 100644 index 0000000..6bcda6d --- /dev/null +++ b/utill/src/main/res/values/dimens.xml @@ -0,0 +1,73 @@ + + + + 45dp + + 10sp + 12sp + 14sp + 16sp + 18sp + 20sp + 22sp + 24sp + 30sp + + -50dp + -60dp + -70dp + -75dp + + + 0.5dp + 0dp + 1dp + 2dp + 3dp + 4dp + 5dp + 6dp + 7dp + 8dp + 10dp + 12dp + 14dp + 15dp + 16dp + 18dp + 20dp + 22dp + 25dp + 26dp + 30dp + 40dp + 45dp + 46dp + 48dp + 50dp + 55dp + 56dp + 60dp + 70dp + 80dp + 85dp + 90dp + 100dp + 110dp + 120dp + 130dp + 135dp + 140dp + 150dp + 165dp + 170dp + 180dp + 200dp + 250dp + 300dp + 320dp + 340dp + 350dp + 425dp + + \ No newline at end of file diff --git a/utill/src/main/res/values/strings.xml b/utill/src/main/res/values/strings.xml new file mode 100644 index 0000000..ac876e3 --- /dev/null +++ b/utill/src/main/res/values/strings.xml @@ -0,0 +1,7 @@ + + Utill + 获取验证码 + 重新获取 + + 请输入烟品名称 + diff --git a/utill/src/main/res/values/style.xml b/utill/src/main/res/values/style.xml new file mode 100644 index 0000000..fa3678b --- /dev/null +++ b/utill/src/main/res/values/style.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/values/values.xml b/utill/src/main/res/values/values.xml new file mode 100644 index 0000000..e35e82f --- /dev/null +++ b/utill/src/main/res/values/values.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/xrecycler/build.gradle b/xrecycler/build.gradle index bcb4e31..29f7e74 100644 --- a/xrecycler/build.gradle +++ b/xrecycler/build.gradle @@ -1,19 +1,19 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' -apply plugin: 'com.novoda.bintray-release' - -publish { - artifactId = 'xrecycler' // artifactId - uploadName = 'XRecycler' // 展示名字 - publishVersion = '1.0.3' // 版本 - userOrg = rootProject.userOrg - groupId = rootProject.groupId - repoName = rootProject.repoName - desc = '' - website = rootProject.website - licences = rootProject.licences -} +//apply plugin: 'com.novoda.bintray-release' +// +//publish { +// artifactId = 'xrecycler' // artifactId +// uploadName = 'XRecycler' // 展示名字 +// publishVersion = '1.0.3' // 版本 +// userOrg = rootProject.userOrg +// groupId = rootProject.groupId +// repoName = rootProject.repoName +// desc = '' +// website = rootProject.website +// licences = rootProject.licences +//} android { compileSdkVersion rootProject.ext.android.compileSdkVersion From 0ef702124d20df91d2326c89c6e6ca8bae6ddbf7 Mon Sep 17 00:00:00 2001 From: Edit <1136160757@qq.com> Date: Wed, 1 Apr 2020 10:00:52 +0800 Subject: [PATCH 10/24] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E6=96=87=E4=BB=B6=E9=85=8D=E7=BD=AE=EF=BC=8C?= =?UTF-8?q?=E5=85=81=E8=AE=B8=E6=97=A5=E5=BF=97=E8=BE=93=E5=87=BA=E4=B8=BA?= =?UTF-8?q?=E6=96=87=E4=BB=B6=EF=BC=8C=E6=9B=B4=E6=96=B0=E9=83=A8=E5=88=86?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E7=9A=84=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 9 +- conf.gradle | 61 ++-- gradle.properties | 2 + gradle/wrapper/gradle-wrapper.properties | 4 +- mvp/build.gradle | 4 +- mvp/src/main/AndroidManifest.xml | 3 +- .../droidlover/xdroidmvp/XDroidMvpUtill.java | 14 + .../xdroidmvp/base/SimpleRecAdapter.java | 32 ++ .../xdroidmvp/imageloader/LoadCallback.java | 6 +- .../droidlover/xdroidmvp/log/LogFormat.java | 18 +- .../cn/droidlover/xdroidmvp/log/XLog.java | 29 +- .../xdroidmvp/mvp/XLazyFragment.java | 1 - .../xdroidmvp/net/ApiSubscriber.java | 1 + .../cn/droidlover/xdroidmvp/net/IModel.java | 2 + .../xdroidmvp/net/LogInterceptor.java | 14 +- .../cn/droidlover/xdroidmvp/net/NetError.java | 21 ++ .../cn/droidlover/xdroidmvp/net/XApi.java | 13 +- nativelogger/build.gradle | 49 +++ nativelogger/proguard-rules.pro | 17 + nativelogger/src/main/AndroidManifest.xml | 3 + .../main/java/cn/jesse/nativelogger/Logger.kt | 12 + .../java/cn/jesse/nativelogger/NLogger.kt | 330 +++++++++++++++++ .../cn/jesse/nativelogger/NLoggerConfig.kt | 247 +++++++++++++ .../cn/jesse/nativelogger/NLoggerError.kt | 38 ++ .../nativelogger/formatter/SimpleFormatter.kt | 34 ++ .../nativelogger/formatter/TagFormatter.kt | 95 +++++ .../nativelogger/logger/AndroidLogger.kt | 251 +++++++++++++ .../jesse/nativelogger/logger/FileLogger.kt | 336 ++++++++++++++++++ .../jesse/nativelogger/logger/LoggerLevel.kt | 14 + .../logger/base/AbstractLogger.kt | 172 +++++++++ .../nativelogger/logger/base/IFileLogger.kt | 43 +++ .../jesse/nativelogger/logger/base/ILogger.kt | 333 +++++++++++++++++ .../cn/jesse/nativelogger/util/CloseUtil.kt | 27 ++ .../jesse/nativelogger/util/CrashWatcher.kt | 45 +++ .../cn/jesse/nativelogger/util/DateUtil.kt | 119 +++++++ .../cn/jesse/nativelogger/util/ZipUtil.kt | 119 +++++++ settings.gradle | 2 +- utill/build.gradle | 7 +- .../cn/utill/adapter/BaseViewAdapter.kt | 2 +- .../cn/utill/adapter/StringItemAdapter.kt | 2 +- .../com/lennon/cn/utill/base/BaseActivity.kt | 22 ++ .../com/lennon/cn/utill/base/BaseApi.java | 1 - .../lennon/cn/utill/base/BaseApplication.kt | 190 +--------- .../lennon/cn/utill/base/BaseLazyFragment.kt | 144 ++++++++ .../com/lennon/cn/utill/bean/HttpEntity.java | 5 + .../cn/utill/utill/AudioRecorderUtil.java | 6 +- .../lennon/cn/utill/utill/GetDeviceId.java | 88 +---- 47 files changed, 2632 insertions(+), 355 deletions(-) create mode 100644 mvp/src/main/java/cn/droidlover/xdroidmvp/XDroidMvpUtill.java create mode 100644 nativelogger/build.gradle create mode 100644 nativelogger/proguard-rules.pro create mode 100644 nativelogger/src/main/AndroidManifest.xml create mode 100644 nativelogger/src/main/java/cn/jesse/nativelogger/Logger.kt create mode 100644 nativelogger/src/main/java/cn/jesse/nativelogger/NLogger.kt create mode 100644 nativelogger/src/main/java/cn/jesse/nativelogger/NLoggerConfig.kt create mode 100644 nativelogger/src/main/java/cn/jesse/nativelogger/NLoggerError.kt create mode 100644 nativelogger/src/main/java/cn/jesse/nativelogger/formatter/SimpleFormatter.kt create mode 100644 nativelogger/src/main/java/cn/jesse/nativelogger/formatter/TagFormatter.kt create mode 100644 nativelogger/src/main/java/cn/jesse/nativelogger/logger/AndroidLogger.kt create mode 100644 nativelogger/src/main/java/cn/jesse/nativelogger/logger/FileLogger.kt create mode 100644 nativelogger/src/main/java/cn/jesse/nativelogger/logger/LoggerLevel.kt create mode 100644 nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/AbstractLogger.kt create mode 100644 nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/IFileLogger.kt create mode 100644 nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/ILogger.kt create mode 100644 nativelogger/src/main/java/cn/jesse/nativelogger/util/CloseUtil.kt create mode 100644 nativelogger/src/main/java/cn/jesse/nativelogger/util/CrashWatcher.kt create mode 100644 nativelogger/src/main/java/cn/jesse/nativelogger/util/DateUtil.kt create mode 100644 nativelogger/src/main/java/cn/jesse/nativelogger/util/ZipUtil.kt create mode 100644 utill/src/main/java/com/lennon/cn/utill/base/BaseLazyFragment.kt diff --git a/build.gradle b/build.gradle index 03cdf7a..9935589 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ apply from: "conf.gradle" buildscript { - ext.kotlin_version = '1.3.61' + ext.kotlin_version = '1.3.71' repositories { maven {url 'https://dl.bintray.com/lennon-xia/lennon/'} jcenter() { url 'https://maven.aliyun.com/repository/jcenter' } @@ -14,7 +14,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' + classpath 'com.android.tools.build:gradle:3.6.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' @@ -45,6 +45,11 @@ allprojects { jcenter() maven { url "https://jitpack.io" } } + tasks.withType(Javadoc).all { + enabled = false + } + + } task clean(type: Delete) { diff --git a/conf.gradle b/conf.gradle index bf80f20..de214b7 100644 --- a/conf.gradle +++ b/conf.gradle @@ -1,64 +1,65 @@ ext { android = [ - compileSdkVersion : 29, + compileSdkVersion: 29, - minSdkVersion : 16, - targetSdkVersion : 29, + minSdkVersion : 16, + targetSdkVersion : 29, - versionCode : 1, - versionName : '1.0.0', + versionCode : 1, + versionName : '1.0.0', - VSupportSdk : '27.1.1', - VRetrofitSdk : "2.2.0", - VOkhttp : "3.4.2", - VRxlifecycle : "3.0.0", + VSupportSdk : '27.1.1', + VRetrofitSdk : '2.8.1', + VOkhttp : '4.4.1', + VRxlifecycle : '3.1.0', + + NavigationSDK : "2.2.1", - latest_integration: "latest.integration", ] dependencies = [ - "navigation-fragment-ktx" : "androidx.navigation:navigation-fragment-ktx:2.1.0", - "navigation-ui-ktx" : "androidx.navigation:navigation-ui-ktx:2.1.0", + "navigation-fragment-ktx" : "androidx.navigation:navigation-fragment-ktx:${android["NavigationSDK"]}", + "navigation-ui-ktx" : "androidx.navigation:navigation-ui-ktx:${android["NavigationSDK"]}", - "navigation-ui" : "androidx.navigation:navigation-ui:2.1.0", - "navigation-fragment" : "androidx.navigation:navigation-fragment:2.1.0", + "navigation-ui" : "androidx.navigation:navigation-ui:${android["NavigationSDK"]}", + "navigation-fragment" : "androidx.navigation:navigation-fragment:${android["NavigationSDK"]}", - "constraintlayout" : "androidx.constraintlayout:constraintlayout:${android["latest_integration"]}", + "constraintlayout" : "androidx.constraintlayout:constraintlayout:2.0.0-beta4", - "exifinterface" : "androidx.exifinterface:exifinterface:${android["latest_integration"]}", + "exifinterface" : "androidx.exifinterface:exifinterface:1.3.0-alpha01", - "multidex" : "androidx.multidex:multidex:${android["latest_integration"]}", + "multidex" : "androidx.multidex:multidex:2.0.1", "kotlin" : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version", - "PersistentCookieJar" : "com.github.franmontiel:PersistentCookieJar:${android["latest_integration"]}", + "PersistentCookieJar" : "com.github.franmontiel:PersistentCookieJar:1.0.1", - "qiujuer-ui" : "net.qiujuer.genius:ui:${android["latest_integration"]}", + "qiujuer-ui" : "net.qiujuer.genius:ui:2.1.1", - "appcompat-v7" : "androidx.appcompat:appcompat:${android["latest_integration"]}", + "appcompat-v7" : "androidx.appcompat:appcompat:1.2.0-alpha02", - "support-v4" : "androidx.legacy:legacy-support-v4:${android["latest_integration"]}", + "support-v4" : "androidx.legacy:legacy-support-v4:1.0.0", - "design" : "com.google.android.material:material:${android["latest_integration"]}", + "design" : "com.google.android.material:material:1.2.0-alpha05", "annotations" : "androidx.annotation:annotation:1.1.0", - "recyclerview-v7" : "androidx.recyclerview:recyclerview:${android["latest_integration"]}", + "recyclerview-v7" : "androidx.recyclerview:recyclerview:1.2.0-alpha01", "eventbus" : "org.greenrobot:eventbus:3.0.0", - "glide" : "com.github.bumptech.glide:glide:4.9.0", - "glide-compiler" : "com.github.bumptech.glide:compiler:4.9.0", + "glide" : "com.github.bumptech.glide:glide:4.11.0", + "glide-compiler" : "com.github.bumptech.glide:compiler:4.11.0", "picasso" : "com.squareup.picasso:picasso:2.5.2", "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.3", - "xdroid-mvp" : "com.lennon.utill:xdroid-mvp:1.0.3", + "xdroid-mvp" : "com.lennon.utill:xdroid-mvp:1.1.5", "avi-loading" : "com.wang.avi:library:1.0.2", "rxbus" : "com.blankj:rxbus:1.2", - "gson" : "com.google.code.gson:gson:2.6.2", - "rxandroid" : "io.reactivex.rxjava2:rxandroid:2.0.1", - "rxjava" : "io.reactivex.rxjava2:rxjava:2.0.1", + "gson" : "com.google.code.gson:gson:2.8.6", + "rxandroid" : "io.reactivex.rxjava2:rxandroid:2.1.1", + "rxjava" : "io.reactivex.rxjava2:rxjava:2.2.19", "retrofit" : "com.squareup.retrofit2:retrofit:${android["VRetrofitSdk"]}", "retrofit-converter-gson" : "com.squareup.retrofit2:converter-gson:${android["VRetrofitSdk"]}", @@ -78,6 +79,8 @@ ext { "canary-release" : "com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2", "XStateController" : "com.github.limedroid:XStateController:v1.1.1", + + "nativelogger" : "com.lennon.utill:nativelogger:1.0" ] } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index e091853..4755f39 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,6 +14,8 @@ kotlin.code.style=official android.enableJetifier=true org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M" android.useAndroidX=true +android.debug.obsoleteApi=true +android.obsoleteApi=true # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 695f36b..03241c1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Apr 13 19:19:31 CST 2018 +#Fri Mar 27 15:02:30 CST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/mvp/build.gradle b/mvp/build.gradle index 410aa86..14638d1 100644 --- a/mvp/build.gradle +++ b/mvp/build.gradle @@ -10,7 +10,7 @@ apply plugin: 'kotlin-android-extensions' //publish { // artifactId = 'xdroid-mvp' // artifactId // uploadName = 'XDroidMvp' // 展示名字 -// publishVersion = '1.0.3' // 版本 +// publishVersion = '1.1.5' // 版本 // userOrg = rootProject.userOrg // groupId = rootProject.groupId // repoName = rootProject.repoName @@ -64,6 +64,8 @@ dependencies { api rootProject.ext.dependencies["rxpermissions"] api rootProject.ext.dependencies["kotlin"] + + api rootProject.ext.dependencies["nativelogger"] } tasks.withType(JavaCompile) { diff --git a/mvp/src/main/AndroidManifest.xml b/mvp/src/main/AndroidManifest.xml index 45033fc..3c1c3e3 100644 --- a/mvp/src/main/AndroidManifest.xml +++ b/mvp/src/main/AndroidManifest.xml @@ -1,8 +1,9 @@ + + android:supportsRtl="true"> diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/XDroidMvpUtill.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/XDroidMvpUtill.java new file mode 100644 index 0000000..84f6534 --- /dev/null +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/XDroidMvpUtill.java @@ -0,0 +1,14 @@ +package cn.droidlover.xdroidmvp; + +import android.app.Service; +import android.content.Context; +import android.os.Vibrator; + +public class XDroidMvpUtill { + public static void vibrator(Context context) { +//获取系统震动服务 + Vibrator vib = (Vibrator) context.getSystemService(Service.VIBRATOR_SERVICE); +//震动70毫秒 + vib.vibrate(70); + } +} diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleRecAdapter.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleRecAdapter.java index 1629a20..17524ca 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleRecAdapter.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleRecAdapter.java @@ -6,6 +6,7 @@ import android.view.View; import android.view.ViewGroup; +import cn.droidlover.xdroidmvp.XDroidMvpUtill; import cn.droidlover.xrecyclerview.RecyclerAdapter; /** @@ -13,6 +14,7 @@ */ public abstract class SimpleRecAdapter extends RecyclerAdapter { + public static final int TAG_VIEW = 0; public SimpleRecAdapter(Context context) { super(context); @@ -24,6 +26,36 @@ public F onCreateViewHolder(ViewGroup parent, int viewType) { return newViewHolder(view); } + @Override + public void onBindViewHolder(final F holder, final int position) { + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (getRecItemClick() != null) { + T d = null; + if (position < data.size() && position >= 0) { + d = data.get(position); + } + getRecItemClick().onItemClick(position, d, TAG_VIEW, holder); + } + } + }); + holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + XDroidMvpUtill.vibrator(context); + if (getRecItemClick() != null) { + T d = null; + if (position < data.size() && position >= 0) { + d = data.get(position); + } + getRecItemClick().onItemLongClick(position, d, TAG_VIEW, holder); + } + return true; + } + }); + } + public abstract F newViewHolder(View itemView); public abstract int getLayoutId(); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/LoadCallback.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/LoadCallback.java index c5192fe..c576967 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/LoadCallback.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/LoadCallback.java @@ -7,9 +7,11 @@ */ public abstract class LoadCallback { - void onLoadFailed() {} + public void onLoadFailed() { + } public abstract void onLoadReady(Drawable drawable); - void onLoadCanceled() {} + public void onLoadCanceled() { + } } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/log/LogFormat.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/log/LogFormat.java index 85037aa..46159d2 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/log/LogFormat.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/log/LogFormat.java @@ -28,20 +28,12 @@ public class LogFormat { private static final char VERTICAL_BORDER_CHAR = '║'; - // Length: 100. private static final String TOP_HORIZONTAL_BORDER = - "╔═════════════════════════════════════════════════" + - "══════════════════════════════════════════════════"; - - // Length: 99. - private static final String DIVIDER_HORIZONTAL_BORDER = - "╟─────────────────────────────────────────────────" + - "──────────────────────────────────────────────────"; - - // Length: 100. - private static final String BOTTOM_HORIZONTAL_BORDER = - "╚═════════════════════════════════════════════════" + - "══════════════════════════════════════════════════"; + "╔═══════════════════════════════════════════════════════════════════════════════════════════════════"; + + private static final String DIVIDER_HORIZONTAL_BORDER = "╟───────────────────────────────────────────────────────────────────────────────────────────────────"; + + private static final String BOTTOM_HORIZONTAL_BORDER = "╚═══════════════════════════════════════════════════════════════════════════════════════════════════"; public static String formatJson(String json) { String formatted = null; diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/log/XLog.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/log/XLog.java index 418091c..c3418e3 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/log/XLog.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/log/XLog.java @@ -4,6 +4,8 @@ import android.util.Log; import cn.droidlover.xdroidmvp.XDroidConf; +import cn.jesse.nativelogger.NLogger; +import cn.jesse.nativelogger.logger.LoggerLevel; /** * Created by wanglei on 2016/11/29. @@ -15,25 +17,26 @@ public class XLog { public static String TAG_ROOT = XDroidConf.LOG_TAG; public static void json(String json) { - json(Log.DEBUG, null, json); +// json(Log.DEBUG, null, json); + NLogger.json(LoggerLevel.DEBUG, json); } - public static void json(int logLevel, String tag, String json) { + public static void json(LoggerLevel logLevel, String tag, String json) { if (LOG) { String formatJson = LogFormat.formatBorder(new String[]{LogFormat.formatJson(json)}); - XPrinter.println(logLevel, TextUtils.isEmpty(tag) ? TAG_ROOT : tag, formatJson); + NLogger.println(logLevel, TextUtils.isEmpty(tag) ? TAG_ROOT : tag, formatJson); } } public static void xml(String xml) { - xml(Log.DEBUG, null, xml); + xml(LoggerLevel.DEBUG, null, xml); } - public static void xml(int logLevel, String tag, String xml) { + public static void xml(LoggerLevel logLevel, String tag, String xml) { if (LOG) { String formatXml = LogFormat.formatBorder(new String[]{LogFormat.formatXml(xml)}); - XPrinter.println(logLevel, TextUtils.isEmpty(tag) ? TAG_ROOT : tag, formatXml); + NLogger.println(logLevel, TextUtils.isEmpty(tag) ? TAG_ROOT : tag, formatXml); } } @@ -44,31 +47,31 @@ public static void error(Throwable throwable) { public static void error(String tag, Throwable throwable) { if (LOG) { String formatError = LogFormat.formatBorder(new String[]{LogFormat.formatThrowable(throwable)}); - XPrinter.println(Log.ERROR, TextUtils.isEmpty(tag) ? TAG_ROOT : tag, formatError); + NLogger.println(LoggerLevel.ERROR, TextUtils.isEmpty(tag) ? TAG_ROOT : tag, formatError); } } - private static void msg(int logLevel, String tag, String format, Object... args) { + private static void msg(LoggerLevel logLevel, String tag, String format, Object... args) { if (LOG) { String formatMsg = LogFormat.formatBorder(new String[]{LogFormat.formatArgs(format, args)}); - XPrinter.println(logLevel, TextUtils.isEmpty(tag) ? TAG_ROOT : tag, formatMsg); + NLogger.println(logLevel, TextUtils.isEmpty(tag) ? TAG_ROOT : tag, formatMsg); } } public static void d(String msg, Object... args) { - msg(Log.DEBUG, null, msg, args); + msg(LoggerLevel.DEBUG, null, msg, args); } public static void d(String tag, String msg, Object... args) { - msg(Log.DEBUG, tag, msg, args); + msg(LoggerLevel.DEBUG, tag, msg, args); } public static void e(String msg, Object... args) { - msg(Log.ERROR, null, msg, args); + msg(LoggerLevel.ERROR, null, msg, args); } public static void e(String tag, String msg, Object... args) { - msg(Log.ERROR, tag, msg, args); + msg(LoggerLevel.ERROR, tag, msg, args); } } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java index e442fe6..adb7ae2 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java @@ -1,7 +1,6 @@ package cn.droidlover.xdroidmvp.mvp; import android.os.Bundle; -import android.view.View; import com.tbruyelle.rxpermissions2.RxPermissions; diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/ApiSubscriber.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/ApiSubscriber.java index 80c47ab..155976a 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/ApiSubscriber.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/ApiSubscriber.java @@ -21,6 +21,7 @@ public abstract class ApiSubscriber extends ResourceSubscriber public void onError(Throwable e) { NetError error = null; if (e != null) { + e.printStackTrace(); if (!(e instanceof NetError)) { if (e instanceof UnknownHostException) { error = new NetError(e, NetError.NoConnectError); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/IModel.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/IModel.java index 9299970..299044b 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/IModel.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/IModel.java @@ -5,6 +5,8 @@ */ public interface IModel { + String getErrorCode(); + boolean isNull(); //空数据 boolean isAuthError(); //验证错误 diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/LogInterceptor.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/LogInterceptor.java index 51941fd..2614010 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/LogInterceptor.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/LogInterceptor.java @@ -5,6 +5,7 @@ import java.io.IOException; import cn.droidlover.xdroidmvp.log.XLog; +import cn.jesse.nativelogger.logger.LoggerLevel; import okhttp3.Headers; import okhttp3.Interceptor; import okhttp3.MediaType; @@ -35,8 +36,8 @@ private void logRequest(Request request) { String url = request.url().toString(); Headers headers = request.headers(); - XLog.d(TAG, "url : " + url); - XLog.d(TAG, "method : " + request.method()); + XLog.e(TAG, "url : " + url); + XLog.e(TAG, "method : " + request.method()); if (headers != null && headers.size() > 0) { XLog.e(TAG, "headers : " + headers.toString()); } @@ -45,9 +46,9 @@ private void logRequest(Request request) { MediaType mediaType = requestBody.contentType(); if (mediaType != null) { if (isText(mediaType)) { - XLog.d(TAG, "params : " + bodyToString(request)); + XLog.e(TAG, "params : " + bodyToString(request)); } else { - XLog.d(TAG, "params : " + " maybe [file part] , too large too print , ignored!"); + XLog.e(TAG, "params : " + " maybe [file part] , too large too print , ignored!"); } } } @@ -66,12 +67,11 @@ private Response logResponse(Response response) { if (mediaType != null) { if (isText(mediaType)) { String resp = body.string(); - XLog.json(Log.DEBUG, TAG, resp); - + XLog.e(TAG, resp); body = ResponseBody.create(mediaType, resp); return response.newBuilder().body(body).build(); } else { - XLog.d(TAG, "data : " + " maybe [file part] , too large too print , ignored!"); + XLog.e(TAG, "data : " + " maybe [file part] , too large too print , ignored!"); } } } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/NetError.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/NetError.java index 3b78762..738ea24 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/NetError.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/NetError.java @@ -7,6 +7,7 @@ public class NetError extends Exception { private Throwable exception; private int type = NoConnectError; + private String code; public static final int ParseError = 0; //数据解析异常 public static final int NoConnectError = 1; //无连接异常 @@ -25,6 +26,18 @@ public NetError(String detailMessage, int type) { this.type = type; } + public NetError(Throwable exception, int type, String code) { + this.exception = exception; + this.type = type; + this.code = code; + } + + public NetError(String detailMessage, int type, String code) { + super(detailMessage); + this.type = type; + this.code = code; + } + @Override public String getMessage() { if (exception != null) return exception.getMessage(); @@ -34,4 +47,12 @@ public String getMessage() { public int getType() { return type; } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/XApi.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/XApi.java index b11b109..2effc4a 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/XApi.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/XApi.java @@ -103,7 +103,9 @@ private OkHttpClient getClient(String baseUrl, NetProvider provider) { if (Kits.Empty.check(baseUrl)) { throw new IllegalStateException("baseUrl can not be null"); } - if (clientMap.get(baseUrl) != null) return clientMap.get(baseUrl); + if (clientMap.get(baseUrl) != null) { + return clientMap.get(baseUrl); + } checkProvider(provider); @@ -201,13 +203,14 @@ public Publisher apply(Flowable upstream) { return upstream.flatMap(new Function>() { @Override public Publisher apply(T model) throws Exception { - - if (model == null || model.isNull()) { + if (model == null) { return Flowable.error(new NetError(model.getErrorMsg(), NetError.NoDataError)); + } else if (model.isNull()) { + return Flowable.error(new NetError(model.getErrorMsg(), NetError.NoDataError, model.getErrorCode())); } else if (model.isAuthError()) { - return Flowable.error(new NetError(model.getErrorMsg(), NetError.AuthError)); + return Flowable.error(new NetError(model.getErrorMsg(), NetError.AuthError, model.getErrorCode())); } else if (model.isBizError()) { - return Flowable.error(new NetError(model.getErrorMsg(), NetError.BusinessError)); + return Flowable.error(new NetError(model.getErrorMsg(), NetError.BusinessError, model.getErrorCode())); } else { return Flowable.just(model); } diff --git a/nativelogger/build.gradle b/nativelogger/build.gradle new file mode 100644 index 0000000..ae7ce00 --- /dev/null +++ b/nativelogger/build.gradle @@ -0,0 +1,49 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +//apply plugin: 'com.novoda.bintray-release' +// +//publish { +// artifactId = 'nativelogger' // artifactId +// uploadName = 'NativeLogger' // 展示名字 +// publishVersion = '1.0' // 版本 +// userOrg = rootProject.userOrg +// groupId = rootProject.groupId +// repoName = rootProject.repoName +// desc = '' +// website = rootProject.website +// licences = rootProject.licences +//} +android { + compileSdkVersion rootProject.ext.android.compileSdkVersion + + defaultConfig { + minSdkVersion rootProject.ext.android.minSdkVersion + targetSdkVersion rootProject.ext.android.targetSdkVersion + versionCode rootProject.ext.android.versionCode + versionName rootProject.ext.android.versionName + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + api fileTree(dir: 'libs', include: ['*.jar']) + api rootProject.ext.dependencies["kotlin"] +} +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" +} + +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} +artifacts { + archives sourcesJar +} diff --git a/nativelogger/proguard-rules.pro b/nativelogger/proguard-rules.pro new file mode 100644 index 0000000..de1a1e5 --- /dev/null +++ b/nativelogger/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/jesse/Platform/android-sdk-macosx/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/nativelogger/src/main/AndroidManifest.xml b/nativelogger/src/main/AndroidManifest.xml new file mode 100644 index 0000000..2c5cbd5 --- /dev/null +++ b/nativelogger/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + + diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/Logger.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/Logger.kt new file mode 100644 index 0000000..85990fe --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/Logger.kt @@ -0,0 +1,12 @@ +package cn.jesse.nativelogger + +import cn.jesse.nativelogger.logger.LoggerLevel + +/** + * 注解模式初始化: 提供日志TAG 和日志等级快捷设置 + * + * @author Jesse + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE) +annotation class Logger(val tag: String = "NLogger", val level: LoggerLevel = LoggerLevel.WARN) \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/NLogger.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/NLogger.kt new file mode 100644 index 0000000..1212065 --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/NLogger.kt @@ -0,0 +1,330 @@ +package cn.jesse.nativelogger + +import cn.jesse.nativelogger.logger.LoggerLevel +import cn.jesse.nativelogger.logger.base.IFileLogger + +/** + * NLogger 静态工具类 + * + * @author Jesse + */ +object NLogger { + + /** + * 压缩日志文件 + * + * @params listener 子线程回调 + */ + @JvmStatic + fun zipLogs(listener: (succeed: Boolean, target: String) -> Unit) { + val fileLogger = NLoggerConfig.getInstance().getFileLogger() as IFileLogger? + + if (null == fileLogger) { + w("unexpected zip logs, file logger is null") + return + } + fileLogger.zipLogs(listener) + } + + @JvmStatic + fun i(msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().info(msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } else { + NLoggerConfig.getInstance().getFileLogger()?.info(msg) + } + } + + @JvmStatic + fun i(tag: String, msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().info(tag, msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.info(tag, msg) + } + + @JvmStatic + fun i(tag: String, format: String, arg: Any) { + NLoggerConfig.getInstance().getDefaultLogger().info(tag, format, arg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.info(tag, format, arg) + } + + @JvmStatic + fun i(tag: String, format: String, argA: Any, argB: Any) { + NLoggerConfig.getInstance().getDefaultLogger().info(tag, format, argA, argB) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.info(tag, format, argA, argB) + } + + @JvmStatic + fun i(tag: String, format: String, vararg args: Any) { + NLoggerConfig.getInstance().getDefaultLogger().info(tag, format, *args) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.info(tag, format, *args) + } + + @JvmStatic + fun i(tag: String, ex: Throwable) { + NLoggerConfig.getInstance().getDefaultLogger().info(tag, ex) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.info(tag, ex) + } + + @JvmStatic + fun d(msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().debug(msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.debug(msg) + } + + @JvmStatic + fun d(tag: String, msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().debug(tag, msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.debug(tag, msg) + } + + @JvmStatic + fun d(tag: String, format: String, arg: Any) { + NLoggerConfig.getInstance().getDefaultLogger().debug(tag, format, arg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.debug(tag, format, arg) + } + + @JvmStatic + fun d(tag: String, format: String, argA: Any, argB: Any) { + NLoggerConfig.getInstance().getDefaultLogger().debug(tag, format, argA, argB) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.debug(tag, format, argA, argB) + } + + @JvmStatic + fun d(tag: String, format: String, vararg args: Any) { + NLoggerConfig.getInstance().getDefaultLogger().debug(tag, format, *args) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.debug(tag, format, *args) + } + + @JvmStatic + fun d(tag: String, ex: Throwable) { + NLoggerConfig.getInstance().getDefaultLogger().debug(tag, ex) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.debug(tag, ex) + } + + @JvmStatic + fun w(msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().warn(msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.warn(msg) + } + + @JvmStatic + fun w(tag: String, msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().warn(tag, msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.warn(tag, msg) + } + + @JvmStatic + fun w(tag: String, format: String, arg: Any) { + NLoggerConfig.getInstance().getDefaultLogger().warn(tag, format, arg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.warn(tag, format, arg) + } + + @JvmStatic + fun w(tag: String, format: String, argA: Any, argB: Any) { + NLoggerConfig.getInstance().getDefaultLogger().warn(tag, format, argA, argB) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.warn(tag, format, argA, argB) + } + + @JvmStatic + fun w(tag: String, format: String, vararg args: Any) { + NLoggerConfig.getInstance().getDefaultLogger().warn(tag, format, *args) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.warn(tag, format, *args) + } + + @JvmStatic + fun w(tag: String, ex: Throwable) { + NLoggerConfig.getInstance().getDefaultLogger().warn(tag, ex) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.warn(tag, ex) + } + + @JvmStatic + fun e(msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().error(msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.error(msg) + } + + @JvmStatic + fun e(tag: String, msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().error(tag, msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.error(tag, msg) + } + + @JvmStatic + fun e(tag: String, format: String, arg: Any) { + NLoggerConfig.getInstance().getDefaultLogger().error(tag, format, arg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.error(tag, format, arg) + } + + @JvmStatic + fun e(tag: String, format: String, argA: Any, argB: Any) { + NLoggerConfig.getInstance().getDefaultLogger().error(tag, format, argA, argB) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.error(tag, format, argA, argB) + } + + @JvmStatic + fun e(tag: String, format: String, vararg args: Any) { + NLoggerConfig.getInstance().getDefaultLogger().error(tag, format, *args) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.error(tag, format, *args) + } + + @JvmStatic + fun e(tag: String, ex: Throwable) { + NLoggerConfig.getInstance().getDefaultLogger().error(tag, ex) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.error(tag, ex) + } + + @JvmStatic + fun json(level: LoggerLevel, msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().json(level, msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.json(level, msg) + } + + @JvmStatic + fun json(level: LoggerLevel, subTag: String, msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().json(level, subTag, msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.json(level, subTag, msg) + } + + @JvmStatic + fun log(level: LoggerLevel, subTag: String, msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().log(level, subTag, msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.log(level, subTag, msg) + } + + @JvmStatic + fun println(logLevel: LoggerLevel, subTag: String, formatJson: String) { + log(logLevel, subTag, formatJson) + } +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/NLoggerConfig.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/NLoggerConfig.kt new file mode 100644 index 0000000..ea97a54 --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/NLoggerConfig.kt @@ -0,0 +1,247 @@ +package cn.jesse.nativelogger + +import android.os.Environment +import android.text.TextUtils +import cn.jesse.nativelogger.formatter.SimpleFormatter +import cn.jesse.nativelogger.logger.AndroidLogger +import cn.jesse.nativelogger.logger.FileLogger +import cn.jesse.nativelogger.logger.LoggerLevel +import cn.jesse.nativelogger.logger.base.IFileLogger +import cn.jesse.nativelogger.logger.base.ILogger +import cn.jesse.nativelogger.util.CrashWatcher +import java.io.File +import java.util.logging.Formatter + +/** + * NLogger配置器 + */ +class NLoggerConfig { + @Volatile + private var builder: Builder? = null + private val defaultLogger: ILogger = AndroidLogger("NLogger") + @Volatile + private var fileLogger: ILogger? = null + + /** + * build builder to android logger & file logger & exception watcher + * @param builder + * @return + */ + @Synchronized + private fun build(builder: Builder): NLoggerConfig? { + if (builder.isCatchException) { + CrashWatcher.getInstance().init() + CrashWatcher.getInstance().setListener(builder.uncaughtExceptionListener) + } else { + CrashWatcher.getInstance().setListener(null) + } + defaultLogger.setTag(builder.tag) + defaultLogger.setLevel(builder.loggerLevel) + + if (!builder.isFileLoggerEnable) { + fileLogger = null + return mInstance + } + if (fileLogger == null) { + fileLogger = FileLogger(builder.tag) + } else { + fileLogger!!.setTag(builder.tag) + } + fileLogger!!.setLevel(builder.loggerLevel) + val iFileLogger = fileLogger as IFileLogger? + iFileLogger!!.setFilePathAndFormatter(builder.fileDirectory, builder.fileFormatter, builder.expiredPeriod) + return mInstance + } + + /** + * instance builder + * @return + */ + fun builder(): Builder { + if (builder == null) { + synchronized(NLoggerConfig::class.java) { + if (builder == null) { + builder = Builder() + } + } + } + return builder!! + } + + /** + * get android logger + * + */ + fun getDefaultLogger(): ILogger { + return this.defaultLogger + } + + /** + * get file logger + * + */ + fun getFileLogger(): ILogger? { + return this.fileLogger + } + + companion object { + @Volatile + private var mInstance: NLoggerConfig? = null + + /** + * get instance + * @return + */ + @JvmStatic + fun getInstance(): NLoggerConfig { + if (mInstance == null) { + synchronized(NLoggerConfig::class.java) { + if (mInstance == null) { + mInstance = NLoggerConfig() + } + } + } + return mInstance!! + } + + /** + * init NLoggerConfig from annotation + * @param obj + */ + @JvmStatic + fun init(obj: Any) { + val clazz = obj.javaClass + if (!clazz.isAnnotationPresent(Logger::class.java)) { + return + } + + val inject = clazz.getAnnotation(Logger::class.java) as Logger + val level: LoggerLevel = inject.level + + NLoggerConfig.getInstance() + .builder() + .tag(inject.tag) + .loggerLevel(level) + .build() + } + } + + inner class Builder { + var tag = NLoggerConfig::class.java.simpleName + var loggerLevel = LoggerLevel.WARN + var isCatchException = false + var uncaughtExceptionListener: ((thread: Thread?, ex: Throwable?) -> Unit)? = null + var isFileLoggerEnable = false + var fileDirectory = Environment.getExternalStorageDirectory().path + "/native.logs/" + var fileFormatter: Formatter = SimpleFormatter() + var expiredPeriod = 1 + + /** + * init builder , prepare env + * + */ + init { + val filePath = File(fileDirectory) + if (!filePath.exists()) { + filePath.mkdirs() + } + + uncaughtExceptionListener = { _, _ -> + android.os.Process.killProcess(android.os.Process.myPid()) + } + } + + /** + * set tag + * + * @throws IllegalArgumentException if the tag is null | empty + */ + fun tag(tag: String): Builder { + if (TextUtils.isEmpty(tag)) { + throw IllegalArgumentException("unexpected tag") + } + + this.tag = tag + return this + } + + /** + * catch uncaught exception + * + */ + fun catchException(enable: Boolean, listener: ((thread: Thread?, ex: Throwable?) -> Unit)?): Builder { + this.isCatchException = enable + this.uncaughtExceptionListener = listener + return this + } + + /** + * set the logger level + * + */ + fun loggerLevel(level: LoggerLevel): Builder { + this.loggerLevel = level + return this + } + + /** + * set file logger enable + * + */ + fun fileLogger(enable: Boolean): Builder { + this.isFileLoggerEnable = enable + return this + } + + /** + * set pack log period + * + * @throws IllegalArgumentException if the path is empty + */ + fun fileDirectory(path: String): Builder { + if (TextUtils.isEmpty(path)) { + throw IllegalArgumentException("unexpected path") + } + + val filePath = File(path) + if (!filePath.exists() && !filePath.mkdirs()) { + NLoggerConfig.getInstance().defaultLogger.error(tag, "can not make dir, please check permission") + } + + this.fileDirectory = path + return this + } + + /** + * set file logger formatter + * + * @throws IllegalArgumentException if the formatter is null + */ + fun fileFormatter(formatter: Formatter?): Builder { + if (null == formatter) { + throw IllegalArgumentException("unexpected file formatter") + } + + this.fileFormatter = formatter + return this + } + + /** + * set the period of file expired + * + * @throws IllegalArgumentException if the period <= 0 + */ + fun expiredPeriod(period: Int): Builder { + if (period <= 0) { + throw IllegalArgumentException("unexpected period : $period") + } + + expiredPeriod = period + return this + } + + fun build(): NLoggerConfig? { + return mInstance!!.build(this) + } + } +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/NLoggerError.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/NLoggerError.kt new file mode 100644 index 0000000..c008ac5 --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/NLoggerError.kt @@ -0,0 +1,38 @@ +package cn.jesse.nativelogger + +/** + * NLogger 通用错误类 + * + * @author Jesse + */ +class NLoggerError : Error { + + /** + * Constructs a new {@code Error} with the current stack trace and the + * specified detail message. + * + * @param detailMessage + * the detail message for this error. + */ + constructor(detailMessage : String) : super(detailMessage) + + /** + * Constructs a new {@code Error} with the current stack trace and the + * specified cause. + * + * @param throwable + * the cause of this error. + */ + constructor(throwable : Throwable) : super(throwable) + + /** + * Constructs a new {@code Error} with the current stack trace, the + * specified detail message and the specified cause. + * + * @param detailMessage + * the detail message for this error. + * @param throwable + * the cause of this error. + */ + constructor(detailMessage: String, throwable: Throwable) : super(detailMessage, throwable) +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/formatter/SimpleFormatter.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/formatter/SimpleFormatter.kt new file mode 100644 index 0000000..808477a --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/formatter/SimpleFormatter.kt @@ -0,0 +1,34 @@ +package cn.jesse.nativelogger.formatter + +import java.text.MessageFormat +import java.util.* +import java.util.logging.Formatter +import java.util.logging.LogRecord + +/** + * 文件日志默认格式化工具 + * + * @author Jesse + */ +class SimpleFormatter : Formatter() { + + private val LINE_SEPARATOR = "\n" + + /** + * Converts a object into a human readable string + * representation. + * + */ + override fun format(r: LogRecord): String { + val sb = StringBuilder() + sb.append(MessageFormat.format("{0,date,yyyy-MM-dd HH:mm:ss} ", *arrayOf(Date(r.millis)))) + sb.append(r.loggerName).append(": ") + sb.append(r.level.name).append(LINE_SEPARATOR) + sb.append(formatMessage(r)).append(LINE_SEPARATOR) + if (r.thrown != null) { + sb.append("Throwable occurred: ") + sb.append(TagFormatter.format(r.thrown)) + } + return sb.toString() + } +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/formatter/TagFormatter.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/formatter/TagFormatter.kt new file mode 100644 index 0000000..1e3c52c --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/formatter/TagFormatter.kt @@ -0,0 +1,95 @@ +package cn.jesse.nativelogger.formatter + +import android.util.Log +import cn.jesse.nativelogger.util.CloseUtil +import java.io.PrintWriter +import java.io.StringWriter + +/** + * 各种格式化日志工具 + * + * @author Jesse + */ +object TagFormatter { + private val RESULT_UNEXPECTED_FORMAT = "unexpected format" + private val WITH = " with " + + /** + * 按照 %s : %s格式化日志 + * + * @param subTag 子Tag + * @param msg 日志信息 + */ + fun format(subTag: String, msg: String) = try { + String.format("%s : %s", subTag, msg) + } catch (e: Exception) { + Log.e(subTag, RESULT_UNEXPECTED_FORMAT, e) + RESULT_UNEXPECTED_FORMAT + } + + /** + * 扩展日志格式 %s $子格式 + * @param subTag 子Tag + * @param format 子格式 + * @param arg 被子格式格式的对象 + */ + fun format(subTag: String, format: String, arg: Any) = try { + String.format("%s : $format", subTag, arg) + } catch (e: Exception) { + Log.e(subTag, RESULT_UNEXPECTED_FORMAT, e) + RESULT_UNEXPECTED_FORMAT + WITH + format + } + + /** + * 扩展日志格式 %s $子格式 + * @param subTag 子Tag + * @param format 子格式 + * @param argA 被子格式格式的对象A + * @param argB 被子格式格式的对象B + */ + fun format(subTag: String, format: String, argA: Any, argB: Any) = try { + String.format("%s : $format", subTag, argA, argB) + } catch (e: Exception) { + Log.e(subTag, RESULT_UNEXPECTED_FORMAT, e) + RESULT_UNEXPECTED_FORMAT + WITH + format + } + + /** + * 扩展日志格式 %s $子格式 + * @param subTag 子Tag + * @param format 子格式 + * @param args 可变参数组 + */ + fun format(subTag: String, format: String, vararg args: Any) = try { + String.format("%s : %s", subTag, String.format(format, *args)) + } catch (e: Exception) { + Log.e(subTag, RESULT_UNEXPECTED_FORMAT, e) + RESULT_UNEXPECTED_FORMAT + WITH + format + } + + /** + * 格式化异常信息 + * + * @param t 异常 + */ + fun format(t: Throwable?): String { + var result = "" + + if (null == t) { + return result + } + + var pw: PrintWriter? = null + + try { + val sw = StringWriter() + pw = PrintWriter(sw) + t.printStackTrace(pw) + result = sw.toString() + } finally { + CloseUtil.close(pw) + } + + return result + } +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/logger/AndroidLogger.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/AndroidLogger.kt new file mode 100644 index 0000000..4864349 --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/AndroidLogger.kt @@ -0,0 +1,251 @@ +package cn.jesse.nativelogger.logger + +import android.util.Log +import cn.jesse.nativelogger.formatter.TagFormatter +import cn.jesse.nativelogger.logger.base.AbstractLogger + +/** + * Android 日志管理器实现 + * + * @author Jesse + */ +class AndroidLogger(tag: String) : AbstractLogger(tag) { + private var debugEnable = true + private var infoEnable = true + private var warningEnable = true + private var errorEnable = true + + override fun setLevel(level: LoggerLevel) { + when (level) { + LoggerLevel.DEBUG -> { + // default + } + LoggerLevel.INFO -> { + debugEnable = false + } + LoggerLevel.WARN -> { + debugEnable = false + infoEnable = false + } + LoggerLevel.ERROR -> { + debugEnable = false + infoEnable = false + warningEnable = false + } + LoggerLevel.OFF -> { + debugEnable = false + infoEnable = false + warningEnable = false + errorEnable = false + } + } + } + + override fun isDebugEnabled(): Boolean { + return debugEnable + } + + override fun debug(msg: String) { + if (!isDebugEnabled()) { + return + } + + Log.d(mTag, msg) + } + + override fun debug(subTag: String, msg: String) { + if (!isDebugEnabled()) { + return + } + + Log.d(mTag, TagFormatter.format(subTag, msg)) + } + + override fun debug(subTag: String, format: String, arg: Any) { + if (!isDebugEnabled()) { + return + } + + Log.d(mTag, TagFormatter.format(subTag, format, arg)) + } + + override fun debug(subTag: String, format: String, argA: Any, argB: Any) { + if (!isDebugEnabled()) { + return + } + + Log.d(mTag, TagFormatter.format(subTag, format, argA, argB)) + } + + override fun debug(subTag: String, format: String, vararg arguments: Any) { + if (!isDebugEnabled()) { + return + } + + Log.d(mTag, TagFormatter.format(subTag, format, *arguments)) + } + + override fun debug(subTag: String, t: Throwable) { + if (!isDebugEnabled()) { + return + } + + Log.d(mTag, subTag + " " + TagFormatter.format(t)) + } + + override fun isInfoEnabled(): Boolean { + return infoEnable + } + + override fun info(msg: String) { + if (!isInfoEnabled()) { + return + } + + Log.i(mTag, msg) + } + + override fun info(subTag: String, msg: String) { + if (!isInfoEnabled()) { + return + } + + Log.i(mTag, TagFormatter.format(subTag, msg)) + } + + override fun info(subTag: String, format: String, arg: Any) { + if (!isInfoEnabled()) { + return + } + + Log.i(mTag, TagFormatter.format(subTag, format, arg)) + } + + override fun info(subTag: String, format: String, argA: Any, argB: Any) { + if (!isInfoEnabled()) { + return + } + + Log.i(mTag, TagFormatter.format(subTag, format, argA, argB)) + } + + override fun info(subTag: String, format: String, vararg arguments: Any) { + if (!isInfoEnabled()) { + return + } + + Log.i(mTag, TagFormatter.format(subTag, format, *arguments)) + } + + override fun info(subTag: String, t: Throwable) { + if (!isInfoEnabled()) { + return + } + + Log.i(mTag, subTag + " " + TagFormatter.format(t)) + } + + override fun isWarnEnabled(): Boolean { + return warningEnable + } + + override fun warn(msg: String) { + if (!isWarnEnabled()) { + return + } + + Log.w(mTag, msg) + } + + override fun warn(subTag: String, msg: String) { + if (!isWarnEnabled()) { + return + } + + Log.w(mTag, TagFormatter.format(subTag, msg)) + } + + override fun warn(subTag: String, format: String, arg: Any) { + if (!isWarnEnabled()) { + return + } + + Log.w(mTag, TagFormatter.format(subTag, format, arg)) + } + + override fun warn(subTag: String, format: String, vararg arguments: Any) { + if (!isWarnEnabled()) { + return + } + + Log.w(mTag, TagFormatter.format(subTag, format, *arguments)) + } + + override fun warn(subTag: String, format: String, argA: Any, argB: Any) { + if (!isWarnEnabled()) { + return + } + + Log.w(mTag, TagFormatter.format(subTag, format, argA, argB)) + } + + override fun warn(subTag: String, t: Throwable) { + if (!isWarnEnabled()) { + return + } + + Log.w(mTag, subTag + " " + TagFormatter.format(t)) + } + + override fun isErrorEnabled(): Boolean { + return errorEnable + } + + override fun error(msg: String) { + if (!isErrorEnabled()) { + return + } + + Log.e(mTag, msg) + } + + override fun error(subTag: String, msg: String) { + if (!isErrorEnabled()) { + return + } + + Log.e(mTag, TagFormatter.format(subTag, msg)) + } + + override fun error(subTag: String, format: String, arg: Any) { + if (!isErrorEnabled()) { + return + } + + Log.e(mTag, TagFormatter.format(subTag, format, arg)) + } + + override fun error(subTag: String, format: String, argA: Any, argB: Any) { + if (!isErrorEnabled()) { + return + } + + Log.e(mTag, TagFormatter.format(subTag, format, argA, argB)) + } + + override fun error(subTag: String, format: String, vararg arguments: Any) { + if (!isErrorEnabled()) { + return + } + + Log.e(mTag, TagFormatter.format(subTag, format, *arguments)) + } + + override fun error(subTag: String, t: Throwable) { + if (!isErrorEnabled()) { + return + } + + Log.e(mTag, subTag + " " + TagFormatter.format(t)) + } +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/logger/FileLogger.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/FileLogger.kt new file mode 100644 index 0000000..fee16d7 --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/FileLogger.kt @@ -0,0 +1,336 @@ +package cn.jesse.nativelogger.logger + +import android.os.Handler +import android.os.HandlerThread +import cn.jesse.nativelogger.formatter.TagFormatter +import cn.jesse.nativelogger.logger.base.AbstractLogger +import cn.jesse.nativelogger.logger.base.IFileLogger +import cn.jesse.nativelogger.util.DateUtil +import cn.jesse.nativelogger.util.ZipUtil +import java.io.File +import java.io.IOException +import java.util.logging.* + +/** + * 文件日志管理器实现 + */ +class FileLogger(tag: String) : AbstractLogger(tag), IFileLogger { + internal val logger: Logger + private lateinit var logDir: String + private lateinit var formatter: Formatter + private var expiredPeriod: Int = 0 + + private var handler: Handler + + init { + val fileLoggerThread = HandlerThread(FileLogger::class.java.simpleName) + fileLoggerThread.start() + handler = Handler(fileLoggerThread.looper) + + this.logger = Logger.getLogger(tag) + logger.useParentHandlers = false + } + + /* + * Java文件操作 获取不带扩展名的文件名 + * + * Created on: 2011-8-2 + * Author: blueeagle + */ + fun getFileNameNoEx(filename: String): String { + if (filename.isNotEmpty()) { + val dot = filename.indexOf('.') + if (dot > -1 && dot < filename.length) { + return filename.substring(0, dot) + } + } + return filename + } + + override fun setFilePathAndFormatter(directory: String, formatter: Formatter, expiredPeriod: Int) { + this.logDir = directory + this.formatter = formatter + this.expiredPeriod = expiredPeriod + + if (!logDir.endsWith("/")) + logDir += "/" + val f = File(logDir) + if (f.isDirectory && !f.exists()) { + f.mkdirs() + } + DateUtil.DeleteFileDate(expiredPeriod, f) + val file = File(logDir + DateUtil.getCurrentDate()) + val fh: FileHandler + try { + fh = FileHandler(file.toString(), true) + fh.formatter = formatter + logger.addHandler(fh) + } catch (e: IOException) { + //unused + error(this.javaClass.simpleName, e) + } + handler.post { ZipUtil.getSuitableFilesWithClear(directory, expiredPeriod) } + } + + override fun logDirectory(): String { + return this.logDir + } + + override fun fileFormatter(): Formatter { + return this.formatter + } + + override fun expiredPeriod(): Int { + return this.expiredPeriod + } + + override fun zipLogs(listener: (succeed: Boolean, target: String) -> Unit) { + handler.post { + var result = false + val targetZipFileName = logDir + DateUtil.getCurrentDate() + ZipUtil.SUFFIX_ZIP + try { + val zipFile = File(targetZipFileName) + if (zipFile.exists() && !zipFile.delete()) { + error(tag(), "can not delete exist zip file!") + } + result = ZipUtil.zipFiles( + ZipUtil.getSuitableFilesWithClear(logDir, expiredPeriod), + zipFile, DateUtil.getCurrentDate() + ) + } catch (e: Exception) { + error(tag(), e) + } + + listener(result, targetZipFileName) + } + } + + override fun setLevel(level: LoggerLevel) { + when (level) { + LoggerLevel.OFF -> logger.level = Level.OFF + LoggerLevel.ERROR -> logger.level = Level.SEVERE + LoggerLevel.WARN -> logger.level = Level.WARNING + LoggerLevel.INFO -> logger.level = Level.INFO + LoggerLevel.DEBUG -> logger.level = Level.FINE + } + } + + @Synchronized + private fun log(level: Level, msg: String, t: Throwable?) { + val record = LogRecord(level, msg) + record.sourceClassName=tag() + record.loggerName = tag() + record.thrown = t + logger.log(record) + } + + override fun isDebugEnabled(): Boolean { + return logger.isLoggable(Level.FINE) + } + + override fun debug(msg: String) { + if (!isDebugEnabled()) { + return + } + + handler.post { log(Level.FINE, msg, null) } + } + + override fun debug(subTag: String, msg: String) { + if (!isDebugEnabled()) { + return + } + + handler.post { log(Level.FINE, TagFormatter.format(subTag, msg), null) } + } + + override fun debug(subTag: String, format: String, arg: Any) { + if (!isDebugEnabled()) { + return + } + + handler.post { log(Level.FINE, TagFormatter.format(subTag, format, arg), null) } + } + + override fun debug(subTag: String, format: String, argA: Any, argB: Any) { + if (!isDebugEnabled()) { + return + } + + handler.post { log(Level.FINE, TagFormatter.format(subTag, format, argA, argB), null) } + } + + override fun debug(subTag: String, format: String, vararg arguments: Any) { + if (!isDebugEnabled()) { + return + } + + handler.post { log(Level.FINE, TagFormatter.format(subTag, format, *arguments), null) } + } + + override fun debug(subTag: String, t: Throwable) { + if (!isDebugEnabled()) { + return + } + + log(Level.FINE, subTag, t) + } + + override fun isInfoEnabled(): Boolean { + return logger.isLoggable(Level.INFO) + } + + override fun info(msg: String) { + if (!isInfoEnabled()) { + return + } + + handler.post { log(Level.INFO, msg, null) } + } + + override fun info(subTag: String, msg: String) { + if (!isInfoEnabled()) { + return + } + + handler.post { log(Level.INFO, TagFormatter.format(subTag, msg), null) } + } + + override fun info(subTag: String, format: String, arg: Any) { + if (!isInfoEnabled()) { + return + } + + handler.post { log(Level.INFO, TagFormatter.format(subTag, format, arg), null) } + } + + override fun info(subTag: String, format: String, argA: Any, argB: Any) { + if (!isInfoEnabled()) { + return + } + + handler.post { log(Level.INFO, TagFormatter.format(subTag, format, argA, argB), null) } + } + + override fun info(subTag: String, format: String, vararg arguments: Any) { + if (!isInfoEnabled()) { + return + } + + handler.post { log(Level.INFO, TagFormatter.format(subTag, format, *arguments), null) } + } + + override fun info(subTag: String, t: Throwable) { + if (!isInfoEnabled()) { + return + } + + log(Level.INFO, subTag, t) + } + + + override fun isWarnEnabled(): Boolean { + return logger.isLoggable(Level.WARNING) + } + + override fun warn(msg: String) { + if (!isWarnEnabled()) { + return + } + + handler.post { log(Level.WARNING, msg, null) } + } + + override fun warn(subTag: String, msg: String) { + if (!isWarnEnabled()) { + return + } + + handler.post { log(Level.WARNING, TagFormatter.format(subTag, msg), null) } + } + + override fun warn(subTag: String, format: String, arg: Any) { + if (!isWarnEnabled()) { + return + } + + handler.post { log(Level.WARNING, TagFormatter.format(subTag, format, arg), null) } + } + + override fun warn(subTag: String, format: String, vararg arguments: Any) { + if (!isWarnEnabled()) { + return + } + + handler.post { log(Level.WARNING, TagFormatter.format(subTag, format, *arguments), null) } + } + + override fun warn(subTag: String, format: String, argA: Any, argB: Any) { + if (!isWarnEnabled()) { + return + } + + handler.post { log(Level.WARNING, TagFormatter.format(subTag, format, argA, argB), null) } + } + + override fun warn(subTag: String, t: Throwable) { + if (!isWarnEnabled()) { + return + } + + log(Level.WARNING, subTag, t) + } + + + override fun isErrorEnabled(): Boolean { + return logger.isLoggable(Level.SEVERE) + } + + override fun error(msg: String) { + if (!isErrorEnabled()) { + return + } + + handler.post { log(Level.SEVERE, msg, null) } + } + + override fun error(subTag: String, msg: String) { + if (!isErrorEnabled()) { + return + } + + handler.post { log(Level.SEVERE, TagFormatter.format(subTag, msg), null) } + } + + override fun error(subTag: String, format: String, arg: Any) { + if (!isErrorEnabled()) { + return + } + + handler.post { log(Level.SEVERE, TagFormatter.format(subTag, format, arg), null) } + } + + override fun error(subTag: String, format: String, argA: Any, argB: Any) { + if (!isErrorEnabled()) { + return + } + + handler.post { log(Level.SEVERE, TagFormatter.format(subTag, format, argA, argB), null) } + } + + override fun error(subTag: String, format: String, vararg arguments: Any) { + if (!isErrorEnabled()) { + return + } + + handler.post { log(Level.SEVERE, TagFormatter.format(subTag, format, *arguments), null) } + } + + override fun error(subTag: String, t: Throwable) { + if (!isErrorEnabled()) { + return + } + + log(Level.SEVERE, subTag, t) + } +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/logger/LoggerLevel.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/LoggerLevel.kt new file mode 100644 index 0000000..6bed0cb --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/LoggerLevel.kt @@ -0,0 +1,14 @@ +package cn.jesse.nativelogger.logger + +/** + * 日志等级 枚举 + * + * @author Jesse + */ +enum class LoggerLevel { + DEBUG, + INFO, + WARN, + ERROR, + OFF +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/AbstractLogger.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/AbstractLogger.kt new file mode 100644 index 0000000..a5a16a3 --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/AbstractLogger.kt @@ -0,0 +1,172 @@ +package cn.jesse.nativelogger.logger.base + +import cn.jesse.nativelogger.NLoggerError +import cn.jesse.nativelogger.logger.LoggerLevel +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject + +/** + * Logger抽象类, 提供部分默认逻辑 + * + * @author Jesse + */ +abstract class AbstractLogger(tag: String) : ILogger { + protected open var mTag: String = "AbstractLogger" + + init { + mTag = tag + } + + override fun tag(): String { + return mTag + } + + + override fun setTag(tag: String) { + this.mTag = tag + } + + override fun isEnabled(level: LoggerLevel): Boolean { + return when (level) { + LoggerLevel.DEBUG -> isDebugEnabled() + LoggerLevel.INFO -> isInfoEnabled() + LoggerLevel.WARN -> isWarnEnabled() + LoggerLevel.ERROR -> isErrorEnabled() + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + override fun log(level: LoggerLevel, subTag: String, t: Throwable) { + when (level) { + LoggerLevel.DEBUG -> debug(subTag, t) + LoggerLevel.INFO -> info(subTag, t) + LoggerLevel.WARN -> warn(subTag, t) + LoggerLevel.ERROR -> error(subTag, t) + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + override fun log(level: LoggerLevel, msg: String) { + when (level) { + LoggerLevel.DEBUG -> debug(msg) + LoggerLevel.INFO -> info(msg) + LoggerLevel.WARN -> warn(msg) + LoggerLevel.ERROR -> error(msg) + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + override fun log(level: LoggerLevel, subTag: String, msg: String) { + when (level) { + LoggerLevel.DEBUG -> debug(subTag, msg) + LoggerLevel.INFO -> info(subTag, msg) + LoggerLevel.WARN -> warn(subTag, msg) + LoggerLevel.ERROR -> error(subTag, msg) + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + override fun log(level: LoggerLevel, subTag: String, format: String, arg: Any) { + when (level) { + LoggerLevel.DEBUG -> debug(subTag, format, arg) + LoggerLevel.INFO -> info(subTag, format, arg) + LoggerLevel.WARN -> warn(subTag, format, arg) + LoggerLevel.ERROR -> error(subTag, format, arg) + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + override fun log(level: LoggerLevel, subTag: String, format: String, argA: Any, argB: Any) { + when (level) { + LoggerLevel.DEBUG -> debug(subTag, format, argA, argB) + LoggerLevel.INFO -> info(subTag, format, argA, argB) + LoggerLevel.WARN -> warn(subTag, format, argA, argB) + LoggerLevel.ERROR -> error(subTag, format, argA, argB) + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + override fun log(level: LoggerLevel, subTag: String, format: String, vararg arguments: Any) { + when (level) { + LoggerLevel.DEBUG -> debug(subTag, format, *arguments) + LoggerLevel.INFO -> info(subTag, format, *arguments) + LoggerLevel.WARN -> warn(subTag, format, *arguments) + LoggerLevel.ERROR -> error(subTag, format, *arguments) + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + override fun json(level: LoggerLevel, msg: String) { + if (!isEnabled(level)) { + return + } + + val json = parseJson(msg) + + when (level) { + LoggerLevel.DEBUG -> debug(json) + LoggerLevel.INFO -> info(json) + LoggerLevel.WARN -> warn(json) + LoggerLevel.ERROR -> error(json) + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + override fun json(level: LoggerLevel, subTag: String, msg: String) { + if (!isEnabled(level)) + return + + val json = parseJson(msg) + + when (level) { + LoggerLevel.DEBUG -> debug(subTag, json) + LoggerLevel.INFO -> info(subTag, json) + LoggerLevel.WARN -> warn(subTag, json) + LoggerLevel.ERROR -> error(subTag, json) + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + /** + * format json + * as: + *

+     * {
+     * "query": "Pizza",
+     * "locations": [
+     * 94043,
+     * 90210
+     * ]
+     * }
+ * @param json + * @return + */ + private fun parseJson(json: String?): String { + if (null == json) { + return ERROR_FORMAT + } + + try { + if (json.startsWith("{")) { + val jsonObject = JSONObject(json) + return jsonObject.toString(JSON_INDENT) + } + if (json.startsWith("[")) { + val jsonArray = JSONArray(json) + return jsonArray.toString(JSON_INDENT) + } + return ERROR_FORMAT + } catch (e: JSONException) { + error(this.javaClass.simpleName, e) + return ERROR_FORMAT + } + + } + + companion object { + private val ERROR_FORMAT = "unexpected format" + private val ERROR_LEVEL = "unexpected LoggerLevel" + private val JSON_INDENT = 2 + } +} diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/IFileLogger.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/IFileLogger.kt new file mode 100644 index 0000000..28e931c --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/IFileLogger.kt @@ -0,0 +1,43 @@ +package cn.jesse.nativelogger.logger.base + +import java.util.logging.Formatter + +/** + * 文件日志管理器接口 + * + * @author Jesse + */ +interface IFileLogger { + + /** + * 设置文件路径, 日志格式工具, 和日志保留时间 + * + * @param directory 路径 + * @param formatter 日志格式化工具 + * @param expiredPeriod 日志过期时间, 单位为天 + */ + fun setFilePathAndFormatter(directory: String, formatter: Formatter, expiredPeriod: Int) + + /** + * 获取日志文件存放路径 + */ + fun logDirectory(): String + + /** + * 获取文件日志格式化工具 + */ + fun fileFormatter(): Formatter + + /** + * 获取日志过期时间 + */ + fun expiredPeriod(): Int + + /** + * 压缩日志 + * + * @param listener 压缩结果回调. succeed 是否成功, target 压缩包路径 + * + */ + fun zipLogs(listener: (succeed: Boolean, target: String) -> Unit) +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/ILogger.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/ILogger.kt new file mode 100644 index 0000000..df4d969 --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/ILogger.kt @@ -0,0 +1,333 @@ +package cn.jesse.nativelogger.logger.base + +import cn.jesse.nativelogger.logger.LoggerLevel + +/** + * Logger 接口 + * + * @author Jesse + */ +interface ILogger { + + /** + * 设置日志tag + * + * @param tag TAG + */ + fun setTag(tag: String) + + /** + * 获取日志tag + */ + fun tag(): String + + /** + * 设置日志等级 + * + * @param level 等级枚举 + */ + fun setLevel(level: LoggerLevel) + + /** + * 判断是否可以debug + */ + fun isDebugEnabled(): Boolean + + /** + * debug 日志 + * + * @param msg 信息 + */ + fun debug(msg: String) + + /** + * debug 日志 + * + * @param subTag 子TAG + * @param msg 信息 + */ + fun debug(subTag: String, msg: String) + + /** + * debug 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arg 被格式化对象 + */ + fun debug(subTag: String, format: String, arg: Any) + + /** + * debug 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param argA 被格式化对象A + * @param argB 被格式化对象B + */ + fun debug(subTag: String, format: String, argA: Any, argB: Any) + + /** + * debug 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arguments 可变参数组 + */ + fun debug(subTag: String, format: String, vararg arguments: Any) + + /** + * debug 日志 + * + * @param subTag 子TAG + * @param t 异常信息 + */ + fun debug(subTag: String, t: Throwable) + + /** + * 判断是否可以info + */ + fun isInfoEnabled(): Boolean + + /** + * info 日志 + * + * @param msg 信息 + */ + fun info(msg: String) + + /** + * info 日志 + * + * @param subTag 子TAG + * @param msg 信息 + */ + fun info(subTag: String, msg: String) + + /** + * info 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arg 被格式化对象 + */ + fun info(subTag: String, format: String, arg: Any) + + /** + * info 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param argA 被格式化对象A + * @param argB 被格式化对象B + */ + fun info(subTag: String, format: String, argA: Any, argB: Any) + + /** + * info 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arguments 可变参数组 + */ + fun info(subTag: String, format: String, vararg arguments: Any) + + /** + * info 日志 + * + * @param subTag 子TAG + * @param t 异常信息 + */ + fun info(subTag: String, t: Throwable) + + /** + * 判断是否可以 warn + */ + fun isWarnEnabled(): Boolean + + /** + * warn 日志 + * + * @param msg 信息 + */ + fun warn(msg: String) + + /** + * warn 日志 + * + * @param subTag 子TAG + * @param msg 信息 + */ + fun warn(subTag: String, msg: String) + + /** + * warn 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arg 被格式化对象 + */ + fun warn(subTag: String, format: String, arg: Any) + + /** + * warn 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arguments 可变参数组 + */ + fun warn(subTag: String, format: String, vararg arguments: Any) + + /** + * warn 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param argA 被格式化对象A + * @param argB 被格式化对象B + */ + fun warn(subTag: String, format: String, argA: Any, argB: Any) + + /** + * warn 日志 + * + * @param subTag 子TAG + * @param t 异常信息 + */ + fun warn(subTag: String, t: Throwable) + + /** + * 判断是否可以 error + */ + fun isErrorEnabled(): Boolean + + /** + * error 日志 + * + * @param msg 信息 + */ + fun error(msg: String) + + /** + * error 日志 + * + * @param subTag 子TAG + * @param msg 信息 + */ + fun error(subTag: String, msg: String) + + /** + * error 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arg 被格式化对象 + */ + fun error(subTag: String, format: String, arg: Any) + + /** + * error 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param argA 被格式化对象A + * @param argB 被格式化对象B + */ + fun error(subTag: String, format: String, argA: Any, argB: Any) + + /** + * error 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arguments 可变参数组 + */ + fun error(subTag: String, format: String, vararg arguments: Any) + + /** + * error 日志 + * + * @param subTag 子TAG + * @param t 异常信息 + */ + fun error(subTag: String, t: Throwable) + + /** + * 判断日志管理器是否开启 + */ + fun isEnabled(level: LoggerLevel): Boolean + + /** + * 根据日志等级打印对应信息 + * + * @param level 日志等级 + * @param msg 日志信息 + */ + fun log(level: LoggerLevel, msg: String) + + /** + * 根据日志等级打印对应信息 + * + * @param level 日志等级 + * @param subTag 子TAG + * @param msg 日志信息 + */ + fun log(level: LoggerLevel, subTag: String, msg: String) + + /** + * 根据日志等级打印对应信息 + * + * @param level 日志等级 + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arg 被格式化对象 + */ + fun log(level: LoggerLevel, subTag: String, format: String, arg: Any) + + /** + * 根据日志等级打印对应信息 + * + * @param level 日志等级 + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param argA 被格式化对象A + * @param argB 被格式化对象B + */ + fun log(level: LoggerLevel, subTag: String, format: String, argA: Any, argB: Any) + + /** + * 根据日志等级打印对应信息 + * + * @param level 日志等级 + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arguments 可变参数组 + */ + fun log(level: LoggerLevel, subTag: String, format: String, vararg arguments: Any) + + /** + * 根据日志等级打印对应信息 + * + * @param level 日志等级 + * @param subTag 子TAG + * @param t 异常信息 + */ + fun log(level: LoggerLevel, subTag: String, t: Throwable) + + /** + * 格式化json日志 + * + * @param level 日志等级 + * @param msg json串 + */ + fun json(level: LoggerLevel, msg: String) + + /** + * 格式化json日志 + * + * @param level 日志等级 + * @param subTag 子TAG + * @param msg json串 + */ + fun json(level: LoggerLevel, subTag: String, msg: String) +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/util/CloseUtil.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/util/CloseUtil.kt new file mode 100644 index 0000000..b723976 --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/util/CloseUtil.kt @@ -0,0 +1,27 @@ +package cn.jesse.nativelogger.util + +import java.io.Closeable +import java.io.IOException + +import cn.jesse.nativelogger.NLogger + +/** + * 关闭各种 + * + * @author Jesse + */ +object CloseUtil { + + /** + * 关闭Closeable对象 + * + * @param closeable 对象 + */ + fun close(closeable: Closeable?) { + try { + closeable?.close() + } catch (e: IOException) { + NLogger.e("close", e) + } + } +} diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/util/CrashWatcher.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/util/CrashWatcher.kt new file mode 100644 index 0000000..7c2255c --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/util/CrashWatcher.kt @@ -0,0 +1,45 @@ +package cn.jesse.nativelogger.util + +/** + * 监听全局异常, 捕获并回调 + * + * @author Jesse + */ +class CrashWatcher private constructor() : Thread.UncaughtExceptionHandler { + + private var mDefaultHandler: Thread.UncaughtExceptionHandler? = null + private var listener: ((thread: Thread?, ex: Throwable?) -> Unit)? = null + + fun init() { + if (null != mDefaultHandler) { + return + } + + mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler() + Thread.setDefaultUncaughtExceptionHandler(this) + } + + fun setListener(listener: ((thread: Thread?, ex: Throwable?) -> Unit)?) { + this.listener = listener + } + + override fun uncaughtException(t: Thread, e: Throwable) { + listener?.invoke(t, e) + } + + companion object { + private var mInstance: CrashWatcher? = null + + fun getInstance(): CrashWatcher { + if (null == mInstance) { + synchronized(CrashWatcher::class.java) { + if (null == mInstance) { + mInstance = CrashWatcher() + } + } + } + + return mInstance!! + } + } +} diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/util/DateUtil.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/util/DateUtil.kt new file mode 100644 index 0000000..491bb0a --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/util/DateUtil.kt @@ -0,0 +1,119 @@ +package cn.jesse.nativelogger.util + +import java.io.File +import java.text.ParseException +import java.text.SimpleDateFormat +import java.util.* + +/** + * 日期工具类 + * + * @author Jesse + */ +object DateUtil { + private val TEMPLATE_DATE = "yyyy-MM-dd" + + /** + * 以yyyy-MM-dd格式获取当前时间 + */ + fun getCurrentDate(): String { + val sdf = SimpleDateFormat(TEMPLATE_DATE) + return sdf.format(System.currentTimeMillis()) + } + + + fun DeleteFileDate(days: Int, file: File) { + var start = getCurrentDateBefor(days) + var end = getCurrentDate() + val sdf = SimpleDateFormat(TEMPLATE_DATE) + var dStart: Date? + var dEnd: Date? + try { + dStart = sdf.parse(start) + dEnd = sdf.parse(end) + val dateList = findDates(dStart!!, dEnd!!) + var f = file.listFiles() + if (f != null && !f.isEmpty()) + for (f1 in f) { + var flag = false + for (date in dateList) { + if (f1.name.startsWith(sdf.format(date))) { + flag=true + } + } + if (!flag){ + f1.delete() + } + } + } catch (e: ParseException) { + e.printStackTrace() + } catch (e1: Exception) { + e1.printStackTrace() + } + } + + //JAVA获取某段时间内的所有日期 + fun findDates(dStart: Date, dEnd: Date): List { + val cStart = Calendar.getInstance() + cStart.time = dStart + val dateList = ArrayList() + //别忘了,把起始日期加上 + dateList.add(dStart) + // 此日期是否在指定日期之后 + while (dEnd.after(cStart.time)) { + // 根据日历的规则,为给定的日历字段添加或减去指定的时间量 + cStart.add(Calendar.DAY_OF_MONTH, 1) + dateList.add(cStart.time) + } + return dateList + } + + fun isValidDate(str: String): Boolean { + var convertSuccess = true + // 指定日期格式为四位年/两位月份/两位日期,注意yyyy/MM/dd区分大小写; + val format = SimpleDateFormat(TEMPLATE_DATE) + try { + // 设置lenient为false. + // 否则SimpleDateFormat会比较宽松地验证日期,比如2007/02/29会被接受,并转换成2007/03/01 + format.isLenient = false + format.parse(str) + } catch (e: ParseException) { + // e.printStackTrace(); + // 如果throw java.text.ParseException或者NullPointerException,就说明格式不对 + convertSuccess = false + } + + return convertSuccess + } + + fun getCurrentDateBefor(days: Int): String { + val calendar = Calendar.getInstance() +//今天 + System.out.println(calendar.getTime()) +//10天前 + calendar.add(Calendar.DATE, -days) + System.out.println(calendar.getTime()) + val sdf = SimpleDateFormat(TEMPLATE_DATE) + return sdf.format(calendar.time) + } + + /** + * @Title: isInDate + * @Description: 判断一个时间段(YYYY-MM-DD)是否在一个区间 + * @param @param date + * @param @param strDateBegin + * @param @param strDateEnd + * @param @return 设定文件 + * @return boolean 返回类型 + * @throws + */ + fun isInDate(strDate: String, strDateBegin: String, strDateEnd: String): Boolean { + // 截取当前时间年月日 转成整型 + val tempDate = Integer.parseInt(strDate.replace("-", "")) + // 截取开始时间年月日 转成整型 + val tempDateBegin = Integer.parseInt(strDateBegin.replace("-", "")) + // 截取结束时间年月日 转成整型 + val tempDateEnd = Integer.parseInt(strDateEnd.replace("-", "")) + return (tempDate in tempDateBegin..tempDateEnd) + } +} diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/util/ZipUtil.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/util/ZipUtil.kt new file mode 100644 index 0000000..ef4948d --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/util/ZipUtil.kt @@ -0,0 +1,119 @@ +package cn.jesse.nativelogger.util + +import android.text.TextUtils +import cn.jesse.nativelogger.NLogger +import java.io.* +import java.nio.charset.Charset +import java.util.zip.ZipEntry +import java.util.zip.ZipOutputStream + +/** + * 压缩工具 + * + * @author Jesse + */ +object ZipUtil { + val SUFFIX_ZIP = ".zip" + private val BUFF_SIZE = 1024 * 1024 + private val SUFFIX_LOCK = ".lck" + + /** + * get suitable files from path depend on pack num ,clear redundant files + * + * @param path source files path + * @param expiredPeriod expired file period + */ + fun getSuitableFilesWithClear(path: String, expiredPeriod: Int): Collection { + val files = ArrayList() + val file = File(path) + val subFile = file.listFiles() + if (subFile != null && !subFile.isEmpty()) { + for (item in subFile) { + if (item.isDirectory) { + continue + } + + val expired = expiredPeriod * 24 * 60 * 60 * 1000L + if ((System.currentTimeMillis() - item.lastModified() > expired) && !item.delete()) { + NLogger.e("can not delete expired file " + item.name) + } + + if (item.name.endsWith(SUFFIX_LOCK) || item.name.endsWith(SUFFIX_ZIP)) { + continue + } + + files.add(item) + + } + } + return files + } + + /** + * zip files + * + * @param resFileList zip from files + * @param zipFile zip to file + * @param comment comment of target file + */ + @Throws(IOException::class) + fun zipFiles(resFileList: Collection?, zipFile: File, comment: String): Boolean { + if (null == resFileList || resFileList.isEmpty()) { + return false + } + + val zipOutputStream = ZipOutputStream(BufferedOutputStream(FileOutputStream(zipFile), BUFF_SIZE)) + for (resFile in resFileList) { + zipFile(resFile, zipOutputStream, "") + } + + if (!TextUtils.isEmpty(comment)) { + zipOutputStream.setComment(comment) + } + + CloseUtil.close(zipOutputStream) + return true + } + + /** + * zip file + * + * @param resFile zip from file + * @param zipOut zip to file + * @param rootPath target file path + */ + @Throws(IOException::class) + fun zipFile(resFile: File, zipOut: ZipOutputStream, rootPath: String) { + + var separator = "" + if (rootPath.isNotEmpty()) { + separator = File.separator + } + + var filePath = rootPath + separator + resFile.name + filePath = String(filePath.toByteArray(), Charset.forName("GB2312")) + + if (resFile.isDirectory) { + val fileList = resFile.listFiles() + + for (file in fileList!!) { + zipFile(file, zipOut, filePath) + } + return + } + + val buffer = ByteArray(BUFF_SIZE) + val bis = BufferedInputStream(FileInputStream(resFile), BUFF_SIZE) + zipOut.putNextEntry(ZipEntry(filePath)) + var realLength = bis.read(buffer) + + while (realLength != -1) { + zipOut.write(buffer, 0, realLength) + realLength = bis.read(buffer) + } + + CloseUtil.close(bis) + zipOut.flush() + zipOut.closeEntry() + } +} diff --git a/settings.gradle b/settings.gradle index ad346bd..d6763d5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':mvp', ':test',':xrecycler',':utill' +include ':app', ':mvp', ':test',':xrecycler',':utill',':nativelogger' diff --git a/utill/build.gradle b/utill/build.gradle index 0723a40..ed37298 100644 --- a/utill/build.gradle +++ b/utill/build.gradle @@ -8,7 +8,7 @@ apply plugin: 'com.novoda.bintray-release' publish { artifactId = 'lennon-utill' // artifactId uploadName = 'LennonUtill' // 展示名字 - publishVersion = '1.0.3' // 版本 + publishVersion = '1.2.2' // 版本 userOrg = rootProject.userOrg groupId = rootProject.groupId repoName = rootProject.repoName @@ -16,6 +16,7 @@ publish { website = rootProject.website licences = rootProject.licences } + android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -25,10 +26,6 @@ android { versionCode rootProject.ext.android.versionCode versionName rootProject.ext.android.versionName } - - lintOptions { - abortOnError false - } } dependencies { api fileTree(include: ['*.jar'], dir: 'libs') diff --git a/utill/src/main/java/com/lennon/cn/utill/adapter/BaseViewAdapter.kt b/utill/src/main/java/com/lennon/cn/utill/adapter/BaseViewAdapter.kt index 4a7ed3c..c0a6f7b 100644 --- a/utill/src/main/java/com/lennon/cn/utill/adapter/BaseViewAdapter.kt +++ b/utill/src/main/java/com/lennon/cn/utill/adapter/BaseViewAdapter.kt @@ -19,7 +19,7 @@ class BaseViewAdapter(context: Context) : SimpleRecAdapter(context: Context) : } override fun onBindViewHolder(holder: ViewHolder, position: Int) { -// super.onBindViewHolder(holder, position) + super.onBindViewHolder(holder, position) holder.name.text = data[position].itemString holder.name.setBackgroundResource(R.color.color_ffffff) holder.name.setTextColor(Utill.getColor(context.resources, R.color.color_0F83F0)) diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt index d6eaa52..4fe5af9 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt @@ -3,9 +3,11 @@ package com.lennon.cn.utill.base import android.app.Activity import android.content.Context import android.content.Intent +import android.os.Build import android.os.Bundle import android.view.View import android.view.ViewGroup +import android.view.WindowManager import android.widget.Toast import cn.droidlover.xdroidmvp.log.XLog import cn.droidlover.xdroidmvp.mvp.XActivity @@ -46,6 +48,26 @@ abstract class BaseActivity

> : XActivity

(), BaseView

{ return 0 } + /** + * 隐藏虚拟按键,并且设置成全屏 + */ + fun hideBottomUIMenu() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { // lower api + val v = this.window.decorView + v.systemUiVisibility = View.GONE + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + //for new api versions. + val decorView = window.decorView + val uiOptions = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar + or View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar + or View.SYSTEM_UI_FLAG_IMMERSIVE) + decorView.systemUiVisibility = uiOptions + window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) + } + } override fun showProgressDialog(msg: String) { if (dialog != null) dialog!!.dismiss() dialog = CustomProgressDialog(getContext()) diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseApi.java b/utill/src/main/java/com/lennon/cn/utill/base/BaseApi.java index 538cded..e60e163 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseApi.java +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseApi.java @@ -22,7 +22,6 @@ public BaseApi(String url) { service = XApi.getInstance().getRetrofit(url, true).create(tClass); } catch (Exception e) { e.printStackTrace(); - BaseApplication.Companion.registerProvider(); service = XApi.getInstance().getRetrofit(url, true).create(tClass); } } diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseApplication.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseApplication.kt index 6f3df81..026b652 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseApplication.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseApplication.kt @@ -4,27 +4,21 @@ import android.app.Activity import android.content.Context import android.content.res.Configuration import android.content.res.Resources +import android.os.Looper import android.text.TextUtils +import android.widget.Toast import androidx.multidex.MultiDex import androidx.multidex.MultiDexApplication import cn.droidlover.xdroidmvp.cache.SharedPref import cn.droidlover.xdroidmvp.log.XLog -import cn.droidlover.xdroidmvp.net.NetError -import cn.droidlover.xdroidmvp.net.NetProvider -import cn.droidlover.xdroidmvp.net.RequestHandler -import cn.droidlover.xdroidmvp.net.XApi -import com.franmontiel.persistentcookiejar.PersistentCookieJar -import com.franmontiel.persistentcookiejar.cache.SetCookieCache -import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor import com.lennon.cn.utill.conf.Lennon import com.lennon.cn.utill.utill.DensityUtils -import com.lennon.cn.utill.utill.TimeUtill import com.lennon.cn.utill.utill.Utill import lennon.com.utill.BuildConfig -import okhttp3.* import java.io.BufferedReader import java.io.FileReader import java.util.* +import java.util.logging.SimpleFormatter /** * Created by lennon on 2017/7/26. @@ -53,18 +47,10 @@ open class BaseApplication : MultiDexApplication() { return res } - open fun init() { - registerProvider() -// val context = applicationContext - // 获取当前包名 -// val packageName = context.packageName - // 获取当前进程名 -// val processName = getProcessName(android.os.Process.myPid()) - // 设置是否为上报进程 -// val strategy = CrashReport.UserStrategy(context) -// strategy.isUploadProcess = processName == null || processName == packageName - // 初始化Bugly -// CrashReport.initCrashReport(context, Lennon.getCrashID(), Lennon.isTest()) + override fun onCreate() { + DensityUtils.setDensity(this) + appliction = this + super.onCreate() // NLoggerConfig.getInstance() // .builder() // .tag("APP") @@ -107,19 +93,10 @@ open class BaseApplication : MultiDexApplication() { // }.build() } - override fun onCreate() { - DensityUtils.setDensity(this) - appliction = this - super.onCreate() - - } - companion object { private var list: ArrayList? = null private val loger = true private var appliction: BaseApplication? = null - private var cookiePersistor: SharedPrefsCookiePersistor? = null - private var cookieJar: PersistentCookieJar? = null private var test = true /** * 获取进程号对应的进程名 @@ -150,68 +127,6 @@ open class BaseApplication : MultiDexApplication() { return null } -// fun init() { -// NLoggerConfig.getInstance() -// .builder() -// .tag("APP") -// .loggerLevel(LoggerLevel.INFO) -// .fileLogger(true) -// .fileDirectory(getDataFile() + "/logs") -// .fileFormatter(SimpleFormatter()) -// .expiredPeriod(7) -// .catchException(true) { _, throwable -> -// getCuttureActivity()?.finish() -// val t = if (throwable != null) { -// throwable -// } else { -// Throwable("未知异常") -// } -// t.printStackTrace() -//// if (getCuttureActivity() != null) { -// getCuttureActivity()!!.runOnUiThread { -// // 使用Toast来显示异常信息 -// object : Thread() { -// override fun run() { -// Looper.prepare() -// Toast.makeText( -// getCuttureActivity(), -// "很抱歉,程序出现异常,即将退出,请联系客服:" + t.message, -// Toast.LENGTH_LONG -// ).show() -// Looper.loop() -// } -// }.start() -// // val dialog = CommonAlertDialog(getCuttureActivity()) -//// dialog.setTitle("非常抱歉") -//// dialog.setCancelable(false) -//// dialog.setCanceledOnTouchOutside(false) -//// dialog.setMsg("程序出错了,请联系客服:" + t.message) -//// dialog.setDialogListener(object : OnAlertDialogListener() { -//// override fun onSure() { -//// super.onSure() -//// dialog.dismiss() -//// XLog.e("uncaughtException", throwable!!) -//// Lennon.restartApp() -//// AppExit() -//// } -//// }) -//// dialog.show() -//// } -//// } else { -// Toast.makeText( -// getCuttureActivity(), -// "非常抱歉,程序出错了,请联系客服:" + t.message, -// Toast.LENGTH_SHORT -// ).show() -// XLog.e("uncaughtException", t.message) -// Lennon.restartApp() -// AppExit() -// } -//// android.os.Process.killProcess(android.os.Process.myPid()) -// }.build() -// -// } - fun isLoger(): Boolean { return loger } @@ -220,95 +135,12 @@ open class BaseApplication : MultiDexApplication() { return appliction } - fun registerProvider() { - if (cookieJar == null) { - cookiePersistor = SharedPrefsCookiePersistor(appliction!!) - cookieJar = PersistentCookieJar(SetCookieCache(), cookiePersistor!!) - } - XApi.registerProvider(object : NetProvider { - - override fun configInterceptors(): Array { - return arrayOfNulls(0) - } - - override fun configHttps(builder: OkHttpClient.Builder) { - - } - - override fun configCookie(): CookieJar? { - return cookieJar - } - - override fun configHandler(): RequestHandler { - return object : RequestHandler { - private var sendTime = 0L - private var url = "" - override fun onBeforeRequest( - request: Request, - chain: Interceptor.Chain - ): Request? { - val builder = chain.request().newBuilder() - builder.addHeader("Content-Type", "application/json;charset=utf-8") - builder.addHeader("Accept", "application/json;charset=utf-8") - sendTime = System.currentTimeMillis() - url = request.url().toString() - try { - return builder.build() - } catch (e: Exception) { - e.printStackTrace() - } - return null - } - - override fun onAfterRequest( - response: Response, - chain: Interceptor.Chain - ): Response { - if (getCuttureActivity() != null) { - val end = System.currentTimeMillis() - val mss = end - sendTime - XLog.e("请求" + url + "耗时" + TimeUtill.formatDuring(mss)) - } - return response - } - } - } - - override fun configConnectTimeoutMills(): Long { - return 60000 - } - - override fun configReadTimeoutMills(): Long { - return 60000 - } - - override fun configLogEnable(): Boolean { - return BaseApplication.isLoger() - } - - override fun handleError(error: NetError): Boolean { - return Lennon.handleNetError(error) - } - - override fun dispatchProgressEnable(): Boolean { - return false - } - }) - } fun getDataFile(): String { Utill.makeDir(Lennon.getFilePathName()) return Lennon.getFilePathName() } - val cookie: List - get() = cookiePersistor!!.loadAll() - - fun clearCookies() { - cookieJar!!.clear() - } - - fun setCookies(cookie: List) {} fun addActivity(activity: Activity) { if (list == null) list = ArrayList() @@ -331,7 +163,7 @@ open class BaseApplication : MultiDexApplication() { fun AppExit() { Lennon.appExit() for (activity in list!!) { - activity?.finish() + activity.finish() } System.exit(0) } @@ -366,12 +198,6 @@ open class BaseApplication : MultiDexApplication() { fun isTest(): Boolean { return test && BuildConfig.DEBUG } - - fun addCookie(cookie: Cookie) { - val l = cookiePersistor!!.loadAll() - l.add(cookie) - cookiePersistor!!.saveAll(l) - } } open fun restart(context: Context) { diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseLazyFragment.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseLazyFragment.kt new file mode 100644 index 0000000..cfed0fe --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseLazyFragment.kt @@ -0,0 +1,144 @@ +package com.lennon.cn.utill.base + +import android.app.Activity +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import cn.droidlover.xdroidmvp.log.XLog +import cn.droidlover.xdroidmvp.mvp.XLazyFragment +import com.lennon.cn.utill.dialog.CommonAlertDialog +import com.lennon.cn.utill.dialog.CustomProgressDialog +import com.lennon.cn.utill.dialog.OnAlertDialogListener + +abstract class BaseLazyFragment

> : XLazyFragment

(), BaseView

{ + private var mView: View? = null + private var dialog: CustomProgressDialog? = null + private var mActivity: Activity? = null + override fun showLoading(visibility: Int) { + } + + override fun useEventBus(): Boolean { + return true + } + + private var isFirst = true + private var stop = false + + + override fun onRefresh(bRefresh: Boolean) { + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + XLog.e(getName() + " onCreateView") + mView = super.onCreateView(inflater, container, savedInstanceState) + return mView + } + + + override fun showProgressDialog(msg: String) { + if (dialog != null) + dialog!!.dismiss() + dialog = CustomProgressDialog(getContext()) + dialog!!.setMessage(msg) + dialog!!.show() + } + + + override fun onResume() { + super.onResume() + XLog.e(getName() + " onResume") + } + + + protected fun getName(): String { + return javaClass.simpleName + } + + override fun closeProgressDialog() { + if (null != dialog) + dialog!!.dismiss() + } + + override fun toast(msg: String) { + toast(msg, true) + } + + override fun toast(msg: String, runnable: Runnable) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + runnable.run() + } + }) + Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) + .show() + } + + override fun toast(msg: String, flag: Boolean) { + if (flag) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + } + }) + } + Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) + .show() + } + + override fun toast(msg: String, second: Int) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + } + }) + Toast.makeText(getContext(), msg, second).show() + } + + override fun onStop() { + XLog.e(getName() + " onStop") + stop = true + super.onStop() + } + + override fun onAttach(activity: Activity) { + XLog.e(getName() + " onAttach") + super.onAttach(activity) + mActivity = activity + } + + override fun onPause() { + XLog.e(getName() + " onPause") + super.onPause() + } + + override fun getContext(): Context? { + return if (mActivity != null) { + mActivity + } else { + BaseApplication.getCuttureActivity() + } + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/bean/HttpEntity.java b/utill/src/main/java/com/lennon/cn/utill/bean/HttpEntity.java index 7aa1b84..1b9a223 100644 --- a/utill/src/main/java/com/lennon/cn/utill/bean/HttpEntity.java +++ b/utill/src/main/java/com/lennon/cn/utill/bean/HttpEntity.java @@ -42,6 +42,11 @@ public boolean isSuccess() { return "0000".equals(code); } + @Override + public String getErrorCode() { + return code; + } + @Override public boolean isNull() { if (!"0000".equals(code)) { diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/AudioRecorderUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/AudioRecorderUtil.java index 9da9804..ca5b8d1 100644 --- a/utill/src/main/java/com/lennon/cn/utill/utill/AudioRecorderUtil.java +++ b/utill/src/main/java/com/lennon/cn/utill/utill/AudioRecorderUtil.java @@ -59,9 +59,9 @@ public String start() { this.mHandler.removeCallbacks(this.mUpdateMicStatusTimer); try { - this.mMediaRecorder.setAudioSource(1); - this.mMediaRecorder.setOutputFormat(0); - this.mMediaRecorder.setAudioEncoder(1); + this.mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT); + this.mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); + this.mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); this.filePath = this.folderPath + File.separator + System.currentTimeMillis() + ".mp3"; this.mMediaRecorder.setOutputFile(this.filePath); this.mMediaRecorder.setMaxDuration(this.maxLength); diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/GetDeviceId.java b/utill/src/main/java/com/lennon/cn/utill/utill/GetDeviceId.java index 5eac5a8..0115586 100644 --- a/utill/src/main/java/com/lennon/cn/utill/utill/GetDeviceId.java +++ b/utill/src/main/java/com/lennon/cn/utill/utill/GetDeviceId.java @@ -4,18 +4,13 @@ import android.content.Context; import android.os.Build; import android.text.TextUtils; -import cn.droidlover.xdroidmvp.cache.SharedPref; import cn.droidlover.xdroidmvp.log.XLog; -import com.lennon.cn.utill.base.BaseApplication; import java.io.*; import java.util.ArrayList; import java.util.List; - public class GetDeviceId { - //保存的文件 采用隐藏文件的形式进行保存 - private static final String DEVICES_FILE_NAME = ".DEVICES"; /** * 获取设备唯一标识符 @@ -63,47 +58,12 @@ public static String getDeviceId(Activity context) { * @return */ public static List readDeviceID(Context context) { - File file = getDevicesDir(context); File newFile = getNewDevicesDir(context); List strings = getFileContent(new File(newFile.getAbsolutePath() + "/", "devices.txt")); if (strings.size() > 0) { return strings; } -// return getFileContent(file); - -// try { -// List strings = new ArrayList<>(); -// FileInputStream fis = new FileInputStream(newFile); -// InputStreamReader isr = new InputStreamReader(fis, "UTF-8"); -// BufferedReader in = new BufferedReader(isr); -// String temp; -// while ((temp = in.readLine()) != null) { -// strings.add(temp); -// } -// in.close(); -// XLog.e("-------------readDeviceIdFile" + strings.toString()); -// if (strings.size() > 0) { -// return strings; -// } -// } catch (IOException e) { -// e.printStackTrace(); -// } - try { - strings = new ArrayList<>(); - FileInputStream fis = new FileInputStream(file); - InputStreamReader isr = new InputStreamReader(fis, "UTF-8"); - BufferedReader in = new BufferedReader(isr); - String temp; - while ((temp = in.readLine()) != null) { - strings.add(temp); - } - in.close(); - XLog.e("-------------readDeviceIdFile" + strings.toString()); - return strings; - } catch (IOException e) { - e.printStackTrace(); - return null; - } + return new ArrayList<>(); } /** @@ -113,7 +73,6 @@ public static List readDeviceID(Context context) { * @param context */ public static void saveDeviceID(String str, Context context) { - File file = getDevicesDir(context); File newFile = getNewDevicesDir(context); File f = new File(newFile.getPath(), newFile.getName() + ".txt"); try { @@ -125,28 +84,10 @@ public static void saveDeviceID(String str, Context context) { XLog.e(e.getMessage()); } -// try { writeTxtToFile("deviceId:" + str, newFile.getAbsolutePath() + "/", "devices.txt"); -// writer.write("deviceId:" + str); -// writer.newLine(); + writeTxtToFile("deviceId-version:2", newFile.getAbsolutePath() + "/", "devices.txt"); -// out.close(); -// } catch (IOException e) { -// e.printStackTrace(); -// } -// writeTxtToFile("deviceId:" + str, file.getParent(), file.getName()); -// writeTxtToFile("deviceId-version:2", file.getParent(), file.getName()); - try { - FileOutputStream fos = new FileOutputStream(file); - OutputStreamWriter out = new OutputStreamWriter(fos, "UTF-8"); - BufferedWriter writer = new BufferedWriter(out); - writer.write("deviceId:" + str); - writer.newLine(); - writer.write("deviceId-version:2"); - out.close(); - } catch (IOException e) { - e.printStackTrace(); - } + } @@ -234,29 +175,6 @@ private static List getFileContent(File file) { return content; } - /** - * 统一处理设备唯一标识 保存的文件的地址 - * - * @param context - * @return - */ - private static File getDevicesDir(Context context) { - File mCropFile = null; - mCropFile = new File(BaseApplication.Companion.getDataFile(), DEVICES_FILE_NAME); - if (!mCropFile.getParentFile().exists()) { - mCropFile.getParentFile().mkdirs(); - } - if (!mCropFile.exists()) { - try { - mCropFile.createNewFile(); - } catch (Exception e) { - e.printStackTrace(); - } - } - XLog.e("getDevicesDir-----------------" + context.getFilesDir() + "-------------" + mCropFile.getParent() + "-----------------" + mCropFile.getName()); - return mCropFile; - } - private static File getNewDevicesDir(Context context) { File mCropFile = null; mCropFile = new File(context.getFilesDir(), "devices"); From 5d21e5036f7606b51c8e97bc8788301b98d7aea8 Mon Sep 17 00:00:00 2001 From: Edit <1136160757@qq.com> Date: Wed, 8 Apr 2020 15:58:28 +0800 Subject: [PATCH 11/24] =?UTF-8?q?=E4=BF=AE=E6=94=B9toast=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- utill/build.gradle | 2 +- .../com/lennon/cn/utill/base/BaseActivity.kt | 3 ++ .../cn/utill/dialog/CommonAlertDialog.java | 2 +- .../cn/utill/dialog/CustomProgressDialog.java | 1 + .../lennon/cn/utill/utill/GetDeviceId.java | 2 +- .../lennon/cn/utill/utill/VersionUtill.java | 38 ++++++++++++------- 7 files changed, 32 insertions(+), 18 deletions(-) diff --git a/build.gradle b/build.gradle index 9935589..f1c8b61 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:3.6.1' + classpath 'com.android.tools.build:gradle:3.6.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' diff --git a/utill/build.gradle b/utill/build.gradle index ed37298..ff12937 100644 --- a/utill/build.gradle +++ b/utill/build.gradle @@ -8,7 +8,7 @@ apply plugin: 'com.novoda.bintray-release' publish { artifactId = 'lennon-utill' // artifactId uploadName = 'LennonUtill' // 展示名字 - publishVersion = '1.2.2' // 版本 + publishVersion = '1.2.8' // 版本 userOrg = rootProject.userOrg groupId = rootProject.groupId repoName = rootProject.repoName diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt index 4fe5af9..6c1f52d 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt @@ -87,6 +87,7 @@ abstract class BaseActivity

> : XActivity

(), BaseView

{ val dialog = CommonAlertDialog(this) dialog.setMsg(msg) dialog.disableCancle() + dialog.setCanceledOnTouchOutside(false) dialog.show() dialog.setDialogListener(object : OnAlertDialogListener() { override fun onSure() { @@ -107,6 +108,7 @@ abstract class BaseActivity

> : XActivity

(), BaseView

{ dialog.setMsg(msg) dialog.disableCancle() dialog.show() + dialog.setCanceledOnTouchOutside(false) dialog.setDialogListener(object : OnAlertDialogListener() { override fun onSure() { super.onSure() @@ -123,6 +125,7 @@ abstract class BaseActivity

> : XActivity

(), BaseView

{ val dialog = CommonAlertDialog(this) dialog.setMsg(msg) dialog.disableCancle() + dialog.setCanceledOnTouchOutside(false) dialog.show() dialog.setDialogListener(object : OnAlertDialogListener() { override fun onSure() { diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/CommonAlertDialog.java b/utill/src/main/java/com/lennon/cn/utill/dialog/CommonAlertDialog.java index ed73600..540da14 100644 --- a/utill/src/main/java/com/lennon/cn/utill/dialog/CommonAlertDialog.java +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/CommonAlertDialog.java @@ -9,6 +9,7 @@ import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; + import lennon.com.utill.R; /** @@ -84,7 +85,6 @@ public void setContent(String content) { mContentEdt.setSelection(mContentEdt.getText().length()); } - public void setDialogListener(OnAlertDialogListener listener) { this.mOnListener = listener; } diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/CustomProgressDialog.java b/utill/src/main/java/com/lennon/cn/utill/dialog/CustomProgressDialog.java index e894884..cc8d831 100644 --- a/utill/src/main/java/com/lennon/cn/utill/dialog/CustomProgressDialog.java +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/CustomProgressDialog.java @@ -3,6 +3,7 @@ import android.app.Dialog; import android.content.Context; import android.view.View; +import android.view.Window; import android.widget.LinearLayout; import android.widget.TextView; diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/GetDeviceId.java b/utill/src/main/java/com/lennon/cn/utill/utill/GetDeviceId.java index 0115586..ab5e1f8 100644 --- a/utill/src/main/java/com/lennon/cn/utill/utill/GetDeviceId.java +++ b/utill/src/main/java/com/lennon/cn/utill/utill/GetDeviceId.java @@ -18,7 +18,7 @@ public class GetDeviceId { * @param context * @return */ - public static String getDeviceId(Activity context) { + public static String getDeviceId(Context context) { //读取保存的在sd卡中的唯一标识符 List deviceIds = readDeviceID(context); if (deviceIds != null) { diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/VersionUtill.java b/utill/src/main/java/com/lennon/cn/utill/utill/VersionUtill.java index 1a6732b..466a1a8 100644 --- a/utill/src/main/java/com/lennon/cn/utill/utill/VersionUtill.java +++ b/utill/src/main/java/com/lennon/cn/utill/utill/VersionUtill.java @@ -18,6 +18,8 @@ import androidx.core.app.ActivityCompat; +import com.lennon.cn.utill.base.BaseApplication; + /** * 作者:11361 on 2019/1/24 10:07 *

@@ -25,7 +27,7 @@ */ public class VersionUtill { @SuppressLint({"WifiManagerLeak", "MissingPermission", "HardwareIds"}) - public static String getAndroidId(Activity activity) { + public static String getAndroidId(Context context) { int REQUEST_EXTERNAL_STORAGE = 1; String[] PERMISSIONS_STORAGE = { Manifest.permission.BLUETOOTH, @@ -34,19 +36,27 @@ public static String getAndroidId(Activity activity) { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; - int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE); + int permission = ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE); - if (permission != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(activity, - Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(activity, - Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(activity, - Manifest.permission.ACCESS_WIFI_STATE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(activity, + if (permission != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, + Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, + Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, + Manifest.permission.ACCESS_WIFI_STATE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED) { // We don't have permission so prompt the user - ActivityCompat.requestPermissions( - activity, - PERMISSIONS_STORAGE, - REQUEST_EXTERNAL_STORAGE - ); + if (context instanceof Activity) { + ActivityCompat.requestPermissions( + (Activity) context, + PERMISSIONS_STORAGE, + REQUEST_EXTERNAL_STORAGE + ); + }else if (BaseApplication.Companion.getCuttureActivity()!=null){ + ActivityCompat.requestPermissions( + BaseApplication.Companion.getCuttureActivity(), + PERMISSIONS_STORAGE, + REQUEST_EXTERNAL_STORAGE + ); + } } BluetoothAdapter m_BluetoothAdapter = null; // Local Bluetooth adapter m_BluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -54,7 +64,7 @@ public static String getAndroidId(Activity activity) { if (m_BluetoothAdapter != null) { m_szBTMAC = m_BluetoothAdapter.getAddress();//蓝牙MAC地址 } - WifiManager wm = (WifiManager) activity.getSystemService(Context.WIFI_SERVICE); + WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); String m_szWLANMAC = wm.getConnectionInfo().getMacAddress();//wifi MAC地址 String m_szDevIDShort = "35" + //we make this look like a valid IMEI Build.BOARD.length() % 10 +//主板编号 @@ -70,8 +80,8 @@ public static String getAndroidId(Activity activity) { Build.TAGS.length() % 10 +//描述build的标签,如未签名,debug等等 Build.TYPE.length() % 10 +//build的类型 Build.USER.length() % 10; //13 digits - String m_szAndroidID = Settings.Secure.getString(activity.getContentResolver(), Settings.Secure.ANDROID_ID); - TelephonyManager TelephonyMgr = (TelephonyManager) activity.getSystemService(Activity.TELEPHONY_SERVICE); + String m_szAndroidID = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); + TelephonyManager TelephonyMgr = (TelephonyManager) context.getSystemService(Activity.TELEPHONY_SERVICE); String m_szImei = TelephonyMgr.getDeviceId(); String m_szLongID = m_szImei + m_szDevIDShort + m_szAndroidID + m_szWLANMAC + m_szBTMAC; From 6ec87593b925d8372d5bbaa9bc12754a2ce4bfc1 Mon Sep 17 00:00:00 2001 From: Edit <1136160757@qq.com> Date: Tue, 28 Apr 2020 10:38:42 +0800 Subject: [PATCH 12/24] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=8C=E7=BB=B4?= =?UTF-8?q?=E7=A0=81=E7=94=9F=E6=88=90=E5=B7=A5=E5=85=B7=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- conf.gradle | 6 +- mvp/build.gradle | 2 +- .../xdroidmvp/imageloader/GlideLoader.java | 48 +- .../xdroidmvp/imageloader/ILoader.java | 2 + .../cn/droidlover/xdroidmvp/mvp/IPresent.java | 2 +- .../cn/droidlover/xdroidmvp/mvp/IView.java | 2 +- .../xdroidmvp/mvp/XPresentation.java | 86 +++ qrutill/.gitignore | 1 + qrutill/build.gradle | 40 ++ qrutill/consumer-rules.pro | 0 qrutill/proguard-rules.pro | 21 + qrutill/src/main/AndroidManifest.xml | 2 + .../java/com/inlee/qrutill/QRCodeTool.java | 495 +++++++++++++++ .../com/inlee/qrutill/bean/QRCodeBean.java | 7 + settings.gradle | 1 + utill/build.gradle | 24 +- .../lennon/cn/utill/base/BasePresentation.kt | 110 ++++ .../java/com/lennon/cn/utill/conf/Lennon.kt | 6 + .../lennon/cn/utill/conf/LennonProvider.kt | 13 +- .../com/lennon/cn/utill/utill/DensityUtils.kt | 22 +- .../cn/utill/widget/CycleWheelView.java | 597 ++++++++++++++++++ utill/src/main/res/layout/headbar.xml | 2 +- utill/src/main/res/layout/item_base_view.xml | 21 +- utill/src/main/res/layout/item_cyclewheel.xml | 17 + 25 files changed, 1485 insertions(+), 44 deletions(-) create mode 100644 mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java create mode 100644 qrutill/.gitignore create mode 100644 qrutill/build.gradle create mode 100644 qrutill/consumer-rules.pro create mode 100644 qrutill/proguard-rules.pro create mode 100644 qrutill/src/main/AndroidManifest.xml create mode 100644 qrutill/src/main/java/com/inlee/qrutill/QRCodeTool.java create mode 100644 qrutill/src/main/java/com/inlee/qrutill/bean/QRCodeBean.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt create mode 100644 utill/src/main/java/com/lennon/cn/utill/widget/CycleWheelView.java create mode 100644 utill/src/main/res/layout/item_cyclewheel.xml diff --git a/build.gradle b/build.gradle index f1c8b61..bc3fb77 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:3.6.2' + classpath 'com.android.tools.build:gradle:3.6.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' diff --git a/conf.gradle b/conf.gradle index de214b7..27f91c6 100644 --- a/conf.gradle +++ b/conf.gradle @@ -3,7 +3,7 @@ ext { android = [ compileSdkVersion: 29, - minSdkVersion : 16, + minSdkVersion : 17, targetSdkVersion : 29, versionCode : 1, @@ -18,6 +18,8 @@ ext { ] dependencies = [ + "zxing" : "com.google.zxing:core:3.3.3", + "navigation-fragment-ktx" : "androidx.navigation:navigation-fragment-ktx:${android["NavigationSDK"]}", "navigation-ui-ktx" : "androidx.navigation:navigation-ui-ktx:${android["NavigationSDK"]}", @@ -52,7 +54,7 @@ ext { "picasso" : "com.squareup.picasso:picasso:2.5.2", "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.3", - "xdroid-mvp" : "com.lennon.utill:xdroid-mvp:1.1.5", + "xdroid-mvp" : "com.lennon.utill:xdroid-mvp:1.1.8", "avi-loading" : "com.wang.avi:library:1.0.2", "rxbus" : "com.blankj:rxbus:1.2", diff --git a/mvp/build.gradle b/mvp/build.gradle index 14638d1..a5b3d7d 100644 --- a/mvp/build.gradle +++ b/mvp/build.gradle @@ -10,7 +10,7 @@ apply plugin: 'kotlin-android-extensions' //publish { // artifactId = 'xdroid-mvp' // artifactId // uploadName = 'XDroidMvp' // 展示名字 -// publishVersion = '1.1.5' // 版本 +// publishVersion = '1.1.8' // 版本 // userOrg = rootProject.userOrg // groupId = rootProject.groupId // repoName = rootProject.repoName diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/GlideLoader.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/GlideLoader.java index 9116c25..d283289 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/GlideLoader.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/GlideLoader.java @@ -4,7 +4,9 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; + import androidx.annotation.Nullable; + import android.widget.ImageView; import com.bumptech.glide.Glide; @@ -75,6 +77,9 @@ public void loadNet(Context context, String url, Options options, final LoadCall @Override public void onLoadFailed(@Nullable Drawable errorDrawable) { super.onLoadFailed(errorDrawable); + if (callback != null) { + callback.onLoadFailed(); + } } @Override @@ -83,6 +88,10 @@ public void onResourceReady(Drawable resource, Transition tran if (callback != null) { callback.onLoadReady(resource); } + } else { + if (callback != null) { + callback.onLoadFailed(); + } } } @@ -129,7 +138,7 @@ private RequestOptions wrapScaleType(Options options) { .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .priority(Priority.HIGH); - if (options != null){ + if (options != null) { if (options.scaleType != null) { if (options.loadingResId != Options.RES_NONE) { request.placeholder(options.loadingResId); @@ -158,7 +167,7 @@ private RequestOptions wrapScaleType(Options options) { } else { request.centerCrop(); } - }else { + } else { request.centerCrop(); } @@ -179,6 +188,41 @@ public void loadCircle(String url, final ImageView target, Options options) { } + @Override + public void loadCircle(String url, Context context, Options options, final LoadCallback callback) { + RequestOptions requestOptions = wrapScaleType(options); + requestOptions.optionalCircleCrop(); + + getRequestManager(context) + .load(url) + .apply(requestOptions) + .transition(withCrossFade()) + .into(new SimpleTarget() { + + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + super.onLoadFailed(errorDrawable); + if (callback != null) { + callback.onLoadFailed(); + } + } + + @Override + public void onResourceReady(Drawable resource, Transition transition) { + if (resource != null) { + if (callback != null) { + callback.onLoadReady(resource); + } + } else { + if (callback != null) { + callback.onLoadFailed(); + } + } + } + + }); + } + //加载圆形图片 @Override public void loadCorner(String url, final ImageView target, int radius, Options options) { diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/ILoader.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/ILoader.java index 26b0361..1fc3067 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/ILoader.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/ILoader.java @@ -35,6 +35,8 @@ public interface ILoader { void loadCircle(String url, ImageView target, Options options); + void loadCircle(String url, Context context, Options options, LoadCallback callback); + void loadCorner(String url, ImageView target, int radius, Options options); class Options { diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IPresent.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IPresent.java index 2d2f075..814256a 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IPresent.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IPresent.java @@ -4,7 +4,7 @@ * Created by wanglei on 2016/12/29. */ -public interface IPresent { +public interface IPresent { void attachV(V view); void detachV(); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java index 7c1d76e..44cbd7c 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java @@ -7,7 +7,7 @@ * Created by wanglei on 2016/12/29. */ -public interface IView

{ +public interface IView

{ void bindEvent(); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java new file mode 100644 index 0000000..f4110e4 --- /dev/null +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java @@ -0,0 +1,86 @@ +package cn.droidlover.xdroidmvp.mvp; + +import android.app.Activity; +import android.app.Presentation; +import android.content.Context; +import android.os.Bundle; +import android.view.Display; + +import com.tbruyelle.rxpermissions2.RxPermissions; + +import cn.droidlover.xdroidmvp.XDroidConf; +import cn.droidlover.xdroidmvp.event.BusProvider; + +public abstract class XPresentation

extends Presentation implements IView

{ + private P p; + private VDelegate vDelegate; + protected Context context; + + private RxPermissions rxPermissions; + public XPresentation(Context outerContext, Display display) { + super(outerContext, display); + this.context=outerContext; + } + protected RxPermissions getRxPermissions() { + rxPermissions = new RxPermissions(getOwnerActivity()); + rxPermissions.setLogging(XDroidConf.DEV); + return rxPermissions; + } + public XPresentation(Context outerContext, Display display, int theme) { + super(outerContext, display, theme); + this.context=outerContext; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getP(); + + if (getLayoutId() > 0) { + setContentView(getLayoutId()); + bindEvent(); + } + initData(savedInstanceState); + } + protected VDelegate getvDelegate() { + if (vDelegate == null) { + vDelegate = VDelegateBase.create(context); + } + return vDelegate; + } + + @Override + protected void onStart() { + super.onStart(); + if (useEventBus()) { + BusProvider.getBus().register(this); + } + } + + @Override + public void onDisplayRemoved() { + super.onDisplayRemoved(); + if (useEventBus()) { + BusProvider.getBus().unregister(this); + } + if (getP() != null) { + getP().detachV(); + } + getvDelegate().destory(); + + p = null; + vDelegate = null; + } + + protected P getP() { + if (p == null) { + p = newP(); + } + if (p != null) { + if (!p.hasV()) { + p.attachV(this); + } + } + return p; + } +} diff --git a/qrutill/.gitignore b/qrutill/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/qrutill/.gitignore @@ -0,0 +1 @@ +/build diff --git a/qrutill/build.gradle b/qrutill/build.gradle new file mode 100644 index 0000000..ea85055 --- /dev/null +++ b/qrutill/build.gradle @@ -0,0 +1,40 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' + +//apply plugin: 'com.novoda.bintray-release' +//publish { +// artifactId = 'lennon-qr-utill' // artifactId +// uploadName = 'LennonQRUtill' // 展示名字 +// publishVersion = '1.0.0' // 版本 +// userOrg = rootProject.userOrg +// groupId = rootProject.groupId +// repoName = rootProject.repoName +// desc = '' +// website = rootProject.website +// licences = rootProject.licences +//} + +android { + compileSdkVersion rootProject.ext.android.compileSdkVersion + + defaultConfig { + minSdkVersion rootProject.ext.android.minSdkVersion + targetSdkVersion rootProject.ext.android.targetSdkVersion + versionCode rootProject.ext.android.versionCode + versionName rootProject.ext.android.versionName + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation rootProject.ext.dependencies["xdroid-mvp"] + api rootProject.ext.dependencies["zxing"] +} diff --git a/qrutill/consumer-rules.pro b/qrutill/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/qrutill/proguard-rules.pro b/qrutill/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/qrutill/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/qrutill/src/main/AndroidManifest.xml b/qrutill/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a419a71 --- /dev/null +++ b/qrutill/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/qrutill/src/main/java/com/inlee/qrutill/QRCodeTool.java b/qrutill/src/main/java/com/inlee/qrutill/QRCodeTool.java new file mode 100644 index 0000000..180a393 --- /dev/null +++ b/qrutill/src/main/java/com/inlee/qrutill/QRCodeTool.java @@ -0,0 +1,495 @@ +package com.inlee.qrutill; + +import android.graphics.*; +import android.text.TextUtils; +import cn.droidlover.xdroidmvp.net.NetError; +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.QRCodeWriter; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import com.inlee.qrutill.bean.QRCodeBean; + +import io.reactivex.*; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.functions.Function; +import io.reactivex.schedulers.Schedulers; +import org.reactivestreams.Publisher; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +/** + * Created by dingyi on 2016/12/1. + */ + +public class QRCodeTool { + /** + * 线程切换 + * + * @return + */ + public static FlowableTransformer getScheduler() { + return new FlowableTransformer() { + @Override + public Publisher apply(Flowable upstream) { + return upstream.subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .unsubscribeOn(AndroidSchedulers.mainThread()); + } + }; + } + + /** + * 异常处理变换 + * + * @return + */ + public static FlowableTransformer getApiTransformer() { + + return new FlowableTransformer() { + @Override + public Publisher apply(Flowable upstream) { + return upstream.flatMap(new Function>() { + @Override + public Publisher apply(T model) throws Exception { + if (model == null) { + return Flowable.error(new NetError("无数据", NetError.NoDataError)); + } else { + return Flowable.just(model); + } + } + }); + } + }; + } + + public Flowable saveQrCode(final String content, final int widthPix, final int heightPix, final Bitmap logoBm, final String filePath) { + return makeFlowable(callableSaveQrCode(content, widthPix, heightPix, logoBm, filePath)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable> saveQrCodes(List contents, final int widthPix, final int heightPix, final String filePath) { + return makeFlowable(callableSaveQrCodes(contents, widthPix, heightPix, filePath)); + } + + public Flowable getLogoQrCode(final Bitmap src, final Bitmap logo) { + return makeFlowable(callableGetLogoQrCode(src, logo)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable getQrCode(final String qrCode, final int widthPix, final int heightPix) { + return makeFlowable(callableGetQrCode(qrCode, widthPix, heightPix)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable> getQrCodes(final List qrCodes, final int widthPix, final int heightPix) { + return makeFlowable(callableGetQrCodes(qrCodes, widthPix, heightPix)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable> getQrCodesforShop(String memberId, final List shops, final int widthPix, final int heightPix) { + List qrCodes = new ArrayList<>(); + for (String shop : shops) { + qrCodes.add(shop + "&staffMId=" + memberId); + } + return makeFlowable(callableGetQrCodes(qrCodes, widthPix, heightPix)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable> getQrCodesforShop(final String shop, final int widthPix, final int heightPix, boolean flag) { + List qrCodes = new ArrayList<>(); + qrCodes.add(shop); + return makeFlowable(callableGetQrCodes(qrCodes, widthPix, heightPix, flag)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + private Callable callableSaveQrCode(final String content, final int widthPix, final int heightPix, final Bitmap logoBm, final String filePath) { + return new Callable() { + @Override + public Object call() throws Exception { + return createQRImage(content, widthPix, heightPix, logoBm, filePath); + } + }; + } + + private Callable> callableSaveQrCodes(final List contents, final int widthPix, final int heightPix, final String filePath) { + return new Callable>() { + @Override + public List call() throws Exception { + List list = new ArrayList(); + for (int i = 0; i < contents.size(); i++) { + list.add(createQRImage(contents.get(i).getQRCodeUrl(), widthPix, heightPix, null, filePath + "/" + contents.get(i).getFileName() + ".jpg")); + } + return list; + } + }; + } + + //flie:要删除的文件夹的所在位置 + private void deleteFile(File file) { + if (file.isDirectory()) { + File[] files = file.listFiles(); + for (int i = 0; i < files.length; i++) { + File f = files[i]; + deleteFile(f); + } + file.delete();//如要保留文件夹,只删除文件,请注释这行 + } else if (file.exists()) { + file.delete(); + } + } + + private Callable callableGetLogoQrCode(final Bitmap src, final Bitmap logo) { + return new Callable() { + @Override + public Bitmap call() throws Exception { + return addLogo(src, logo); + } + }; + } + + private Callable callableGetQrCode(final String qrCode, final int widthPix, final int heightPix) { + return new Callable() { + @Override + public Bitmap call() throws Exception { + return createQRImage(qrCode, widthPix, heightPix); + } + }; + } + + private Callable> callableGetQrCodes(final List qrCodes, final int widthPix, final int heightPix) { + return new Callable>() { + @Override + public List call() throws Exception { + List bitmaps = new ArrayList<>(); + for (String qrCode : qrCodes) { + bitmaps.add(addLogo(createQRImage(qrCode, widthPix, heightPix), null)); + } + return bitmaps; + } + }; + } + + private Callable> callableGetQrCodes(final List qrCodes, final int widthPix, final int heightPix, final boolean flag) { + return new Callable>() { + @Override + public List call() throws Exception { + List bitmaps = new ArrayList<>(); + for (String qrCode : qrCodes) { + if (flag) { + bitmaps.add(addLogo(createQRImage(qrCode, widthPix, heightPix), null)); + } else { + bitmaps.add(createQRImage(qrCode, widthPix, heightPix)); + } + } + return bitmaps; + } + }; + } + + /** + * 生成二维码Bitmap + * + * @param content 内容 + * @param widthPix 图片宽度 + * @param heightPix 图片高度 + * @param logoBm 二维码中心的Logo图标(可以为null) + * @param filePath 用于存储二维码图片的文件路径 + * @return 生成二维码及保存文件是否成功 + */ + private Bitmap createQRImage(String content, int widthPix, int heightPix, Bitmap logoBm, String filePath, String name) { + try { + if (content == null || "".equals(content)) { + return null; + } + //配置参数 + Map hints = new HashMap<>(); + hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); + //容错级别 + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); + //设置空白边距的宽度 +// hints.put(EncodeHintType.MARGIN, 2); //default is 4 + + // 图像数据转换,使用了矩阵转换 + BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, widthPix, heightPix, hints); + int[] pixels = new int[widthPix * heightPix]; + // 下面这里按照二维码的算法,逐个生成二维码的图片, + // 两个for循环是图片横列扫描的结果 + for (int y = 0; y < heightPix; y++) { + for (int x = 0; x < widthPix; x++) { + if (bitMatrix.get(x, y)) { + pixels[y * widthPix + x] = 0xff000000; +// pixels[y * widthPix + x] = 0xFFFF0000; + } else { + pixels[y * widthPix + x] = 0xffffffff; + } + } + } + + // 生成二维码图片的格式,使用ARGB_8888 + Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix); + + if (logoBm != null) { + bitmap = addLogo(bitmap, logoBm); + } + + //必须使用compress方法将bitmap保存到文件中再进行读取。直接返回的bitmap是没有任何压缩的,内存消耗巨大! + if (bitmap == null) { + return null; + } + if (!TextUtils.isEmpty(name)) { + bitmap = addTextToBitmap(bitmap, name); + } + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(filePath)); + return bitmap; + } catch (WriterException | IOException e) { + e.printStackTrace(); + } + return null; + } + + public static Bitmap addTextToBitmap(Bitmap bmpSrc, String text) { + int srcWidth = bmpSrc.getWidth(); + int srcHeight = bmpSrc.getHeight(); + + // 先计算text所需要的height + int textSize = srcWidth / 20; + int padding = srcWidth / 200; + int textLinePadding = 1; + // 每行的文字 + int perLineWords = (srcWidth - 2 * padding) / textSize; + int lineNum = text.length() / perLineWords; + lineNum = text.length() % perLineWords == 0 ? lineNum : lineNum + 1; + int textTotalHeight = lineNum * (textSize + textLinePadding) + 2 * padding; + + Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight + textTotalHeight + 10, + Bitmap.Config.ARGB_8888); + try { + Canvas canvas = new Canvas(bitmap); + canvas.drawColor(Color.WHITE); + canvas.drawBitmap(bmpSrc, 0, 0, null); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setColor(Color.BLACK); + paint.setTextSize(textSize); + String lineText; + for (int i = 0, startY = srcHeight + textSize, start, end; i < lineNum; i++) { + start = i * perLineWords; + end = start + perLineWords; + lineText = text.substring(start, end > text.length() ? text.length() : end); + canvas.drawText(lineText, (srcWidth - textSize * lineText.length() + 2 * padding) / 2, startY, paint); + startY += textSize + textLinePadding; + } + canvas.save(); + canvas.restore(); + } catch (Exception e) { + bitmap = null; + e.printStackTrace(); + e.getStackTrace(); + } + return bitmap; + } + + /** + * 生成二维码Bitmap + * + * @param content 内容 + * @param widthPix 图片宽度 + * @param heightPix 图片高度 + * @param logoBm 二维码中心的Logo图标(可以为null) + * @param filePath 用于存储二维码图片的文件路径 + * @return 生成二维码及保存文件是否成功 + */ + private Bitmap createQRImage(String content, int widthPix, int heightPix, Bitmap logoBm, String filePath) { + try { + if (content == null || "".equals(content)) { + return null; + } + //配置参数 + Map hints = new HashMap<>(); + hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); + //容错级别 + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); + //设置空白边距的宽度 +// hints.put(EncodeHintType.MARGIN, 2); //default is 4 + + // 图像数据转换,使用了矩阵转换 + BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, widthPix, heightPix, hints); + int[] pixels = new int[widthPix * heightPix]; + // 下面这里按照二维码的算法,逐个生成二维码的图片, + // 两个for循环是图片横列扫描的结果 + for (int y = 0; y < heightPix; y++) { + for (int x = 0; x < widthPix; x++) { + if (bitMatrix.get(x, y)) { + pixels[y * widthPix + x] = 0xff000000; +// pixels[y * widthPix + x] = 0xFFFF0000; + } else { + pixels[y * widthPix + x] = 0xffffffff; + } + } + } + + // 生成二维码图片的格式,使用ARGB_8888 + Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix); + + if (logoBm != null) { + bitmap = addLogo(bitmap, logoBm); + } + + //必须使用compress方法将bitmap保存到文件中再进行读取。直接返回的bitmap是没有任何压缩的,内存消耗巨大! + if (bitmap == null) { + return null; + } + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(filePath)); + return bitmap; + } catch (WriterException | IOException e) { + e.printStackTrace(); + } + + return null; + } + + /** + * 在二维码中间添加Logo图案 + */ + private Bitmap addLogo(Bitmap src, Bitmap logo) { + if (src == null) { + return null; + } + if (logo == null) { + return src; + } + //获取图片的宽高 + int srcWidth = src.getWidth(); + int srcHeight = src.getHeight(); + int logoWidth = logo.getWidth(); + int logoHeight = logo.getHeight(); + if (srcWidth == 0 || srcHeight == 0) { + return null; + } + if (logoWidth == 0 || logoHeight == 0) { + return src; + } + //logo大小为二维码整体大小的1/5 + float scaleFactor = srcWidth * 1.0f / 5 / logoWidth; + Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888); + try { + Canvas canvas = new Canvas(bitmap); + canvas.drawBitmap(src, 0, 0, null); + canvas.scale(scaleFactor, scaleFactor, srcWidth / 2, srcHeight / 2); + canvas.drawBitmap(logo, (srcWidth - logoWidth) / 2, (srcHeight - logoHeight) / 2, null); + canvas.save(); + canvas.restore(); + } catch (Exception e) { + bitmap = null; + e.getStackTrace(); + } + return bitmap; + } + + /** + * 在二维码中间添加Logo图案 + */ + private Bitmap addBitmap(Bitmap src, Bitmap logo) { + if (src == null) { + return null; + } + if (logo == null) { + return src; + } + //获取图片的宽高 + int srcWidth = src.getWidth(); + int srcHeight = src.getHeight(); + int logoWidth = logo.getWidth(); + int logoHeight = logo.getHeight(); + if (srcWidth == 0 || srcHeight == 0) { + return null; + } + if (logoWidth == 0 || logoHeight == 0) { + return src; + } + //logo大小为二维码整体大小的1/5 +// float scaleFactor = srcWidth * 1.0f / 5 / logoWidth; + Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888); + try { + Canvas canvas = new Canvas(bitmap); + canvas.drawBitmap(src, 0, 0, null); +// canvas.scale(scaleFactor, scaleFactor, srcWidth / 2, srcHeight / 2); + canvas.drawBitmap(logo, (srcWidth - logoWidth) / 2, (srcHeight - logoHeight) / 2, null); + canvas.save(); + canvas.restore(); + } catch (Exception e) { + bitmap = null; + e.getStackTrace(); + } + return bitmap; + } + + private Bitmap createQRImage(String qrCode, int widthPix, int heightPix) { + Bitmap bitmap = null; + try { + //判断URL合法性 + if (qrCode == null || "".equals(qrCode) || qrCode.length() < 1) { + return bitmap; + } + Hashtable hints = new Hashtable(); + hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); + //图像数据转换,使用了矩阵转换 + BitMatrix bitMatrix = new QRCodeWriter().encode(qrCode, BarcodeFormat.QR_CODE, widthPix, heightPix, hints); + int[] pixels = new int[widthPix * heightPix]; + //下面这里按照二维码的算法,逐个生成二维码的图片, + //两个for循环是图片横列扫描的结果 + for (int y = 0; y < heightPix; y++) { + for (int x = 0; x < widthPix; x++) { + if (bitMatrix.get(x, y)) { + pixels[y * widthPix + x] = 0xff000000; +// pixels[y * widthPix + x] = 0xfffd0000; +// pixels[y * widthPix + x] = 0xff3399ff; + } else { + pixels[y * widthPix + x] = 0x00000000; + } + } + } + //生成二维码图片的格式,使用ARGB_8888 + bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix); + //显示到一个ImageView上面 + } catch (WriterException e) { + e.printStackTrace(); + } + return bitmap; + } + + private Flowable makeFlowable(final Callable func) { + return Flowable.create(new FlowableOnSubscribe() { + @Override + public void subscribe(FlowableEmitter e) throws Exception { + try { + e.onNext(func.call()); + } catch (Exception e1) { + } + } + }, BackpressureStrategy.ERROR); + } + + private Observable makeObservable(final Callable func) { + return Observable.create(new ObservableOnSubscribe() { + + @Override + public void subscribe(ObservableEmitter subscriber) throws Exception { + try { + subscriber.onNext(func.call()); + } catch (Exception e) { + } + } + }); + } +} diff --git a/qrutill/src/main/java/com/inlee/qrutill/bean/QRCodeBean.java b/qrutill/src/main/java/com/inlee/qrutill/bean/QRCodeBean.java new file mode 100644 index 0000000..630a59b --- /dev/null +++ b/qrutill/src/main/java/com/inlee/qrutill/bean/QRCodeBean.java @@ -0,0 +1,7 @@ +package com.inlee.qrutill.bean; + +public interface QRCodeBean { + String getFileName(); + + String getQRCodeUrl(); +} diff --git a/settings.gradle b/settings.gradle index d6763d5..2b47d73 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,2 @@ include ':app', ':mvp', ':test',':xrecycler',':utill',':nativelogger' +include ':qrutill' diff --git a/utill/build.gradle b/utill/build.gradle index ff12937..3812fdf 100644 --- a/utill/build.gradle +++ b/utill/build.gradle @@ -4,18 +4,18 @@ apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' -apply plugin: 'com.novoda.bintray-release' -publish { - artifactId = 'lennon-utill' // artifactId - uploadName = 'LennonUtill' // 展示名字 - publishVersion = '1.2.8' // 版本 - userOrg = rootProject.userOrg - groupId = rootProject.groupId - repoName = rootProject.repoName - desc = '' - website = rootProject.website - licences = rootProject.licences -} +//apply plugin: 'com.novoda.bintray-release' +//publish { +// artifactId = 'lennon-utill' // artifactId +// uploadName = 'LennonUtill' // 展示名字 +// publishVersion = '1.3.5' // 版本 +// userOrg = rootProject.userOrg +// groupId = rootProject.groupId +// repoName = rootProject.repoName +// desc = '' +// website = rootProject.website +// licences = rootProject.licences +//} android { compileSdkVersion rootProject.ext.android.compileSdkVersion diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt b/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt new file mode 100644 index 0000000..8ea1074 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt @@ -0,0 +1,110 @@ +package com.lennon.cn.utill.base + +import android.app.Activity +import android.content.Context +import android.view.Display +import android.widget.Toast +import cn.droidlover.xdroidmvp.log.XLog +import cn.droidlover.xdroidmvp.mvp.XPresentation +import cn.droidlover.xdroidmvp.net.NetError +import com.lennon.cn.utill.dialog.CommonAlertDialog +import com.lennon.cn.utill.dialog.CustomProgressDialog +import com.lennon.cn.utill.dialog.OnAlertDialogListener + +abstract class BasePresentation

>(context: Context, display: Display) : + XPresentation

(context, display), BaseView

{ + private var TAG = javaClass.simpleName + private var dialog: CustomProgressDialog? = null + override fun showProgressDialog(msg: String) { + if (dialog != null) dialog!!.dismiss() + dialog = CustomProgressDialog(getContext()) + dialog!!.setMessage(msg) + dialog!!.show() + } + + override fun useEventBus(): Boolean { + return true + } + + override fun closeProgressDialog() { + if (null != dialog) dialog!!.dismiss() + } + + override fun toast(msg: String, second: Int) { + val dialog = CommonAlertDialog(context) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.setCanceledOnTouchOutside(false) + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + } + }) + Toast.makeText(getContext(), msg, second) + .show() + } + + override fun toast(msg: String) { + toast(msg, true) + } + + override fun toast(msg: String, runnable: Runnable) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.show() + dialog.setCanceledOnTouchOutside(false) + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + runnable.run() + } + }) + Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) + .show() + } + + override fun toast(msg: String, flag: Boolean) { + if (flag) { + val dialog = CommonAlertDialog(context) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.setCanceledOnTouchOutside(false) + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + } + }) + } + Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) + .show() + } + + override fun showLoading(visibility: Int) { + } + + override fun showLoadingError(errorType: NetError) { + + } + + override fun getActivity(): Activity? { + return null + } + + override fun bindEvent() { + + } + + override fun getOptionsMenuId(): Int { + return 0 + } + + override fun onRefresh(bRefresh: Boolean) { + XLog.e("$TAG:onRefresh()") + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt b/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt index 19eed4e..77e36b2 100644 --- a/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt +++ b/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt @@ -102,5 +102,11 @@ class Lennon { } provider!!.restartApp() } + fun setDensity(orientation: DensityUtils.Density) { + if (provider == null) { + throw Throwable("请先注册provider") + } + provider!!.setDensity(orientation) + } } } \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt b/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt index fa5e4fe..c1abc22 100644 --- a/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt +++ b/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt @@ -1,8 +1,10 @@ package com.lennon.cn.utill.conf import android.app.Activity +import android.graphics.drawable.GradientDrawable import cn.droidlover.xdroidmvp.net.NetError import com.lennon.cn.utill.utill.DensityUtils +import com.lennon.cn.utill.utill.Utill abstract class LennonProvider { abstract fun appName(): String @@ -13,12 +15,13 @@ abstract class LennonProvider { abstract fun clean() abstract fun isTest(): Boolean abstract fun getFilePathName(): String + private var orientation: DensityUtils.Density = DensityUtils.Density.WIDTH + fun setDensity(orientation: DensityUtils.Density) { + this.orientation = orientation + } + open fun useDensity(activity: Activity) { -// if (Utill.isPad(activity)) { -// DensityUtils.setOrientation(activity, DensityUtils.HEIGHT) -// } else { - DensityUtils.setDefault(activity) -// } + DensityUtils.setOrientation(activity, orientation) } abstract fun handleNetError(error: NetError): Boolean diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt b/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt index 53fcf17..ba2a234 100644 --- a/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt +++ b/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt @@ -12,8 +12,12 @@ object DensityUtils { private var appScaledDensity: Float = 0.toFloat() private var appDisplayMetrics: DisplayMetrics? = null private var barHeight: Int = 0 - val WIDTH = "width" - val HEIGHT = "height" +// val WIDTH = "width" +// val HEIGHT = "height" + + enum class Density { + WIDTH, HEIGHT; + } /** * 在Application里初始化一下 @@ -50,7 +54,7 @@ object DensityUtils { * @param activity */ fun setDefault(activity: Activity) { - setAppOrientation(activity, WIDTH) + setAppOrientation(activity, Density.WIDTH) } /** @@ -59,18 +63,20 @@ object DensityUtils { * @param activity * @param orientation */ - fun setOrientation(activity: Activity, orientation: String) { + fun setOrientation(activity: Activity, orientation: Density) { setAppOrientation(activity, orientation) } + /** * 此方法用于在某一个Activity里面更改适配的方向 * 在setContentView()之前设置 * @param activity * @param orientation */ - fun setOrientation(activity: Activity, orientation: String,f: Float) { - setAppOrientation(activity, orientation,f) + fun setOrientation(activity: Activity, orientation: String, f: Float) { + setAppOrientation(activity, orientation, f) } + /** * targetDensity * targetScaledDensity @@ -109,9 +115,9 @@ object DensityUtils { * 这三个参数是统一修改过后的值 * orientation:方向值,传入width或height */ - private fun setAppOrientation(activity: Activity, orientation: String) { + private fun setAppOrientation(activity: Activity, orientation: Density) { val targetDensity: Float - if (orientation == "height") { + if (orientation == Density.HEIGHT) { targetDensity = (appDisplayMetrics!!.heightPixels - barHeight) / 1080f//设计图的高度 单位:dp } else { targetDensity = appDisplayMetrics!!.widthPixels / 420f//设计图的宽度 单位:dp diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/CycleWheelView.java b/utill/src/main/java/com/lennon/cn/utill/widget/CycleWheelView.java new file mode 100644 index 0000000..42512fd --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/CycleWheelView.java @@ -0,0 +1,597 @@ +package com.lennon.cn.utill.widget; +/** + * Copyright (C) 2015 + *

+ * CycleWheelView.java + *

+ * Description: + *

+ * Author: Liao Longhui + *

+ * Ver 1.0, 2015-07-15, Liao Longhui, Create file + */ + + +import android.content.Context; +import android.graphics.*; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; + +import lennon.com.utill.R; + + +/** + * 可循环滚动的选择器 + * + * @author Liao Longhui + */ +public class CycleWheelView extends ListView { + + public static final String TAG = CycleWheelView.class.getSimpleName(); + + private static final int COLOR_DIVIDER_DEFALUT = Color.parseColor("#ffffff"); + + private static final int HEIGHT_DIVIDER_DEFAULT = 2; + + private static final int COLOR_SOLID_DEFAULT = Color.parseColor("#ffffff"); + + private static final int COLOR_SOLID_SELET_DEFAULT = Color.parseColor("#ffffff"); + + private static final int WHEEL_SIZE_DEFAULT = 3; + + + private Handler mHandler; + + + private CycleWheelViewAdapter mAdapter; + + /** + * Labels + */ + + private List mLabels; + + /** + * Color Of Selected Label + */ + + private int mLabelSelectColor = Color.WHITE; + + /** + * Color Of Unselected Label + */ + + private int mLabelColor = Color.GRAY; + + /** + * Gradual Alph + */ + + private float mAlphaGradual = 0.7f; + + /** + * Color Of Divider + */ + + private int dividerColor = COLOR_DIVIDER_DEFALUT; + + /** + * Height Of Divider + */ + + private int dividerHeight = HEIGHT_DIVIDER_DEFAULT; + + /** + * Color of Selected Solid + */ + + private int seletedSolidColor = COLOR_SOLID_SELET_DEFAULT; + + /** + * Color of Unselected Solid + */ + + private int solidColor = COLOR_SOLID_DEFAULT; + + /** + * Size Of Wheel , it should be odd number like 3 or greater + */ + + private int mWheelSize = WHEEL_SIZE_DEFAULT; + + /** + * res Id of Wheel Item Layout + */ + + private int mItemLayoutId; + + /** + * res Id of Label TextView + */ + + private int mItemLabelTvId; + + /** + * Height of Wheel Item + */ + + private int mItemHeight; + + + private boolean cylceEnable; + + + private int mCurrentPositon; + + + private WheelItemSelectedListener mItemSelectedListener; + + + public CycleWheelView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + + public CycleWheelView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + + public CycleWheelView(Context context) { + super(context); + } + + + private void init() { + mHandler = new Handler(); + mItemLayoutId = R.layout.item_cyclewheel; + mItemLabelTvId = R.id.tv_label_item_wheel; + mAdapter = new CycleWheelViewAdapter(); + setVerticalScrollBarEnabled(false); + setScrollingCacheEnabled(false); + setCacheColorHint(Color.TRANSPARENT); + setFadingEdgeLength(0); + setOverScrollMode(OVER_SCROLL_NEVER); + setDividerHeight(0); + setAdapter(mAdapter); + setOnScrollListener(new OnScrollListener() { + @Override + + + public void onScrollStateChanged(AbsListView view, int scrollState) { + if (scrollState == SCROLL_STATE_IDLE) { + View itemView = getChildAt(0); + if (itemView != null) { + float deltaY = itemView.getY(); + if (deltaY == 0) { + return; + } + if (Math.abs(deltaY) < mItemHeight / 2) { + smoothScrollBy(getDistance(deltaY), 50); + } else { + smoothScrollBy(getDistance(mItemHeight + deltaY), 50); + } + } + } + } + + + @Override + + + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, + int totalItemCount) { + refreshItems(); + } + + + }); + } + + + private int getDistance(float scrollDistance) { + if (Math.abs(scrollDistance) <= 2) { + return (int) scrollDistance; + } else if (Math.abs(scrollDistance) < 12) { + return scrollDistance > 0 ? 2 : -2; + } else { + return (int) (scrollDistance / 6); + } + } + + private void refreshItems() { + int offset = mWheelSize / 2; + int firstPosition = getFirstVisiblePosition(); + int position = 0; + if (getChildAt(0) == null) { + return; + } + if (Math.abs(getChildAt(0).getY()) <= mItemHeight / 2) { + position = firstPosition + offset; + } else { + position = firstPosition + offset + 1; + } + if (position == mCurrentPositon) { + return; + } + mCurrentPositon = position; + if (mItemSelectedListener != null) { + mItemSelectedListener.onItemSelected(getSelection(), getSelectLabel()); + } + resetItems(firstPosition, position, offset); + } + + private void resetItems(int firstPosition, int position, int offset) { + for (int i = position - offset - 1; i < position + offset + 1; i++) { + View itemView = getChildAt(i - firstPosition); + if (itemView == null) { + continue; + } + TextView labelTv = (TextView) itemView.findViewById(mItemLabelTvId); + if (position == i) { + labelTv.setTextColor(mLabelSelectColor); + labelTv.setTextSize(18); + itemView.setAlpha(1f); + } else { + labelTv.setTextColor(mLabelColor); + labelTv.setTextSize(16); + int delta = Math.abs(i - position); + double alpha = Math.pow(mAlphaGradual, delta); + itemView.setAlpha((float) alpha); + } + } + } + + + /** + * 设置滚轮的刻度列表 + * + * @param labels + */ + + + public void setLabels(List labels) { + mLabels = labels; + mAdapter.setData(mLabels); + mAdapter.notifyDataSetChanged(); + initView(); + } + + + /** + * 设置滚轮滚动监听 + * + * @param mItemSelectedListener + */ + + + public void setOnWheelItemSelectedListener(WheelItemSelectedListener mItemSelectedListener) { + this.mItemSelectedListener = mItemSelectedListener; + } + + + /** + * 获取滚轮的刻度列表 + * + * @return + */ + + + public List getLabels() { + return mLabels; + } + + + /** + * 设置滚轮是否为循环滚动 + * + * @param enable true-循环 false-单程 + */ + + + public void setCycleEnable(boolean enable) { + if (cylceEnable != enable) { + cylceEnable = enable; + mAdapter.notifyDataSetChanged(); + setSelection(getSelection()); + } + } + + + /* +* 滚动到指定位置 +*/ + + @Override + + + public void setSelection(final int position) { + mHandler.post(new Runnable() { + @Override + public void run() { + CycleWheelView.super.setSelection(getPosition(position)); + } + }); + } + + + private int getPosition(int positon) { + if (mLabels == null || mLabels.size() == 0) { + return 0; + } + if (cylceEnable) { + int d = Integer.MAX_VALUE / 2 / mLabels.size(); + return positon + d * mLabels.size(); + } + return positon; + } + + + /** + * 获取当前滚轮位置 + * + * @return + */ + + + public int getSelection() { + if (mCurrentPositon == 0) { + mCurrentPositon = mWheelSize / 2; + } + return (mCurrentPositon - mWheelSize / 2) % mLabels.size(); + } + + + /** + * 获取当前滚轮位置的刻度 + * + * @return + */ + + + public String getSelectLabel() { + int position = getSelection(); + position = position < 0 ? 0 : position; + try { + return mLabels.get(position); + } catch (Exception e) { + return ""; + } + } + + + /** + * 如果需要自定义滚轮每个Item,调用此方法设置自定义Item布局,自定义布局中需要一个TextView来显示滚轮刻度 + * + * @param itemResId 布局文件Id + * @param labelTvId 刻度TextView的资源Id + */ + + + public void setWheelItemLayout(int itemResId, int labelTvId) { + mItemLayoutId = itemResId; + mItemLabelTvId = labelTvId; + mAdapter = new CycleWheelViewAdapter(); + mAdapter.setData(mLabels); + setAdapter(mAdapter); + initView(); + } + + + /*** + * 设置未选中刻度文字颜色 + * + * @param labelColor + */ + + + public void setLabelColor(int labelColor) { + this.mLabelColor = labelColor; + resetItems(getFirstVisiblePosition(), mCurrentPositon, mWheelSize / 2); + } + + + /** + * 设置选中刻度文字颜色 + * + * @param labelSelectColor + */ + + + public void setLabelSelectColor(int labelSelectColor) { + this.mLabelSelectColor = labelSelectColor; + resetItems(getFirstVisiblePosition(), mCurrentPositon, mWheelSize / 2); + } + + + /** + * 设置滚轮刻度透明渐变值 + * + * @param alphaGradual + */ + + + public void setAlphaGradual(float alphaGradual) { + this.mAlphaGradual = alphaGradual; + resetItems(getFirstVisiblePosition(), mCurrentPositon, mWheelSize / 2); + } + + + /** + * 设置滚轮可显示的刻度数量,必须为奇数,且大于等于3 + * + * @param wheelSize + * @throws CycleWheelViewException 滚轮数量错误 + */ + + + public void setWheelSize(int wheelSize){ + if (wheelSize < 3 || wheelSize % 2 != 1) { +// throw new CycleWheelViewException("Wheel Size Error , Must Be 3,5,7,9..."); + mWheelSize = WHEEL_SIZE_DEFAULT; + } else { + mWheelSize = wheelSize; + initView(); + } + } + + /** + * 设置块的颜色 + * + * @param unselectedSolidColor 未选中的块的颜色 + * @param selectedSolidColor 选中的块的颜色 + */ + + public void setSolid(int unselectedSolidColor, int selectedSolidColor) { + this.solidColor = unselectedSolidColor; + this.seletedSolidColor = selectedSolidColor; + initView(); + } + + /** + * 设置分割线样式 + * + * @param dividerColor 分割线颜色 + * @param dividerHeight 分割线高度(px) + */ + + public void setDivider(int dividerColor, int dividerHeight) { + this.dividerColor = dividerColor; + this.dividerHeight = dividerHeight; + } + + @SuppressWarnings("deprecation") + private void initView() { + mItemHeight = measureHeight(); + ViewGroup.LayoutParams lp = getLayoutParams(); + lp.height = mItemHeight * mWheelSize; + mAdapter.setData(mLabels); + mAdapter.notifyDataSetChanged(); + Drawable backgroud = new Drawable() { + @Override + public void draw(Canvas canvas) { + int viewWidth = getWidth(); + Paint dividerPaint = new Paint(); + dividerPaint.setColor(dividerColor); + dividerPaint.setStrokeWidth(dividerHeight); + Paint seletedSolidPaint = new Paint(); + seletedSolidPaint.setColor(seletedSolidColor); + Paint solidPaint = new Paint(); + solidPaint.setColor(solidColor); + canvas.drawRect(0, 0, viewWidth, mItemHeight * (mWheelSize / 2), solidPaint); + canvas.drawRect(0, mItemHeight * (mWheelSize / 2 + 1), viewWidth, mItemHeight + * (mWheelSize), solidPaint); + canvas.drawRect(0, mItemHeight * (mWheelSize / 2), viewWidth, mItemHeight + * (mWheelSize / 2 + 1), seletedSolidPaint); + canvas.drawLine(0, mItemHeight * (mWheelSize / 2), viewWidth, mItemHeight + * (mWheelSize / 2), dividerPaint); + canvas.drawLine(0, mItemHeight * (mWheelSize / 2 + 1), viewWidth, mItemHeight + * (mWheelSize / 2 + 1), dividerPaint); + } + + @Override + public void setAlpha(int alpha) { + } + + @Override + public void setColorFilter(ColorFilter cf) { + } + + @Override + public int getOpacity() { + return PixelFormat.UNKNOWN; + } + }; + setBackgroundDrawable(backgroud); + } + + private int measureHeight() { + View itemView = LayoutInflater.from(getContext()).inflate(mItemLayoutId, null); + itemView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + int w = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + int h = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + itemView.measure(w, h); + int height = itemView.getMeasuredHeight(); + // int width = view.getMeasuredWidth(); + return height; + } + + public interface WheelItemSelectedListener { + public void onItemSelected(int position, String label); + } + + public class CycleWheelViewException extends Exception { + + private static final long serialVersionUID = 1L; + + public CycleWheelViewException(String detailMessage) { + super(detailMessage); + } + } + + + public class CycleWheelViewAdapter extends BaseAdapter { + private List mData = new ArrayList(); + + public void setData(List mWheelLabels) { + mData.clear(); + mData.addAll(mWheelLabels); + } + + @Override + + + public int getCount() { + if (cylceEnable) { + return Integer.MAX_VALUE; + } + return mData.size() + mWheelSize - 1; + } + + + @Override + public Object getItem(int position) { + return ""; + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public boolean isEnabled(int position) { + return false; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = LayoutInflater.from(getContext()).inflate(mItemLayoutId, null); + } + TextView textView = (TextView) convertView.findViewById(mItemLabelTvId); + if (position < mWheelSize / 2 + || (!cylceEnable && position >= mData.size() + mWheelSize / 2)) { + textView.setText(""); + convertView.setVisibility(View.INVISIBLE); + } else { + textView.setText(mData.get((position - mWheelSize / 2) % mData.size())); + convertView.setVisibility(View.VISIBLE); + } + return convertView; + } + } +} \ No newline at end of file diff --git a/utill/src/main/res/layout/headbar.xml b/utill/src/main/res/layout/headbar.xml index b30362e..b03c19f 100644 --- a/utill/src/main/res/layout/headbar.xml +++ b/utill/src/main/res/layout/headbar.xml @@ -2,7 +2,7 @@ + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:orientation="vertical" + android:padding="@dimen/space_5"> + android:layout_height="@dimen/space_50" + android:contentDescription="TODO" /> + android:gravity="center" + android:textColor="@color/color_333333" /> \ No newline at end of file diff --git a/utill/src/main/res/layout/item_cyclewheel.xml b/utill/src/main/res/layout/item_cyclewheel.xml new file mode 100644 index 0000000..e90b18f --- /dev/null +++ b/utill/src/main/res/layout/item_cyclewheel.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file From 1448da283a388d2c41ddcf92a26e0efded31a22e Mon Sep 17 00:00:00 2001 From: Edit <1136160757@qq.com> Date: Tue, 28 Apr 2020 16:13:41 +0800 Subject: [PATCH 13/24] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=B1=8F=E5=B9=95?= =?UTF-8?q?=E9=80=82=E9=85=8D=E6=8E=A7=E5=88=B6=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utill/build.gradle | 24 +++++++++---------- .../java/com/lennon/cn/utill/conf/Lennon.kt | 2 +- .../lennon/cn/utill/conf/LennonProvider.kt | 10 ++++++-- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/utill/build.gradle b/utill/build.gradle index 3812fdf..5d8fbfe 100644 --- a/utill/build.gradle +++ b/utill/build.gradle @@ -4,18 +4,18 @@ apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' -//apply plugin: 'com.novoda.bintray-release' -//publish { -// artifactId = 'lennon-utill' // artifactId -// uploadName = 'LennonUtill' // 展示名字 -// publishVersion = '1.3.5' // 版本 -// userOrg = rootProject.userOrg -// groupId = rootProject.groupId -// repoName = rootProject.repoName -// desc = '' -// website = rootProject.website -// licences = rootProject.licences -//} +apply plugin: 'com.novoda.bintray-release' +publish { + artifactId = 'lennon-utill' // artifactId + uploadName = 'LennonUtill' // 展示名字 + publishVersion = '1.3.6' // 版本 + userOrg = rootProject.userOrg + groupId = rootProject.groupId + repoName = rootProject.repoName + desc = '' + website = rootProject.website + licences = rootProject.licences +} android { compileSdkVersion rootProject.ext.android.compileSdkVersion diff --git a/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt b/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt index 77e36b2..50577e4 100644 --- a/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt +++ b/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt @@ -85,7 +85,7 @@ class Lennon { if (provider == null) { DensityUtils.setDefault(activity) } else { - provider!!.useDensity(activity) + provider!!.setDensity(activity) } } diff --git a/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt b/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt index c1abc22..1da5e7a 100644 --- a/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt +++ b/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt @@ -20,8 +20,14 @@ abstract class LennonProvider { this.orientation = orientation } - open fun useDensity(activity: Activity) { - DensityUtils.setOrientation(activity, orientation) + open fun setDensity(activity: Activity) { + if (useDensity()) { + DensityUtils.setOrientation(activity, orientation) + } + } + + open fun useDensity(): Boolean { + return true } abstract fun handleNetError(error: NetError): Boolean From 43669fa9201b2ea3c2eecc9010d2a49ea67c08c9 Mon Sep 17 00:00:00 2001 From: Edit <1136160757@qq.com> Date: Mon, 22 Jun 2020 17:31:47 +0800 Subject: [PATCH 14/24] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E9=83=A8?= =?UTF-8?q?=E5=88=86=E4=BB=A3=E7=A0=81=EF=BC=8C=E5=8A=A0=E5=85=A5=E4=BA=86?= =?UTF-8?q?=E5=AE=9A=E6=97=B6=E7=BB=93=E6=9D=9F=E5=BC=B9=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/layout/view_loading.xml | 20 +- build.gradle | 19 +- conf.gradle | 80 +++--- gradle/wrapper/gradle-wrapper.properties | 4 +- mvp/build.gradle | 75 +++++- nativelogger/build.gradle | 83 +++++-- qrutill/build.gradle | 92 +++++-- .../java/com/inlee/qrutill/QRCodeTool.java | 235 ++++++------------ settings.gradle | 7 +- utill/build.gradle | 106 ++++++-- .../com/lennon/cn/utill/base/BaseActivity.kt | 39 ++- .../lennon/cn/utill/base/BaseApplication.kt | 11 +- .../com/lennon/cn/utill/base/BaseFragment.kt | 29 ++- .../lennon/cn/utill/base/BaseLazyFragment.kt | 29 ++- .../lennon/cn/utill/base/BasePresentation.kt | 29 ++- .../java/com/lennon/cn/utill/base/BaseView.kt | 4 +- .../lennon/cn/utill/bean/ToastRunnable.java | 21 ++ .../com/lennon/cn/utill/cache/DataCache.java | 6 +- .../lennon/cn/utill/cache/DataListCache.java | 6 +- xrecycler/build.gradle | 71 +++++- 20 files changed, 688 insertions(+), 278 deletions(-) create mode 100644 utill/src/main/java/com/lennon/cn/utill/bean/ToastRunnable.java diff --git a/app/src/main/res/layout/view_loading.xml b/app/src/main/res/layout/view_loading.xml index cfd8d8a..dd43976 100644 --- a/app/src/main/res/layout/view_loading.xml +++ b/app/src/main/res/layout/view_loading.xml @@ -1,16 +1,16 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="#ffffff"> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:visibility="visible" + app:indicatorColor="@color/colorPrimary" + app:indicatorName="LineScale" /> diff --git a/build.gradle b/build.gradle index bc3fb77..35bdca1 100644 --- a/build.gradle +++ b/build.gradle @@ -2,9 +2,10 @@ apply from: "conf.gradle" buildscript { - ext.kotlin_version = '1.3.71' + ext.kotlin_version = '1.3.72' repositories { maven {url 'https://dl.bintray.com/lennon-xia/lennon/'} + maven {url 'http://127.0.0.1:8081/repository/lennon/' } jcenter() { url 'https://maven.aliyun.com/repository/jcenter' } jcenter() @@ -14,28 +15,24 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:3.6.3' + classpath 'com.android.tools.build:gradle:4.0.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' - classpath 'com.novoda:bintray-release:0.9.1' + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' + classpath 'com.github.dcendents:android-maven-plugin:1.2' +// classpath 'com.novoda:bintray-release:+' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } -ext{ - userOrg = 'lennon-xia' // 用户名 - repoName = "lennon" // 仓库名 - groupId = 'com.lennon.utill' // groupId - desc = 'useful tool for android' // 描述 - website = 'https://github.com/lennon19940306/XDroidMvp-AndroidX' // 地址 , 填写真实可访问地址 - licences = ['Apache-2.0']// 描述 , 填写licences -} + allprojects { repositories { maven {url 'https://dl.bintray.com/lennon-xia/lennon/'} jcenter() { url 'https://maven.aliyun.com/repository/jcenter' } + maven {url 'http://127.0.0.1:8081/repository/lennon/' } mavenCentral() maven { diff --git a/conf.gradle b/conf.gradle index 27f91c6..90e17a0 100644 --- a/conf.gradle +++ b/conf.gradle @@ -1,5 +1,21 @@ ext { - + siteUrl = 'https://github.com/lennon19940306/XDroidMvp-AndroidX' + // #修改# // 项目的主页地址,我这里是我的PickerView项目在github的链接地址 + gitUrl = 'https://github.com/lennon19940306/XDroidMvp-AndroidX.git' + // #修改# // 项目 git 地址,我这里同样是用Github上的git地址 + group = "com.lennon.utill" + // #修改# // 组名称,这个相当于依赖的时候 compile 'com.bigkoo:pickerview:1.0.1' “:”号前面的前缀 + userOrg = 'lennon-xia' // 用户名 + repoName = "lennon" // 仓库名 + groupId = 'com.lennon.utill' // groupId +// ext{ +// userOrg = 'lennon-xia' // 用户名 +// repoName = "lennon" // 仓库名 +// groupId = 'com.lennon.utill' // groupId +// desc = 'useful tool for android' // 描述 +// website = 'https://github.com/lennon19940306/XDroidMvp-AndroidX' // 地址 , 填写真实可访问地址 +// licences = ['Apache-2.0']// 描述 , 填写licences +// } android = [ compileSdkVersion: 29, @@ -10,15 +26,24 @@ ext { versionName : '1.0.0', VSupportSdk : '27.1.1', - VRetrofitSdk : '2.8.1', - VOkhttp : '4.4.1', + VRetrofitSdk : '2.9.0', + VOkhttp : '4.7.2', VRxlifecycle : '3.1.0', - NavigationSDK : "2.2.1", + NavigationSDK : "2.2.2", ] dependencies = [ - "zxing" : "com.google.zxing:core:3.3.3", + + "annotations" : "androidx.annotation:annotation:1.1.0", + + "constraintlayout" : "androidx.constraintlayout:constraintlayout:1.1.3", + + "glide" : "com.github.bumptech.glide:glide:4.11.0", + "glide-compiler" : "com.github.bumptech.glide:compiler:4.11.0", + + + "zxing" : "com.google.zxing:core:3.4.0", "navigation-fragment-ktx" : "androidx.navigation:navigation-fragment-ktx:${android["NavigationSDK"]}", "navigation-ui-ktx" : "androidx.navigation:navigation-ui-ktx:${android["NavigationSDK"]}", @@ -26,9 +51,9 @@ ext { "navigation-ui" : "androidx.navigation:navigation-ui:${android["NavigationSDK"]}", "navigation-fragment" : "androidx.navigation:navigation-fragment:${android["NavigationSDK"]}", - "constraintlayout" : "androidx.constraintlayout:constraintlayout:2.0.0-beta4", - "exifinterface" : "androidx.exifinterface:exifinterface:1.3.0-alpha01", + + "exifinterface" : "androidx.exifinterface:exifinterface:1.2.0", "multidex" : "androidx.multidex:multidex:2.0.1", @@ -36,32 +61,31 @@ ext { "PersistentCookieJar" : "com.github.franmontiel:PersistentCookieJar:1.0.1", - "qiujuer-ui" : "net.qiujuer.genius:ui:2.1.1", + "qiujuer-ui" : "net.qiujuer.genius:ui:+", - "appcompat-v7" : "androidx.appcompat:appcompat:1.2.0-alpha02", + "appcompat-v7" : "androidx.appcompat:appcompat:1.1.0", "support-v4" : "androidx.legacy:legacy-support-v4:1.0.0", - "design" : "com.google.android.material:material:1.2.0-alpha05", + "design" : "com.google.android.material:material:1.0.0", - "annotations" : "androidx.annotation:annotation:1.1.0", - "recyclerview-v7" : "androidx.recyclerview:recyclerview:1.2.0-alpha01", - "eventbus" : "org.greenrobot:eventbus:3.0.0", - "glide" : "com.github.bumptech.glide:glide:4.11.0", - "glide-compiler" : "com.github.bumptech.glide:compiler:4.11.0", - "picasso" : "com.squareup.picasso:picasso:2.5.2", + "recyclerview-v7" : "androidx.recyclerview:recyclerview:1.1.0", + + "eventbus" : "org.greenrobot:eventbus:+", + + "picasso" : "com.squareup.picasso:picasso:+", - "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.3", - "xdroid-mvp" : "com.lennon.utill:xdroid-mvp:1.1.8", + "xrecyclerview" : "com.lennon.utill:xrecycler:+", + "xdroid-mvp" : "com.lennon.utill:xdroid-mvp:+", - "avi-loading" : "com.wang.avi:library:1.0.2", - "rxbus" : "com.blankj:rxbus:1.2", + "avi-loading" : "com.wang.avi:library:+", + "rxbus" : "com.blankj:rxbus:+", - "gson" : "com.google.code.gson:gson:2.8.6", - "rxandroid" : "io.reactivex.rxjava2:rxandroid:2.1.1", - "rxjava" : "io.reactivex.rxjava2:rxjava:2.2.19", + "gson" : "com.google.code.gson:gson:+", + "rxandroid" : "io.reactivex.rxjava2:rxandroid:+", + "rxjava" : "io.reactivex.rxjava2:rxjava:+", "retrofit" : "com.squareup.retrofit2:retrofit:${android["VRetrofitSdk"]}", "retrofit-converter-gson" : "com.squareup.retrofit2:converter-gson:${android["VRetrofitSdk"]}", @@ -75,14 +99,14 @@ ext { "rxlifecycle-components" : "com.trello.rxlifecycle3:rxlifecycle-components:${android["VRxlifecycle"]}", - "rxpermissions" : "com.tbruyelle.rxpermissions2:rxpermissions:0.9.3@aar", + "rxpermissions" : "com.tbruyelle.rxpermissions2:rxpermissions:latest.integration", - "canary-debug" : "com.squareup.leakcanary:leakcanary-android:1.4-beta2", - "canary-release" : "com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2", + "canary-debug" : "com.squareup.leakcanary:leakcanary-android:+", + "canary-release" : "com.squareup.leakcanary:leakcanary-android-no-op:+", - "XStateController" : "com.github.limedroid:XStateController:v1.1.1", + "XStateController" : "com.github.limedroid:XStateController:+", - "nativelogger" : "com.lennon.utill:nativelogger:1.0" + "nativelogger" : "com.lennon.utill:nativelogger:+" ] } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 03241c1..4fc515c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Mar 27 15:02:30 CST 2020 +#Wed Jun 17 10:11:02 CST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip diff --git a/mvp/build.gradle b/mvp/build.gradle index a5b3d7d..18d40fd 100644 --- a/mvp/build.gradle +++ b/mvp/build.gradle @@ -4,15 +4,17 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-android-extensions' - +// 插件。无需另外单独配置插件仓库地址 +apply plugin: 'maven-publish' +apply plugin: 'com.jfrog.bintray' //apply plugin: 'com.novoda.bintray-release' // //publish { // artifactId = 'xdroid-mvp' // artifactId // uploadName = 'XDroidMvp' // 展示名字 -// publishVersion = '1.1.8' // 版本 +// publishVersion = '1.1.9' // 版本 // userOrg = rootProject.userOrg -// groupId = rootProject.groupId +// groupId = // repoName = rootProject.repoName // desc = '' // website = rootProject.website @@ -72,10 +74,77 @@ tasks.withType(JavaCompile) { options.encoding = "UTF-8" } +// 定义参数 +def gitUrl = rootProject.ext.siteUrl // Git仓库的url +def groupIdDefined = rootProject.ext.groupId +def artifactIdDefined = "xdroid-mvp" +def uploadNameDefined = 'XDroidMvp' +def versionDefined = "1.2.0" + +// bintray平台信息配置 +Properties properties = new Properties() +properties.load(project.rootProject.file('local.properties').newDataInputStream()) +bintray { + user = properties.getProperty("bintray.user") // local.properties里设置 + key = properties.getProperty("bintray.apikey") // local.properties里设置 + publications = ["MavenPublication"] // 'MyPublication'与下面的publishing闭包里的名称对应 + publish = true // 上传后立即发布到Bintray平台 + pkg { + repo = rootProject.ext.repoName // 必填。bintray平台仓库名,必须已经创建过。 + name = "$uploadNameDefined" // 必填。仓库里包package的名称,没有的话会自动创建。 + licenses = ["Apache-2.0"] // 首次创建package则必须,否则选填。 + vcsUrl = gitUrl // 首次创建package则必须,否则选填。 + version { + name = "$versionDefined" + } + } +} +// 生成sourceJar和javaDocJar构件 task sourcesJar(type: Jar) { from android.sourceSets.main.java.srcDirs classifier = 'sources' } + +task javadoc(type: Javadoc) { + failOnError false + source = android.sourceSets.main.java.sourceFiles + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + classpath += configurations.compile +} +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + artifacts { archives sourcesJar + archives javadocJar } + +// 构件文件和POM信息配置 +publishing { + publications { + MyPublication(MavenPublication) { + artifact("$buildDir/outputs/aar/nicelogger-release.aar") + artifact sourcesJar + artifact javadocJar + groupId "$groupIdDefined" + artifactId "$artifactIdDefined" + version "$versionDefined" + pom.withXml { + def dependenciesNode = asNode().appendNode('dependencies') + // Iterate over the implementation dependencies (we don't want the test ones), adding a node for each + configurations.implementation.allDependencies.each { + // Ensure dependencies such as fileTree are not included in the pom. + if (it.name != 'unspecified') { + def dependencyNode = dependenciesNode.appendNode('dependency') + dependencyNode.appendNode('groupId', it.group) + dependencyNode.appendNode('artifactId', it.name) + dependencyNode.appendNode('version', it.version) + } + } + } + } + } +} + diff --git a/nativelogger/build.gradle b/nativelogger/build.gradle index ae7ce00..4ca421f 100644 --- a/nativelogger/build.gradle +++ b/nativelogger/build.gradle @@ -1,19 +1,8 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' - -//apply plugin: 'com.novoda.bintray-release' -// -//publish { -// artifactId = 'nativelogger' // artifactId -// uploadName = 'NativeLogger' // 展示名字 -// publishVersion = '1.0' // 版本 -// userOrg = rootProject.userOrg -// groupId = rootProject.groupId -// repoName = rootProject.repoName -// desc = '' -// website = rootProject.website -// licences = rootProject.licences -//} +// 插件。无需另外单独配置插件仓库地址 +apply plugin: 'maven-publish' +apply plugin: 'com.jfrog.bintray' android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -40,10 +29,76 @@ tasks.withType(JavaCompile) { options.encoding = "UTF-8" } +// 定义参数 +def gitUrl = rootProject.ext.siteUrl // Git仓库的url +def groupIdDefined = rootProject.ext.groupId +def artifactIdDefined = "nativelogger" +def uploadNameDefined = 'NativeLogger' +def versionDefined = "1.0" + +// bintray平台信息配置 +Properties properties = new Properties() +properties.load(project.rootProject.file('local.properties').newDataInputStream()) +bintray { + user = properties.getProperty("bintray.user") // local.properties里设置 + key = properties.getProperty("bintray.apikey") // local.properties里设置 + publications = ["MavenPublication"] // 'MyPublication'与下面的publishing闭包里的名称对应 + publish = true // 上传后立即发布到Bintray平台 + pkg { + repo = rootProject.ext.repoName // 必填。bintray平台仓库名,必须已经创建过。 + name = "$uploadNameDefined" // 必填。仓库里包package的名称,没有的话会自动创建。 + licenses = ["Apache-2.0"] // 首次创建package则必须,否则选填。 + vcsUrl = gitUrl // 首次创建package则必须,否则选填。 + version { + name = "$versionDefined" + } + } +} +// 生成sourceJar和javaDocJar构件 task sourcesJar(type: Jar) { from android.sourceSets.main.java.srcDirs classifier = 'sources' } + +task javadoc(type: Javadoc) { + failOnError false + source = android.sourceSets.main.java.sourceFiles + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + classpath += configurations.compile +} +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + artifacts { archives sourcesJar + archives javadocJar +} + +// 构件文件和POM信息配置 +publishing { + publications { + MyPublication(MavenPublication) { + artifact("$buildDir/outputs/aar/nicelogger-release.aar") + artifact sourcesJar + artifact javadocJar + groupId "$groupIdDefined" + artifactId "$artifactIdDefined" + version "$versionDefined" + pom.withXml { + def dependenciesNode = asNode().appendNode('dependencies') + // Iterate over the implementation dependencies (we don't want the test ones), adding a node for each + configurations.implementation.allDependencies.each { + // Ensure dependencies such as fileTree are not included in the pom. + if (it.name != 'unspecified') { + def dependencyNode = dependenciesNode.appendNode('dependency') + dependencyNode.appendNode('groupId', it.group) + dependencyNode.appendNode('artifactId', it.name) + dependencyNode.appendNode('version', it.version) + } + } + } + } + } } diff --git a/qrutill/build.gradle b/qrutill/build.gradle index ea85055..2b95ae6 100644 --- a/qrutill/build.gradle +++ b/qrutill/build.gradle @@ -1,19 +1,9 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' - -//apply plugin: 'com.novoda.bintray-release' -//publish { -// artifactId = 'lennon-qr-utill' // artifactId -// uploadName = 'LennonQRUtill' // 展示名字 -// publishVersion = '1.0.0' // 版本 -// userOrg = rootProject.userOrg -// groupId = rootProject.groupId -// repoName = rootProject.repoName -// desc = '' -// website = rootProject.website -// licences = rootProject.licences -//} +// 插件。无需另外单独配置插件仓库地址 +apply plugin: 'maven-publish' +apply plugin: 'com.jfrog.bintray' android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -35,6 +25,80 @@ android { } dependencies { - implementation rootProject.ext.dependencies["xdroid-mvp"] +// implementation rootProject.ext.dependencies["xdroid-mvp"] + api project(path: ':mvp') api rootProject.ext.dependencies["zxing"] } +// 定义参数 +def gitUrl = rootProject.ext.siteUrl // Git仓库的url +def groupIdDefined = rootProject.ext.groupId +def artifactIdDefined = "lennon-qr-utill" +def uploadNameDefined = 'LennonQRUtill' +def versionDefined = "1.0.3" + +// bintray平台信息配置 +Properties properties = new Properties() +properties.load(project.rootProject.file('local.properties').newDataInputStream()) +bintray { + user = properties.getProperty("bintray.user") // local.properties里设置 + key = properties.getProperty("bintray.apikey") // local.properties里设置 + publications = ["MavenPublication"] // 'MyPublication'与下面的publishing闭包里的名称对应 + publish = true // 上传后立即发布到Bintray平台 + pkg { + repo = rootProject.ext.repoName // 必填。bintray平台仓库名,必须已经创建过。 + name = "$uploadNameDefined" // 必填。仓库里包package的名称,没有的话会自动创建。 + licenses = ["Apache-2.0"] // 首次创建package则必须,否则选填。 + vcsUrl = gitUrl // 首次创建package则必须,否则选填。 + version { + name = "$versionDefined" + } + } +} +// 生成sourceJar和javaDocJar构件 +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +task javadoc(type: Javadoc) { + failOnError false + source = android.sourceSets.main.java.sourceFiles + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + classpath += configurations.compile +} +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +artifacts { + archives sourcesJar + archives javadocJar +} + +// 构件文件和POM信息配置 +publishing { + publications { + MyPublication(MavenPublication) { + artifact("$buildDir/outputs/aar/nicelogger-release.aar") + artifact sourcesJar + artifact javadocJar + groupId "$groupIdDefined" + artifactId "$artifactIdDefined" + version "$versionDefined" + pom.withXml { + def dependenciesNode = asNode().appendNode('dependencies') + // Iterate over the implementation dependencies (we don't want the test ones), adding a node for each + configurations.implementation.allDependencies.each { + // Ensure dependencies such as fileTree are not included in the pom. + if (it.name != 'unspecified') { + def dependencyNode = dependenciesNode.appendNode('dependency') + dependencyNode.appendNode('groupId', it.group) + dependencyNode.appendNode('artifactId', it.name) + dependencyNode.appendNode('version', it.version) + } + } + } + } + } +} diff --git a/qrutill/src/main/java/com/inlee/qrutill/QRCodeTool.java b/qrutill/src/main/java/com/inlee/qrutill/QRCodeTool.java index 180a393..91e7de3 100644 --- a/qrutill/src/main/java/com/inlee/qrutill/QRCodeTool.java +++ b/qrutill/src/main/java/com/inlee/qrutill/QRCodeTool.java @@ -2,7 +2,9 @@ import android.graphics.*; import android.text.TextUtils; + import cn.droidlover.xdroidmvp.net.NetError; + import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.WriterException; @@ -16,6 +18,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.functions.Function; import io.reactivex.schedulers.Schedulers; + import org.reactivestreams.Publisher; import java.io.File; @@ -71,12 +74,21 @@ public Publisher apply(T model) throws Exception { } public Flowable saveQrCode(final String content, final int widthPix, final int heightPix, final Bitmap logoBm, final String filePath) { - return makeFlowable(callableSaveQrCode(content, widthPix, heightPix, logoBm, filePath)) + return makeFlowable(callableSaveQrCode(content, 0xff000000, 0xffffffff, widthPix, heightPix, logoBm, filePath)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable saveQrCode(final String content, final int qrColor, final int backColor, final int widthPix, final int heightPix, final Bitmap logoBm, final String filePath) { + return makeFlowable(callableSaveQrCode(content, qrColor, backColor, widthPix, heightPix, logoBm, filePath)) .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); } public Flowable> saveQrCodes(List contents, final int widthPix, final int heightPix, final String filePath) { - return makeFlowable(callableSaveQrCodes(contents, widthPix, heightPix, filePath)); + return makeFlowable(callableSaveQrCodes(contents, 0xff000000, 0xffffffff, widthPix, heightPix, filePath)); + } + + public Flowable> saveQrCodes(List contents, final int qrColor, final int backColor, final int widthPix, final int heightPix, final String filePath) { + return makeFlowable(callableSaveQrCodes(contents, qrColor, backColor, widthPix, heightPix, filePath)); } public Flowable getLogoQrCode(final Bitmap src, final Bitmap logo) { @@ -85,12 +97,22 @@ public Flowable getLogoQrCode(final Bitmap src, final Bitmap logo) { } public Flowable getQrCode(final String qrCode, final int widthPix, final int heightPix) { - return makeFlowable(callableGetQrCode(qrCode, widthPix, heightPix)) + return makeFlowable(callableGetQrCode(qrCode, 0xff000000, 0xffffffff, widthPix, heightPix)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable getQrCode(final String qrCode, final int qrColor, final int backColor, final int widthPix, final int heightPix) { + return makeFlowable(callableGetQrCode(qrCode, qrColor, backColor, widthPix, heightPix)) .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); } public Flowable> getQrCodes(final List qrCodes, final int widthPix, final int heightPix) { - return makeFlowable(callableGetQrCodes(qrCodes, widthPix, heightPix)) + return makeFlowable(callableGetQrCodes(qrCodes, 0xff000000, 0xffffffff, widthPix, heightPix)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable> getQrCodes(final List qrCodes, final int qrColor, final int backColor, final int widthPix, final int heightPix) { + return makeFlowable(callableGetQrCodes(qrCodes, qrColor, backColor, widthPix, heightPix)) .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); } @@ -99,33 +121,50 @@ public Flowable> getQrCodesforShop(String memberId, final List callableGetQrCode(final String qrCode, final int widthPix, final int heightPix) { + + private Callable callableGetQrCode(final String qrCode, final int qrColor, final int backColor, final int widthPix, final int heightPix) { return new Callable() { @Override public Bitmap call() throws Exception { - return createQRImage(qrCode, widthPix, heightPix); + return createQRImage(qrCode, widthPix, heightPix, qrColor, backColor); } }; } - private Callable> callableGetQrCodes(final List qrCodes, final int widthPix, final int heightPix) { + private Callable> callableGetQrCodes(final List qrCodes, final int qrColor, final int backColor, final int widthPix, final int heightPix) { return new Callable>() { @Override public List call() throws Exception { List bitmaps = new ArrayList<>(); for (String qrCode : qrCodes) { - bitmaps.add(addLogo(createQRImage(qrCode, widthPix, heightPix), null)); + bitmaps.add(addLogo(createQRImage(qrCode, qrColor, backColor, widthPix, heightPix), null)); } return bitmaps; } }; } - private Callable> callableGetQrCodes(final List qrCodes, final int widthPix, final int heightPix, final boolean flag) { + + private Callable> callableGetQrCodes(final List qrCodes, final int qrColor, final int backColor, final int widthPix, final int heightPix, final boolean flag) { return new Callable>() { @Override public List call() throws Exception { List bitmaps = new ArrayList<>(); for (String qrCode : qrCodes) { if (flag) { - bitmaps.add(addLogo(createQRImage(qrCode, widthPix, heightPix), null)); + bitmaps.add(addLogo(createQRImage(qrCode, widthPix, heightPix, qrColor, backColor), null)); } else { - bitmaps.add(createQRImage(qrCode, widthPix, heightPix)); + bitmaps.add(createQRImage(qrCode, widthPix, heightPix, qrColor, backColor)); } } return bitmaps; @@ -194,107 +235,21 @@ public List call() throws Exception { }; } - /** - * 生成二维码Bitmap - * - * @param content 内容 - * @param widthPix 图片宽度 - * @param heightPix 图片高度 - * @param logoBm 二维码中心的Logo图标(可以为null) - * @param filePath 用于存储二维码图片的文件路径 - * @return 生成二维码及保存文件是否成功 - */ - private Bitmap createQRImage(String content, int widthPix, int heightPix, Bitmap logoBm, String filePath, String name) { - try { - if (content == null || "".equals(content)) { - return null; - } - //配置参数 - Map hints = new HashMap<>(); - hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); - //容错级别 - hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); - //设置空白边距的宽度 -// hints.put(EncodeHintType.MARGIN, 2); //default is 4 - - // 图像数据转换,使用了矩阵转换 - BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, widthPix, heightPix, hints); - int[] pixels = new int[widthPix * heightPix]; - // 下面这里按照二维码的算法,逐个生成二维码的图片, - // 两个for循环是图片横列扫描的结果 - for (int y = 0; y < heightPix; y++) { - for (int x = 0; x < widthPix; x++) { - if (bitMatrix.get(x, y)) { - pixels[y * widthPix + x] = 0xff000000; -// pixels[y * widthPix + x] = 0xFFFF0000; + private Callable> callableGetQrCodes(final List qrCodes, final int widthPix, final int heightPix, final boolean flag, final int qrColor, final int backColor) { + return new Callable>() { + @Override + public List call() throws Exception { + List bitmaps = new ArrayList<>(); + for (String qrCode : qrCodes) { + if (flag) { + bitmaps.add(addLogo(createQRImage(qrCode, widthPix, heightPix, qrColor, backColor), null)); } else { - pixels[y * widthPix + x] = 0xffffffff; + bitmaps.add(createQRImage(qrCode, widthPix, heightPix, qrColor, backColor)); } } + return bitmaps; } - - // 生成二维码图片的格式,使用ARGB_8888 - Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888); - bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix); - - if (logoBm != null) { - bitmap = addLogo(bitmap, logoBm); - } - - //必须使用compress方法将bitmap保存到文件中再进行读取。直接返回的bitmap是没有任何压缩的,内存消耗巨大! - if (bitmap == null) { - return null; - } - if (!TextUtils.isEmpty(name)) { - bitmap = addTextToBitmap(bitmap, name); - } - bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(filePath)); - return bitmap; - } catch (WriterException | IOException e) { - e.printStackTrace(); - } - return null; - } - - public static Bitmap addTextToBitmap(Bitmap bmpSrc, String text) { - int srcWidth = bmpSrc.getWidth(); - int srcHeight = bmpSrc.getHeight(); - - // 先计算text所需要的height - int textSize = srcWidth / 20; - int padding = srcWidth / 200; - int textLinePadding = 1; - // 每行的文字 - int perLineWords = (srcWidth - 2 * padding) / textSize; - int lineNum = text.length() / perLineWords; - lineNum = text.length() % perLineWords == 0 ? lineNum : lineNum + 1; - int textTotalHeight = lineNum * (textSize + textLinePadding) + 2 * padding; - - Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight + textTotalHeight + 10, - Bitmap.Config.ARGB_8888); - try { - Canvas canvas = new Canvas(bitmap); - canvas.drawColor(Color.WHITE); - canvas.drawBitmap(bmpSrc, 0, 0, null); - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - paint.setColor(Color.BLACK); - paint.setTextSize(textSize); - String lineText; - for (int i = 0, startY = srcHeight + textSize, start, end; i < lineNum; i++) { - start = i * perLineWords; - end = start + perLineWords; - lineText = text.substring(start, end > text.length() ? text.length() : end); - canvas.drawText(lineText, (srcWidth - textSize * lineText.length() + 2 * padding) / 2, startY, paint); - startY += textSize + textLinePadding; - } - canvas.save(); - canvas.restore(); - } catch (Exception e) { - bitmap = null; - e.printStackTrace(); - e.getStackTrace(); - } - return bitmap; + }; } /** @@ -307,7 +262,7 @@ public static Bitmap addTextToBitmap(Bitmap bmpSrc, String text) { * @param filePath 用于存储二维码图片的文件路径 * @return 生成二维码及保存文件是否成功 */ - private Bitmap createQRImage(String content, int widthPix, int heightPix, Bitmap logoBm, String filePath) { + private Bitmap createQRImage(String content, int qrColor, int backColor, int widthPix, int heightPix, Bitmap logoBm, String filePath) { try { if (content == null || "".equals(content)) { return null; @@ -328,10 +283,12 @@ private Bitmap createQRImage(String content, int widthPix, int heightPix, Bitmap for (int y = 0; y < heightPix; y++) { for (int x = 0; x < widthPix; x++) { if (bitMatrix.get(x, y)) { - pixels[y * widthPix + x] = 0xff000000; +// pixels[y * widthPix + x] = 0xff000000; + pixels[y * widthPix + x] = qrColor; // pixels[y * widthPix + x] = 0xFFFF0000; } else { - pixels[y * widthPix + x] = 0xffffffff; +// pixels[y * widthPix + x] = 0xffffffff; + pixels[y * widthPix + x] = backColor; } } } @@ -395,45 +352,7 @@ private Bitmap addLogo(Bitmap src, Bitmap logo) { return bitmap; } - /** - * 在二维码中间添加Logo图案 - */ - private Bitmap addBitmap(Bitmap src, Bitmap logo) { - if (src == null) { - return null; - } - if (logo == null) { - return src; - } - //获取图片的宽高 - int srcWidth = src.getWidth(); - int srcHeight = src.getHeight(); - int logoWidth = logo.getWidth(); - int logoHeight = logo.getHeight(); - if (srcWidth == 0 || srcHeight == 0) { - return null; - } - if (logoWidth == 0 || logoHeight == 0) { - return src; - } - //logo大小为二维码整体大小的1/5 -// float scaleFactor = srcWidth * 1.0f / 5 / logoWidth; - Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888); - try { - Canvas canvas = new Canvas(bitmap); - canvas.drawBitmap(src, 0, 0, null); -// canvas.scale(scaleFactor, scaleFactor, srcWidth / 2, srcHeight / 2); - canvas.drawBitmap(logo, (srcWidth - logoWidth) / 2, (srcHeight - logoHeight) / 2, null); - canvas.save(); - canvas.restore(); - } catch (Exception e) { - bitmap = null; - e.getStackTrace(); - } - return bitmap; - } - - private Bitmap createQRImage(String qrCode, int widthPix, int heightPix) { + private Bitmap createQRImage(String qrCode, int widthPix, int heightPix, int qrColor, int backColor) { Bitmap bitmap = null; try { //判断URL合法性 @@ -450,11 +369,11 @@ private Bitmap createQRImage(String qrCode, int widthPix, int heightPix) { for (int y = 0; y < heightPix; y++) { for (int x = 0; x < widthPix; x++) { if (bitMatrix.get(x, y)) { - pixels[y * widthPix + x] = 0xff000000; + pixels[y * widthPix + x] = qrColor; // pixels[y * widthPix + x] = 0xfffd0000; // pixels[y * widthPix + x] = 0xff3399ff; } else { - pixels[y * widthPix + x] = 0x00000000; + pixels[y * widthPix + x] = backColor; } } } diff --git a/settings.gradle b/settings.gradle index 2b47d73..3264f30 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,7 @@ -include ':app', ':mvp', ':test',':xrecycler',':utill',':nativelogger' +include ':app' +include':mvp' +include':test' +include':xrecycler' +include':utill' +include':nativelogger' include ':qrutill' diff --git a/utill/build.gradle b/utill/build.gradle index 5d8fbfe..548fba1 100644 --- a/utill/build.gradle +++ b/utill/build.gradle @@ -1,21 +1,23 @@ apply plugin: 'com.android.library' -apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' -apply plugin: 'com.novoda.bintray-release' -publish { - artifactId = 'lennon-utill' // artifactId - uploadName = 'LennonUtill' // 展示名字 - publishVersion = '1.3.6' // 版本 - userOrg = rootProject.userOrg - groupId = rootProject.groupId - repoName = rootProject.repoName - desc = '' - website = rootProject.website - licences = rootProject.licences -} +// 插件。无需另外单独配置插件仓库地址 +apply plugin: 'maven-publish' +apply plugin: 'com.jfrog.bintray' +//apply plugin: 'com.novoda.bintray-release' +//publish { +// artifactId = 'lennon-utill' // artifactId +// uploadName = 'LennonUtill' // 展示名字 +// publishVersion = '1.3.8' // 版本 +// userOrg = rootProject.userOrg +// groupId = rootProject.groupId +// repoName = rootProject.repoName +// desc = '' +// website = rootProject.website +// licences = rootProject.licences +//} android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -29,7 +31,10 @@ android { } dependencies { api fileTree(include: ['*.jar'], dir: 'libs') - api rootProject.ext.dependencies["xdroid-mvp"] + + api project(path: ':mvp') + +// api rootProject.ext.dependencies["xdroid-mvp"] api rootProject.ext.dependencies["PersistentCookieJar"] api rootProject.ext.dependencies["qiujuer-ui"] @@ -42,3 +47,76 @@ dependencies { api rootProject.ext.dependencies["navigation-ui"] api rootProject.ext.dependencies["navigation-fragment"] } +// 定义参数 +def gitUrl = rootProject.ext.siteUrl // Git仓库的url +def groupIdDefined = rootProject.ext.groupId +def artifactIdDefined = "lennon-utill" +def uploadNameDefined = 'LennonUtill' +def versionDefined = "1.4.0" + +// bintray平台信息配置 +Properties properties = new Properties() +properties.load(project.rootProject.file('local.properties').newDataInputStream()) +bintray { + user = properties.getProperty("bintray.user") // local.properties里设置 + key = properties.getProperty("bintray.apikey") // local.properties里设置 + publications = ["MavenPublication"] // 'MyPublication'与下面的publishing闭包里的名称对应 + publish = true // 上传后立即发布到Bintray平台 + pkg { + repo = rootProject.ext.repoName // 必填。bintray平台仓库名,必须已经创建过。 + name = "$uploadNameDefined" // 必填。仓库里包package的名称,没有的话会自动创建。 + licenses = ["Apache-2.0"] // 首次创建package则必须,否则选填。 + vcsUrl = gitUrl // 首次创建package则必须,否则选填。 + version { + name = "$versionDefined" + } + } +} +// 生成sourceJar和javaDocJar构件 +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +task javadoc(type: Javadoc) { + failOnError false + source = android.sourceSets.main.java.sourceFiles + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + classpath += configurations.compile +} +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +artifacts { + archives sourcesJar + archives javadocJar +} + +// 构件文件和POM信息配置 +publishing { + publications { + MyPublication(MavenPublication) { + artifact("$buildDir/outputs/aar/nicelogger-release.aar") + artifact sourcesJar + artifact javadocJar + groupId "$groupIdDefined" + artifactId "$artifactIdDefined" + version "$versionDefined" + pom.withXml { + def dependenciesNode = asNode().appendNode('dependencies') + // Iterate over the implementation dependencies (we don't want the test ones), adding a node for each + configurations.implementation.allDependencies.each { + // Ensure dependencies such as fileTree are not included in the pom. + if (it.name != 'unspecified') { + def dependencyNode = dependenciesNode.appendNode('dependency') + dependencyNode.appendNode('groupId', it.group) + dependencyNode.appendNode('artifactId', it.name) + dependencyNode.appendNode('version', it.version) + } + } + } + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt index 6c1f52d..d75959e 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.Intent import android.os.Build import android.os.Bundle +import android.os.Handler import android.view.View import android.view.ViewGroup import android.view.WindowManager @@ -12,6 +13,7 @@ import android.widget.Toast import cn.droidlover.xdroidmvp.log.XLog import cn.droidlover.xdroidmvp.mvp.XActivity import cn.droidlover.xdroidmvp.net.NetError +import com.lennon.cn.utill.bean.ToastRunnable import com.lennon.cn.utill.conf.Lennon import com.lennon.cn.utill.dialog.CommonAlertDialog @@ -68,8 +70,11 @@ abstract class BaseActivity

> : XActivity

(), BaseView

{ window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) } } + override fun showProgressDialog(msg: String) { - if (dialog != null) dialog!!.dismiss() + if (dialog != null) { + dialog!!.dismiss() + } dialog = CustomProgressDialog(getContext()) dialog!!.setMessage(msg) dialog!!.show() @@ -80,7 +85,10 @@ abstract class BaseActivity

> : XActivity

(), BaseView

{ } override fun closeProgressDialog() { - if (null != dialog) dialog!!.dismiss() + if (null != dialog) { + dialog!!.dismiss() + } + dialog = null } override fun toast(msg: String, second: Int) { @@ -103,11 +111,31 @@ abstract class BaseActivity

> : XActivity

(), BaseView

{ toast(msg, true) } - override fun toast(msg: String, runnable: Runnable) { + override fun toast(msg: String, second: Int, runnable: ToastRunnable) { val dialog = CommonAlertDialog(getContext()) dialog.setMsg(msg) dialog.disableCancle() + dialog.setCanceledOnTouchOutside(false) + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + runnable.run() + } + }) dialog.show() + Handler().postDelayed(Runnable { + dialog.dismiss() + runnable.run() + }, second * 1000L) + Toast.makeText(getContext(), msg, second) + .show() + } + + override fun toast(msg: String, runnable: ToastRunnable) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() dialog.setCanceledOnTouchOutside(false) dialog.setDialogListener(object : OnAlertDialogListener() { override fun onSure() { @@ -116,6 +144,11 @@ abstract class BaseActivity

> : XActivity

(), BaseView

{ runnable.run() } }) + dialog.show() + Handler().postDelayed(Runnable { + dialog.dismiss() + runnable.run() + }, 2000) Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) .show() } diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseApplication.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseApplication.kt index 026b652..678bc5d 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseApplication.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseApplication.kt @@ -4,21 +4,16 @@ import android.app.Activity import android.content.Context import android.content.res.Configuration import android.content.res.Resources -import android.os.Looper import android.text.TextUtils -import android.widget.Toast import androidx.multidex.MultiDex import androidx.multidex.MultiDexApplication import cn.droidlover.xdroidmvp.cache.SharedPref -import cn.droidlover.xdroidmvp.log.XLog import com.lennon.cn.utill.conf.Lennon import com.lennon.cn.utill.utill.DensityUtils import com.lennon.cn.utill.utill.Utill -import lennon.com.utill.BuildConfig import java.io.BufferedReader import java.io.FileReader import java.util.* -import java.util.logging.SimpleFormatter /** * Created by lennon on 2017/7/26. @@ -97,7 +92,7 @@ open class BaseApplication : MultiDexApplication() { private var list: ArrayList? = null private val loger = true private var appliction: BaseApplication? = null - private var test = true + /** * 获取进程号对应的进程名 * @@ -194,10 +189,6 @@ open class BaseApplication : MultiDexApplication() { fun getAppliction(): BaseApplication? { return appliction } - - fun isTest(): Boolean { - return test && BuildConfig.DEBUG - } } open fun restart(context: Context) { diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt index 990c16c..bf77881 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt @@ -3,6 +3,7 @@ package com.lennon.cn.utill.base import android.app.Activity import android.content.Context import android.os.Bundle +import android.os.Handler import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -10,6 +11,7 @@ import android.widget.Toast import cn.droidlover.xdroidmvp.log.XLog import cn.droidlover.xdroidmvp.mvp.XFragment +import com.lennon.cn.utill.bean.ToastRunnable import com.lennon.cn.utill.dialog.CommonAlertDialog import com.lennon.cn.utill.dialog.CustomProgressDialog import com.lennon.cn.utill.dialog.OnAlertDialogListener @@ -71,7 +73,28 @@ abstract class BaseFragment

> : XFragment

(), BaseView

{ toast(msg, true) } - override fun toast(msg: String, runnable: Runnable) { + override fun toast(msg: String, second: Int, runnable: ToastRunnable) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.setCanceledOnTouchOutside(false) + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + runnable.run() + } + }) + dialog.show() + Handler().postDelayed(Runnable { + dialog.dismiss() + runnable.run() + }, second * 1000L) + Toast.makeText(getContext(), msg, second) + .show() + } + + override fun toast(msg: String, runnable: ToastRunnable) { val dialog = CommonAlertDialog(getContext()) dialog.setMsg(msg) dialog.disableCancle() @@ -83,6 +106,10 @@ abstract class BaseFragment

> : XFragment

(), BaseView

{ runnable.run() } }) + Handler().postDelayed(Runnable { + dialog.dismiss() + runnable.run() + }, 2000) Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) .show() } diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseLazyFragment.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseLazyFragment.kt index cfed0fe..8db71ff 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseLazyFragment.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseLazyFragment.kt @@ -3,12 +3,14 @@ package com.lennon.cn.utill.base import android.app.Activity import android.content.Context import android.os.Bundle +import android.os.Handler import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast import cn.droidlover.xdroidmvp.log.XLog import cn.droidlover.xdroidmvp.mvp.XLazyFragment +import com.lennon.cn.utill.bean.ToastRunnable import com.lennon.cn.utill.dialog.CommonAlertDialog import com.lennon.cn.utill.dialog.CustomProgressDialog import com.lennon.cn.utill.dialog.OnAlertDialogListener @@ -70,7 +72,28 @@ abstract class BaseLazyFragment

> : XLazyFragment

(), BaseVi toast(msg, true) } - override fun toast(msg: String, runnable: Runnable) { + override fun toast(msg: String, second: Int, runnable: ToastRunnable) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.setCanceledOnTouchOutside(false) + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + runnable.run() + } + }) + dialog.show() + Handler().postDelayed(Runnable { + dialog.dismiss() + runnable.run() + }, second * 1000L) + Toast.makeText(getContext(), msg, second) + .show() + } + + override fun toast(msg: String, runnable: ToastRunnable) { val dialog = CommonAlertDialog(getContext()) dialog.setMsg(msg) dialog.disableCancle() @@ -82,6 +105,10 @@ abstract class BaseLazyFragment

> : XLazyFragment

(), BaseVi runnable.run() } }) + Handler().postDelayed(Runnable { + dialog.dismiss() + runnable.run() + }, 2000L) Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) .show() } diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt b/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt index 8ea1074..0436940 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt @@ -2,11 +2,13 @@ package com.lennon.cn.utill.base import android.app.Activity import android.content.Context +import android.os.Handler import android.view.Display import android.widget.Toast import cn.droidlover.xdroidmvp.log.XLog import cn.droidlover.xdroidmvp.mvp.XPresentation import cn.droidlover.xdroidmvp.net.NetError +import com.lennon.cn.utill.bean.ToastRunnable import com.lennon.cn.utill.dialog.CommonAlertDialog import com.lennon.cn.utill.dialog.CustomProgressDialog import com.lennon.cn.utill.dialog.OnAlertDialogListener @@ -50,7 +52,28 @@ abstract class BasePresentation

>(context: Context, display: D toast(msg, true) } - override fun toast(msg: String, runnable: Runnable) { + override fun toast(msg: String, second: Int, runnable: ToastRunnable) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.setCanceledOnTouchOutside(false) + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + runnable.run() + } + }) + dialog.show() + Handler().postDelayed(Runnable { + dialog.dismiss() + runnable.run() + }, second * 1000L) + Toast.makeText(getContext(), msg, second) + .show() + } + + override fun toast(msg: String, runnable: ToastRunnable) { val dialog = CommonAlertDialog(getContext()) dialog.setMsg(msg) dialog.disableCancle() @@ -63,6 +86,10 @@ abstract class BasePresentation

>(context: Context, display: D runnable.run() } }) + Handler().postDelayed(Runnable { + dialog.dismiss() + runnable.run() + }, 2000L) Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) .show() } diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt index 288f5c8..edcf4a1 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt @@ -3,13 +3,15 @@ package com.lennon.cn.utill.base import android.app.Activity import android.content.Context import cn.droidlover.xdroidmvp.net.NetError +import com.lennon.cn.utill.bean.ToastRunnable interface BaseView

?> { fun showProgressDialog(msg: String) fun closeProgressDialog() fun toast(msg: String, second: Int) fun toast(msg: String) - fun toast(msg: String, runnable: Runnable) + fun toast(msg: String, runnable: ToastRunnable) + fun toast(msg: String, second: Int, runnable: ToastRunnable) fun toast(msg: String, flag: Boolean) fun showLoading(visibility: Int) fun showLoadingError(errorType: NetError) diff --git a/utill/src/main/java/com/lennon/cn/utill/bean/ToastRunnable.java b/utill/src/main/java/com/lennon/cn/utill/bean/ToastRunnable.java new file mode 100644 index 0000000..bde8b80 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/bean/ToastRunnable.java @@ -0,0 +1,21 @@ +package com.lennon.cn.utill.bean; + +public abstract class ToastRunnable implements Runnable { + private boolean executed; + + public ToastRunnable() { + executed = false; + } + + @Override + public void run() { + if (executed) { + } else { + function(); + } + executed = true; + + } + + protected abstract void function(); +} diff --git a/utill/src/main/java/com/lennon/cn/utill/cache/DataCache.java b/utill/src/main/java/com/lennon/cn/utill/cache/DataCache.java index 695fdb6..236c1d7 100644 --- a/utill/src/main/java/com/lennon/cn/utill/cache/DataCache.java +++ b/utill/src/main/java/com/lennon/cn/utill/cache/DataCache.java @@ -6,6 +6,7 @@ import com.lennon.cn.utill.base.BaseApplication; import com.lennon.cn.utill.base.BaseView; import com.lennon.cn.utill.bean.HttpEntity; +import com.lennon.cn.utill.bean.ToastRunnable; import com.lennon.cn.utill.conf.Lennon; import com.trello.rxlifecycle3.LifecycleProvider; @@ -96,9 +97,10 @@ protected void onFail(NetError error) { } activity.closeProgressDialog(); if (error.getType() == NetError.AuthError) { - activity.toast("登陆失效", new Runnable() { + activity.toast("登陆失效", new ToastRunnable() { + @Override - public void run() { + protected void function() { Lennon.Companion.requserLogin(); } }); diff --git a/utill/src/main/java/com/lennon/cn/utill/cache/DataListCache.java b/utill/src/main/java/com/lennon/cn/utill/cache/DataListCache.java index 6f14ec9..21f3aad 100644 --- a/utill/src/main/java/com/lennon/cn/utill/cache/DataListCache.java +++ b/utill/src/main/java/com/lennon/cn/utill/cache/DataListCache.java @@ -7,6 +7,7 @@ import com.lennon.cn.utill.base.BaseApplication; import com.lennon.cn.utill.base.BaseView; import com.lennon.cn.utill.bean.HttpEntity; +import com.lennon.cn.utill.bean.ToastRunnable; import com.lennon.cn.utill.conf.Lennon; import com.trello.rxlifecycle3.LifecycleProvider; @@ -105,9 +106,10 @@ protected void onFail(NetError error) { } activity.closeProgressDialog(); if (error.getType() == NetError.AuthError) { - activity.toast("登陆失效", new Runnable() { + activity.toast("登陆失效", new ToastRunnable() { + @Override - public void run() { + protected void function() { Lennon.Companion.requserLogin(); } }); diff --git a/xrecycler/build.gradle b/xrecycler/build.gradle index 29f7e74..8602936 100644 --- a/xrecycler/build.gradle +++ b/xrecycler/build.gradle @@ -1,12 +1,14 @@ apply plugin: 'com.android.library' -apply plugin: 'com.github.dcendents.android-maven' +// 插件。无需另外单独配置插件仓库地址 +apply plugin: 'maven-publish' +apply plugin: 'com.jfrog.bintray' //apply plugin: 'com.novoda.bintray-release' // //publish { // artifactId = 'xrecycler' // artifactId // uploadName = 'XRecycler' // 展示名字 -// publishVersion = '1.0.3' // 版本 +// publishVersion = '1.0.4' // 版本 // userOrg = rootProject.userOrg // groupId = rootProject.groupId // repoName = rootProject.repoName @@ -49,11 +51,76 @@ tasks.withType(JavaCompile) { options.encoding = "UTF-8" } +// 定义参数 +def gitUrl = rootProject.ext.siteUrl // Git仓库的url +def groupIdDefined = rootProject.ext.groupId +def artifactIdDefined = "xrecycler" +def uploadNameDefined = 'XRecycler' +def versionDefined = "1.0.4" + +// bintray平台信息配置 +Properties properties = new Properties() +properties.load(project.rootProject.file('local.properties').newDataInputStream()) +bintray { + user = properties.getProperty("bintray.user") // local.properties里设置 + key = properties.getProperty("bintray.apikey") // local.properties里设置 + publications = ["MavenPublication"] // 'MyPublication'与下面的publishing闭包里的名称对应 + publish = true // 上传后立即发布到Bintray平台 + pkg { + repo = rootProject.ext.repoName // 必填。bintray平台仓库名,必须已经创建过。 + name = "$uploadNameDefined" // 必填。仓库里包package的名称,没有的话会自动创建。 + licenses = ["Apache-2.0"] // 首次创建package则必须,否则选填。 + vcsUrl = gitUrl // 首次创建package则必须,否则选填。 + version { + name = "$versionDefined" + } + } +} +// 生成sourceJar和javaDocJar构件 task sourcesJar(type: Jar) { from android.sourceSets.main.java.srcDirs classifier = 'sources' } +task javadoc(type: Javadoc) { + failOnError false + source = android.sourceSets.main.java.sourceFiles + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + classpath += configurations.compile +} +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + artifacts { archives sourcesJar + archives javadocJar } + +// 构件文件和POM信息配置 +publishing { + publications { + MyPublication(MavenPublication) { + artifact("$buildDir/outputs/aar/nicelogger-release.aar") + artifact sourcesJar + artifact javadocJar + groupId "$groupIdDefined" + artifactId "$artifactIdDefined" + version "$versionDefined" + pom.withXml { + def dependenciesNode = asNode().appendNode('dependencies') + // Iterate over the implementation dependencies (we don't want the test ones), adding a node for each + configurations.implementation.allDependencies.each { + // Ensure dependencies such as fileTree are not included in the pom. + if (it.name != 'unspecified') { + def dependencyNode = dependenciesNode.appendNode('dependency') + dependencyNode.appendNode('groupId', it.group) + dependencyNode.appendNode('artifactId', it.name) + dependencyNode.appendNode('version', it.version) + } + } + } + } + } +} \ No newline at end of file From 603c7441d4747f63f1bb84ac5ff741e018ce04f8 Mon Sep 17 00:00:00 2001 From: Edit <1136160757@qq.com> Date: Wed, 8 Jul 2020 11:25:12 +0800 Subject: [PATCH 15/24] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=EF=BC=8C=E5=8A=A0=E5=85=A5=E5=8F=8C=E5=B1=8F=E5=BC=82=E6=98=BE?= =?UTF-8?q?base?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 10 +-- conf.gradle | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- mvp/build.gradle | 73 ++-------------- nativelogger/build.gradle | 77 ++++------------ qrutill/build.gradle | 79 ++++------------- utill/build.gradle | 87 +++---------------- .../lennon/cn/utill/base/BasePresentation.kt | 21 ++++- .../com/lennon/cn/utill/utill/DensityUtils.kt | 6 +- xrecycler/build.gradle | 55 ------------ 10 files changed, 83 insertions(+), 331 deletions(-) diff --git a/build.gradle b/build.gradle index 35bdca1..3e68816 100644 --- a/build.gradle +++ b/build.gradle @@ -15,15 +15,11 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:4.0.0' + //noinspection GradleDependency + classpath 'com.android.tools.build:gradle:3.6.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' - classpath 'com.github.dcendents:android-maven-plugin:1.2' -// classpath 'com.novoda:bintray-release:+' - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files + classpath 'com.novoda:bintray-release:0.9.2' } } diff --git a/conf.gradle b/conf.gradle index 90e17a0..7defb4a 100644 --- a/conf.gradle +++ b/conf.gradle @@ -8,6 +8,8 @@ ext { userOrg = 'lennon-xia' // 用户名 repoName = "lennon" // 仓库名 groupId = 'com.lennon.utill' // groupId + website = 'https://github.com/lennon19940306/XDroidMvp-AndroidX' // 地址 , 填写真实可访问地址 + licences = ['Apache-2.0']// 描述 , 填写licences // ext{ // userOrg = 'lennon-xia' // 用户名 // repoName = "lennon" // 仓库名 @@ -78,7 +80,7 @@ ext { "picasso" : "com.squareup.picasso:picasso:+", "xrecyclerview" : "com.lennon.utill:xrecycler:+", - "xdroid-mvp" : "com.lennon.utill:xdroid-mvp:+", + "xdroid-mvp" : "com.lennon.utill:xdroid-mvp:1.1.9", "avi-loading" : "com.wang.avi:library:+", "rxbus" : "com.blankj:rxbus:+", diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4fc515c..aefc567 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/mvp/build.gradle b/mvp/build.gradle index 18d40fd..812852d 100644 --- a/mvp/build.gradle +++ b/mvp/build.gradle @@ -1,24 +1,21 @@ apply plugin: 'com.android.library' -apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-android-extensions' -// 插件。无需另外单独配置插件仓库地址 -apply plugin: 'maven-publish' -apply plugin: 'com.jfrog.bintray' + //apply plugin: 'com.novoda.bintray-release' // //publish { // artifactId = 'xdroid-mvp' // artifactId // uploadName = 'XDroidMvp' // 展示名字 // publishVersion = '1.1.9' // 版本 -// userOrg = rootProject.userOrg -// groupId = -// repoName = rootProject.repoName +// userOrg = rootProject.ext.userOrg +// groupId =rootProject.ext.groupId +// repoName = rootProject.ext.repoName // desc = '' -// website = rootProject.website -// licences = rootProject.licences +// website = rootProject.ext.website +// licences = rootProject.ext.licences //} android { @@ -74,31 +71,6 @@ tasks.withType(JavaCompile) { options.encoding = "UTF-8" } -// 定义参数 -def gitUrl = rootProject.ext.siteUrl // Git仓库的url -def groupIdDefined = rootProject.ext.groupId -def artifactIdDefined = "xdroid-mvp" -def uploadNameDefined = 'XDroidMvp' -def versionDefined = "1.2.0" - -// bintray平台信息配置 -Properties properties = new Properties() -properties.load(project.rootProject.file('local.properties').newDataInputStream()) -bintray { - user = properties.getProperty("bintray.user") // local.properties里设置 - key = properties.getProperty("bintray.apikey") // local.properties里设置 - publications = ["MavenPublication"] // 'MyPublication'与下面的publishing闭包里的名称对应 - publish = true // 上传后立即发布到Bintray平台 - pkg { - repo = rootProject.ext.repoName // 必填。bintray平台仓库名,必须已经创建过。 - name = "$uploadNameDefined" // 必填。仓库里包package的名称,没有的话会自动创建。 - licenses = ["Apache-2.0"] // 首次创建package则必须,否则选填。 - vcsUrl = gitUrl // 首次创建package则必须,否则选填。 - version { - name = "$versionDefined" - } - } -} // 生成sourceJar和javaDocJar构件 task sourcesJar(type: Jar) { from android.sourceSets.main.java.srcDirs @@ -115,36 +87,3 @@ task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from javadoc.destinationDir } - -artifacts { - archives sourcesJar - archives javadocJar -} - -// 构件文件和POM信息配置 -publishing { - publications { - MyPublication(MavenPublication) { - artifact("$buildDir/outputs/aar/nicelogger-release.aar") - artifact sourcesJar - artifact javadocJar - groupId "$groupIdDefined" - artifactId "$artifactIdDefined" - version "$versionDefined" - pom.withXml { - def dependenciesNode = asNode().appendNode('dependencies') - // Iterate over the implementation dependencies (we don't want the test ones), adding a node for each - configurations.implementation.allDependencies.each { - // Ensure dependencies such as fileTree are not included in the pom. - if (it.name != 'unspecified') { - def dependencyNode = dependenciesNode.appendNode('dependency') - dependencyNode.appendNode('groupId', it.group) - dependencyNode.appendNode('artifactId', it.name) - dependencyNode.appendNode('version', it.version) - } - } - } - } - } -} - diff --git a/nativelogger/build.gradle b/nativelogger/build.gradle index 4ca421f..dc328ee 100644 --- a/nativelogger/build.gradle +++ b/nativelogger/build.gradle @@ -1,8 +1,20 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' -// 插件。无需另外单独配置插件仓库地址 -apply plugin: 'maven-publish' -apply plugin: 'com.jfrog.bintray' + +//apply plugin: 'com.novoda.bintray-release' +// +//publish { +// artifactId = 'nativelogger' // artifactId +// uploadName = 'NativeLogger' // 展示名字 +// publishVersion = '1.0' // 版本 +// userOrg = rootProject.userOrg +// groupId = rootProject.groupId +// repoName = rootProject.repoName +// desc = '' +// website = rootProject.website +// licences = rootProject.licences +//} + android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -29,31 +41,6 @@ tasks.withType(JavaCompile) { options.encoding = "UTF-8" } -// 定义参数 -def gitUrl = rootProject.ext.siteUrl // Git仓库的url -def groupIdDefined = rootProject.ext.groupId -def artifactIdDefined = "nativelogger" -def uploadNameDefined = 'NativeLogger' -def versionDefined = "1.0" - -// bintray平台信息配置 -Properties properties = new Properties() -properties.load(project.rootProject.file('local.properties').newDataInputStream()) -bintray { - user = properties.getProperty("bintray.user") // local.properties里设置 - key = properties.getProperty("bintray.apikey") // local.properties里设置 - publications = ["MavenPublication"] // 'MyPublication'与下面的publishing闭包里的名称对应 - publish = true // 上传后立即发布到Bintray平台 - pkg { - repo = rootProject.ext.repoName // 必填。bintray平台仓库名,必须已经创建过。 - name = "$uploadNameDefined" // 必填。仓库里包package的名称,没有的话会自动创建。 - licenses = ["Apache-2.0"] // 首次创建package则必须,否则选填。 - vcsUrl = gitUrl // 首次创建package则必须,否则选填。 - version { - name = "$versionDefined" - } - } -} // 生成sourceJar和javaDocJar构件 task sourcesJar(type: Jar) { from android.sourceSets.main.java.srcDirs @@ -69,36 +56,4 @@ task javadoc(type: Javadoc) { task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from javadoc.destinationDir -} - -artifacts { - archives sourcesJar - archives javadocJar -} - -// 构件文件和POM信息配置 -publishing { - publications { - MyPublication(MavenPublication) { - artifact("$buildDir/outputs/aar/nicelogger-release.aar") - artifact sourcesJar - artifact javadocJar - groupId "$groupIdDefined" - artifactId "$artifactIdDefined" - version "$versionDefined" - pom.withXml { - def dependenciesNode = asNode().appendNode('dependencies') - // Iterate over the implementation dependencies (we don't want the test ones), adding a node for each - configurations.implementation.allDependencies.each { - // Ensure dependencies such as fileTree are not included in the pom. - if (it.name != 'unspecified') { - def dependencyNode = dependenciesNode.appendNode('dependency') - dependencyNode.appendNode('groupId', it.group) - dependencyNode.appendNode('artifactId', it.name) - dependencyNode.appendNode('version', it.version) - } - } - } - } - } -} +} \ No newline at end of file diff --git a/qrutill/build.gradle b/qrutill/build.gradle index 2b95ae6..b758314 100644 --- a/qrutill/build.gradle +++ b/qrutill/build.gradle @@ -1,9 +1,20 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' -// 插件。无需另外单独配置插件仓库地址 -apply plugin: 'maven-publish' -apply plugin: 'com.jfrog.bintray' + +//apply plugin: 'com.novoda.bintray-release' +//publish { +// artifactId = 'lennon-qr-utill' // artifactId +// uploadName = 'LennonQRUtill' // 展示名字 +// publishVersion = '1.0.2' // 版本 +// userOrg = rootProject.userOrg +// groupId = rootProject.groupId +// repoName = rootProject.repoName +// desc = '' +// website = rootProject.website +// licences = rootProject.licences +//} + android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -25,35 +36,11 @@ android { } dependencies { -// implementation rootProject.ext.dependencies["xdroid-mvp"] - api project(path: ':mvp') + implementation rootProject.ext.dependencies["xdroid-mvp"] +// api project(path: ':mvp') api rootProject.ext.dependencies["zxing"] } -// 定义参数 -def gitUrl = rootProject.ext.siteUrl // Git仓库的url -def groupIdDefined = rootProject.ext.groupId -def artifactIdDefined = "lennon-qr-utill" -def uploadNameDefined = 'LennonQRUtill' -def versionDefined = "1.0.3" -// bintray平台信息配置 -Properties properties = new Properties() -properties.load(project.rootProject.file('local.properties').newDataInputStream()) -bintray { - user = properties.getProperty("bintray.user") // local.properties里设置 - key = properties.getProperty("bintray.apikey") // local.properties里设置 - publications = ["MavenPublication"] // 'MyPublication'与下面的publishing闭包里的名称对应 - publish = true // 上传后立即发布到Bintray平台 - pkg { - repo = rootProject.ext.repoName // 必填。bintray平台仓库名,必须已经创建过。 - name = "$uploadNameDefined" // 必填。仓库里包package的名称,没有的话会自动创建。 - licenses = ["Apache-2.0"] // 首次创建package则必须,否则选填。 - vcsUrl = gitUrl // 首次创建package则必须,否则选填。 - version { - name = "$versionDefined" - } - } -} // 生成sourceJar和javaDocJar构件 task sourcesJar(type: Jar) { from android.sourceSets.main.java.srcDirs @@ -69,36 +56,4 @@ task javadoc(type: Javadoc) { task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from javadoc.destinationDir -} - -artifacts { - archives sourcesJar - archives javadocJar -} - -// 构件文件和POM信息配置 -publishing { - publications { - MyPublication(MavenPublication) { - artifact("$buildDir/outputs/aar/nicelogger-release.aar") - artifact sourcesJar - artifact javadocJar - groupId "$groupIdDefined" - artifactId "$artifactIdDefined" - version "$versionDefined" - pom.withXml { - def dependenciesNode = asNode().appendNode('dependencies') - // Iterate over the implementation dependencies (we don't want the test ones), adding a node for each - configurations.implementation.allDependencies.each { - // Ensure dependencies such as fileTree are not included in the pom. - if (it.name != 'unspecified') { - def dependencyNode = dependenciesNode.appendNode('dependency') - dependencyNode.appendNode('groupId', it.group) - dependencyNode.appendNode('artifactId', it.name) - dependencyNode.appendNode('version', it.version) - } - } - } - } - } -} +} \ No newline at end of file diff --git a/utill/build.gradle b/utill/build.gradle index 548fba1..a21d0f8 100644 --- a/utill/build.gradle +++ b/utill/build.gradle @@ -3,21 +3,18 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' -// 插件。无需另外单独配置插件仓库地址 -apply plugin: 'maven-publish' -apply plugin: 'com.jfrog.bintray' -//apply plugin: 'com.novoda.bintray-release' -//publish { -// artifactId = 'lennon-utill' // artifactId -// uploadName = 'LennonUtill' // 展示名字 -// publishVersion = '1.3.8' // 版本 -// userOrg = rootProject.userOrg -// groupId = rootProject.groupId -// repoName = rootProject.repoName -// desc = '' -// website = rootProject.website -// licences = rootProject.licences -//} +apply plugin: 'com.novoda.bintray-release' +publish { + artifactId = 'lennon-utill' // artifactId + uploadName = 'LennonUtill' // 展示名字 + publishVersion = '1.4.3' // 版本 + userOrg = rootProject.userOrg + groupId = rootProject.groupId + repoName = rootProject.repoName + desc = '' + website = rootProject.website + licences = rootProject.licences +} android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -32,9 +29,9 @@ android { dependencies { api fileTree(include: ['*.jar'], dir: 'libs') - api project(path: ':mvp') +// api project(path: ':mvp') -// api rootProject.ext.dependencies["xdroid-mvp"] + api rootProject.ext.dependencies["xdroid-mvp"] api rootProject.ext.dependencies["PersistentCookieJar"] api rootProject.ext.dependencies["qiujuer-ui"] @@ -47,31 +44,7 @@ dependencies { api rootProject.ext.dependencies["navigation-ui"] api rootProject.ext.dependencies["navigation-fragment"] } -// 定义参数 -def gitUrl = rootProject.ext.siteUrl // Git仓库的url -def groupIdDefined = rootProject.ext.groupId -def artifactIdDefined = "lennon-utill" -def uploadNameDefined = 'LennonUtill' -def versionDefined = "1.4.0" -// bintray平台信息配置 -Properties properties = new Properties() -properties.load(project.rootProject.file('local.properties').newDataInputStream()) -bintray { - user = properties.getProperty("bintray.user") // local.properties里设置 - key = properties.getProperty("bintray.apikey") // local.properties里设置 - publications = ["MavenPublication"] // 'MyPublication'与下面的publishing闭包里的名称对应 - publish = true // 上传后立即发布到Bintray平台 - pkg { - repo = rootProject.ext.repoName // 必填。bintray平台仓库名,必须已经创建过。 - name = "$uploadNameDefined" // 必填。仓库里包package的名称,没有的话会自动创建。 - licenses = ["Apache-2.0"] // 首次创建package则必须,否则选填。 - vcsUrl = gitUrl // 首次创建package则必须,否则选填。 - version { - name = "$versionDefined" - } - } -} // 生成sourceJar和javaDocJar构件 task sourcesJar(type: Jar) { from android.sourceSets.main.java.srcDirs @@ -88,35 +61,3 @@ task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from javadoc.destinationDir } - -artifacts { - archives sourcesJar - archives javadocJar -} - -// 构件文件和POM信息配置 -publishing { - publications { - MyPublication(MavenPublication) { - artifact("$buildDir/outputs/aar/nicelogger-release.aar") - artifact sourcesJar - artifact javadocJar - groupId "$groupIdDefined" - artifactId "$artifactIdDefined" - version "$versionDefined" - pom.withXml { - def dependenciesNode = asNode().appendNode('dependencies') - // Iterate over the implementation dependencies (we don't want the test ones), adding a node for each - configurations.implementation.allDependencies.each { - // Ensure dependencies such as fileTree are not included in the pom. - if (it.name != 'unspecified') { - def dependencyNode = dependenciesNode.appendNode('dependency') - dependencyNode.appendNode('groupId', it.group) - dependencyNode.appendNode('artifactId', it.name) - dependencyNode.appendNode('version', it.version) - } - } - } - } - } -} diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt b/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt index 0436940..457f573 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt @@ -2,6 +2,7 @@ package com.lennon.cn.utill.base import android.app.Activity import android.content.Context +import android.os.Bundle import android.os.Handler import android.view.Display import android.widget.Toast @@ -17,6 +18,18 @@ abstract class BasePresentation

>(context: Context, display: D XPresentation

(context, display), BaseView

{ private var TAG = javaClass.simpleName private var dialog: CustomProgressDialog? = null + private var listener: ChangeListener? = null + + interface ChangeListener { + fun onCancel(basePresentation: BasePresentation<*>) + fun onDismiss(basePresentation: BasePresentation<*>) + fun onShow(basePresentation: BasePresentation<*>) + } + + fun setListener(listener: ChangeListener?) { + this.listener = listener + } + override fun showProgressDialog(msg: String) { if (dialog != null) dialog!!.dismiss() dialog = CustomProgressDialog(getContext()) @@ -24,6 +37,13 @@ abstract class BasePresentation

>(context: Context, display: D dialog!!.show() } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setOnCancelListener { listener?.onCancel(this) } + setOnDismissListener { listener?.onDismiss(this) } + setOnShowListener { listener?.onShow(this) } + } + override fun useEventBus(): Boolean { return true } @@ -124,7 +144,6 @@ abstract class BasePresentation

>(context: Context, display: D } override fun bindEvent() { - } override fun getOptionsMenuId(): Int { diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt b/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt index ba2a234..a88bf3f 100644 --- a/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt +++ b/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt @@ -73,7 +73,7 @@ object DensityUtils { * @param activity * @param orientation */ - fun setOrientation(activity: Activity, orientation: String, f: Float) { + fun setOrientation(activity: Activity, orientation: Density, f: Float) { setAppOrientation(activity, orientation, f) } @@ -84,11 +84,11 @@ object DensityUtils { * 这三个参数是统一修改过后的值 * orientation:方向值,传入width或height */ - private fun setAppOrientation(activity: Activity, orientation: String, f: Float) { + private fun setAppOrientation(activity: Activity, orientation: Density, f: Float) { val targetDensity: Float - if (orientation == "height") { + if (orientation == Density.HEIGHT) { targetDensity = (appDisplayMetrics!!.heightPixels - barHeight) / f//设计图的高度 单位:dp } else { targetDensity = appDisplayMetrics!!.widthPixels / f//设计图的宽度 单位:dp diff --git a/xrecycler/build.gradle b/xrecycler/build.gradle index 8602936..3f76546 100644 --- a/xrecycler/build.gradle +++ b/xrecycler/build.gradle @@ -1,7 +1,4 @@ apply plugin: 'com.android.library' -// 插件。无需另外单独配置插件仓库地址 -apply plugin: 'maven-publish' -apply plugin: 'com.jfrog.bintray' //apply plugin: 'com.novoda.bintray-release' // @@ -51,31 +48,6 @@ tasks.withType(JavaCompile) { options.encoding = "UTF-8" } -// 定义参数 -def gitUrl = rootProject.ext.siteUrl // Git仓库的url -def groupIdDefined = rootProject.ext.groupId -def artifactIdDefined = "xrecycler" -def uploadNameDefined = 'XRecycler' -def versionDefined = "1.0.4" - -// bintray平台信息配置 -Properties properties = new Properties() -properties.load(project.rootProject.file('local.properties').newDataInputStream()) -bintray { - user = properties.getProperty("bintray.user") // local.properties里设置 - key = properties.getProperty("bintray.apikey") // local.properties里设置 - publications = ["MavenPublication"] // 'MyPublication'与下面的publishing闭包里的名称对应 - publish = true // 上传后立即发布到Bintray平台 - pkg { - repo = rootProject.ext.repoName // 必填。bintray平台仓库名,必须已经创建过。 - name = "$uploadNameDefined" // 必填。仓库里包package的名称,没有的话会自动创建。 - licenses = ["Apache-2.0"] // 首次创建package则必须,否则选填。 - vcsUrl = gitUrl // 首次创建package则必须,否则选填。 - version { - name = "$versionDefined" - } - } -} // 生成sourceJar和javaDocJar构件 task sourcesJar(type: Jar) { from android.sourceSets.main.java.srcDirs @@ -97,30 +69,3 @@ artifacts { archives sourcesJar archives javadocJar } - -// 构件文件和POM信息配置 -publishing { - publications { - MyPublication(MavenPublication) { - artifact("$buildDir/outputs/aar/nicelogger-release.aar") - artifact sourcesJar - artifact javadocJar - groupId "$groupIdDefined" - artifactId "$artifactIdDefined" - version "$versionDefined" - pom.withXml { - def dependenciesNode = asNode().appendNode('dependencies') - // Iterate over the implementation dependencies (we don't want the test ones), adding a node for each - configurations.implementation.allDependencies.each { - // Ensure dependencies such as fileTree are not included in the pom. - if (it.name != 'unspecified') { - def dependencyNode = dependenciesNode.appendNode('dependency') - dependencyNode.appendNode('groupId', it.group) - dependencyNode.appendNode('artifactId', it.name) - dependencyNode.appendNode('version', it.version) - } - } - } - } - } -} \ No newline at end of file From 27e166cdb581e4bbd6e1387f19334aa25dc07da6 Mon Sep 17 00:00:00 2001 From: Edit <1136160757@qq.com> Date: Fri, 7 Aug 2020 11:38:58 +0800 Subject: [PATCH 16/24] =?UTF-8?q?=E5=8A=A0=E5=85=A5AES=E5=8A=A0=E8=A7=A3?= =?UTF-8?q?=E5=AF=86=E5=B7=A5=E5=85=B7=EF=BC=8C=E5=B9=B6=E4=BF=AE=E6=94=B9?= =?UTF-8?q?RSA=E8=A7=A3=E5=AF=86=E5=85=BC=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf.gradle | 2 +- utill/build.gradle | 2 +- .../dialog/BottomSearchSelectDialog.java | 3 + .../cn/utill/dialog/BottomSelectDialog.java | 4 + .../cn/utill/dialog/CustomProgressDialog.java | 16 +++- .../com/lennon/cn/utill/utill/AESUtils.java | 93 +++++++++++++++++++ .../com/lennon/cn/utill/utill/ByteUtil.java | 68 ++++++++++++++ .../lennon/cn/utill/utill/rsa/RSACoder.java | 6 +- 8 files changed, 188 insertions(+), 6 deletions(-) create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/AESUtils.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/ByteUtil.java diff --git a/conf.gradle b/conf.gradle index 7defb4a..a8be556 100644 --- a/conf.gradle +++ b/conf.gradle @@ -63,7 +63,7 @@ ext { "PersistentCookieJar" : "com.github.franmontiel:PersistentCookieJar:1.0.1", - "qiujuer-ui" : "net.qiujuer.genius:ui:+", + "qiujuer-ui" : "net.qiujuer.genius:ui:2.1.1", "appcompat-v7" : "androidx.appcompat:appcompat:1.1.0", diff --git a/utill/build.gradle b/utill/build.gradle index a21d0f8..35f36df 100644 --- a/utill/build.gradle +++ b/utill/build.gradle @@ -7,7 +7,7 @@ apply plugin: 'com.novoda.bintray-release' publish { artifactId = 'lennon-utill' // artifactId uploadName = 'LennonUtill' // 展示名字 - publishVersion = '1.4.3' // 版本 + publishVersion = '1.4.7' // 版本 userOrg = rootProject.userOrg groupId = rootProject.groupId repoName = rootProject.repoName diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSearchSelectDialog.java b/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSearchSelectDialog.java index d04ce84..978ef14 100644 --- a/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSearchSelectDialog.java +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSearchSelectDialog.java @@ -73,6 +73,7 @@ public BottomSearchSelectDialog(Context context, final T... a) { } this.context = context; this.list = t; + initType(); wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); setContentView(generateCustomView()); setWidth(ViewGroup.LayoutParams.MATCH_PARENT); @@ -95,6 +96,7 @@ public BottomSearchSelectDialog(Context context, List typefaces, } this.context = context; this.list = t; + initType(); wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); setContentView(generateCustomView()); setWidth(ViewGroup.LayoutParams.MATCH_PARENT); @@ -108,6 +110,7 @@ public BottomSearchSelectDialog(Context context, List typefaces, public BottomSearchSelectDialog(Context context, List list) { this.context = context; this.list = list; + initType(); wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); setContentView(generateCustomView()); setWidth(ViewGroup.LayoutParams.MATCH_PARENT); diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSelectDialog.java b/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSelectDialog.java index 7be7eb8..90e7924 100644 --- a/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSelectDialog.java +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSelectDialog.java @@ -5,6 +5,7 @@ import android.content.Context; import android.graphics.PixelFormat; import android.graphics.Typeface; +import android.os.Build; import android.os.IBinder; import android.view.*; import android.widget.PopupWindow; @@ -92,6 +93,7 @@ public BottomSelectDialog(Context context, List typefaces, } this.context = context; this.list = t; + initType(); wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); setContentView(generateCustomView()); setWidth(ViewGroup.LayoutParams.MATCH_PARENT); @@ -105,6 +107,7 @@ public BottomSelectDialog(Context context, List typefaces, public BottomSelectDialog(Context context, List list) { this.context = context; this.list = list; + initType(); wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); setContentView(generateCustomView()); setWidth(ViewGroup.LayoutParams.MATCH_PARENT); @@ -116,6 +119,7 @@ public BottomSelectDialog(Context context, List list) { } public View generateCustomView() { + View root = View.inflate(context, R.layout.dialog_bottom_select, null); xRecyclerView = root.findViewById(R.id.list); xRecyclerView.verticalLayoutManager(context); diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/CustomProgressDialog.java b/utill/src/main/java/com/lennon/cn/utill/dialog/CustomProgressDialog.java index cc8d831..6a997fd 100644 --- a/utill/src/main/java/com/lennon/cn/utill/dialog/CustomProgressDialog.java +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/CustomProgressDialog.java @@ -2,8 +2,9 @@ import android.app.Dialog; import android.content.Context; +import android.os.Build; import android.view.View; -import android.view.Window; +import android.view.WindowManager; import android.widget.LinearLayout; import android.widget.TextView; @@ -24,6 +25,7 @@ public CustomProgressDialog(Context context) { super(context, R.style.dialog_progress); this.mContext = context; setParams(); + applyCompat(); } public CustomProgressDialog(Context context, String title, String msg) { @@ -32,6 +34,7 @@ public CustomProgressDialog(Context context, String title, String msg) { setParams(); setTitile(title); setMessage(msg); + applyCompat(); } public CustomProgressDialog(Context context, int msg_resid) { @@ -39,6 +42,7 @@ public CustomProgressDialog(Context context, int msg_resid) { this.mContext = context; setParams(); setMessage(msg_resid); + applyCompat(); } public CustomProgressDialog(Context context, int title_resid, int msg_resid) { @@ -47,6 +51,16 @@ public CustomProgressDialog(Context context, int title_resid, int msg_resid) { setParams(); setTitile(title_resid); setMessage(msg_resid); + applyCompat(); + } + + private void applyCompat() { + if (Build.VERSION.SDK_INT < 19) { + return; + } + getWindow().setFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); } @Override diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/AESUtils.java b/utill/src/main/java/com/lennon/cn/utill/utill/AESUtils.java new file mode 100644 index 0000000..f83aea0 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/AESUtils.java @@ -0,0 +1,93 @@ +package com.lennon.cn.utill.utill; + +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import kotlin.text.Charsets; + + +public class AESUtils { + + public static final String PASSWARDKEY_STRING = "KrOrO67PfIfjOjqe"; + private static final String IV_STRING = "16-Bytes--String"; + + public AESUtils() { + } + + public static byte[] decryptAESByte(String content, String key) throws Exception { + byte[] encryptedBytes = ByteUtil.hexToByteArray(content); + byte[] enCodeFormat = key.getBytes(); + SecretKeySpec secretKey = new SecretKeySpec(enCodeFormat, "AES"); + byte[] initParam = "16-Bytes--String".getBytes(); + IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(2, secretKey, ivParameterSpec); + byte[] result = cipher.doFinal(encryptedBytes); + return result; + } + + public static String encryptAES(byte[] byteContent, String key) throws Exception { + byte[] enCodeFormat = key.getBytes(); + SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES"); + byte[] initParam = "16-Bytes--String".getBytes(); + IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(1, secretKeySpec, ivParameterSpec); + byte[] encryptedBytes = cipher.doFinal(byteContent); + return ByteUtil.byteToHexStr(encryptedBytes); + } + + public static String encryptAES(String content, String key) throws Exception { + byte[] byteContent = content.getBytes("UTF-8"); + byte[] enCodeFormat = key.getBytes(); + SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES"); + byte[] initParam = "16-Bytes--String".getBytes(); + IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(1, secretKeySpec, ivParameterSpec); + byte[] encryptedBytes = cipher.doFinal(byteContent); + return toHex(encryptedBytes); + } + + public static String toHex(byte[] buffer) { + StringBuffer sb = new StringBuffer(buffer.length * 2); + + for (int i = 0; i < buffer.length; ++i) { + sb.append(Character.forDigit((buffer[i] & 240) >> 4, 16)); + sb.append(Character.forDigit(buffer[i] & 15, 16)); + } + + return sb.toString(); + } + + public static byte[] fromHex(String hex) { + byte[] res = new byte[hex.length() / 2]; + char[] chs = hex.toCharArray(); + int i = 0; + + for (int c = 0; i < chs.length; ++c) { + res[c] = (byte) Integer.parseInt(new String(chs, i, 2), 16); + i += 2; + } + + return res; + } + + public static String decryptAES(String content, String key) throws Exception { + byte[] encryptedBytes = fromHex(content); + byte[] enCodeFormat = key.getBytes(); + SecretKeySpec secretKey = new SecretKeySpec(enCodeFormat, "AES"); + byte[] initParam = "16-Bytes--String".getBytes(); + IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(2, secretKey, ivParameterSpec); + byte[] result = cipher.doFinal(encryptedBytes); + return new String(result, "UTF-8"); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/ByteUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/ByteUtil.java new file mode 100644 index 0000000..5b60a3e --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/ByteUtil.java @@ -0,0 +1,68 @@ +package com.lennon.cn.utill.utill; + +public class ByteUtil { + /** + * 将字节数组转换为十六进制字符串 + * + * @param byteArray byte[] + * @return String + */ + public static String byteToHexStr(byte[] byteArray) { + StringBuilder strDigest = new StringBuilder(); + for (byte aByteArray : byteArray) { + strDigest.append(byteToHexStr(aByteArray)); + } + return strDigest.toString(); + } + + /** + * 将字节转换为十六进制字符串 + * + * @param mByte byte + * @return String + */ + public static char[] byteToHexStr(byte mByte) { + char[] digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + char[] tempArr = new char[2]; + tempArr[0] = digit[(mByte >>> 4) & 0X0F]; + tempArr[1] = digit[mByte & 0X0F]; + return tempArr; + } + + /** + * hex字符串转byte数组 + * + * @param inHex 待转换的Hex字符串 + * @return 转换后的byte数组结果 + */ + public static byte[] hexToByteArray(String inHex) { + int hexlen = inHex.length(); + byte[] result; + if (hexlen % 2 == 1) { + // 奇数 + hexlen++; + result = new byte[(hexlen / 2)]; + inHex = "0" + inHex; + } else { + // 偶数 + result = new byte[(hexlen / 2)]; + } + int j = 0; + for (int i = 0; i < hexlen; i += 2) { + result[j] = hexToByte(inHex.substring(i, i + 2)); + j++; + } + return result; + } + + /** + * Hex字符串转byte + * + * @param inHex 待转换的Hex字符串 + * @return 转换后的byte + */ + public static byte hexToByte(String inHex) { + return (byte) Integer.parseInt(inHex, 16); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSACoder.java b/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSACoder.java index e417a10..9d2a4cd 100644 --- a/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSACoder.java +++ b/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSACoder.java @@ -94,7 +94,7 @@ public static byte[] decryptByPrivateKey(byte[] data, String key) throws Excepti KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec); // 对数据解密 - Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(data); } @@ -131,7 +131,7 @@ public static byte[] decryptByPublicKey(byte[] data, String key) KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key publicKey = keyFactory.generatePublic(x509KeySpec); // 对数据解密 - Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, publicKey); return cipher.doFinal(data); } @@ -177,7 +177,7 @@ public static byte[] encryptByPrivateKey(byte[] data, String key) KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec); // 对数据加密 - Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, privateKey); return cipher.doFinal(data); } From 6d638fa2bfd2eeb3d566a1edb7d30e6c8a30afbd Mon Sep 17 00:00:00 2001 From: Edit <1136160757@qq.com> Date: Wed, 30 Dec 2020 09:35:20 +0800 Subject: [PATCH 17/24] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 2 +- build.gradle | 45 ++--- conf.gradle | 83 ++++----- gradle/wrapper/gradle-wrapper.jar | Bin 53636 -> 55616 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- mvp/build.gradle | 36 ++-- .../cn/droidlover/xdroidmvp/event/IBus.java | 3 +- .../xdroidmvp/imageloader/GlideLoader.java | 21 ++- .../cn/droidlover/xdroidmvp/mvp/IView.java | 6 + .../droidlover/xdroidmvp/mvp/XActivity.java | 8 +- .../droidlover/xdroidmvp/mvp/XFragment.java | 4 + .../xdroidmvp/mvp/XLazyFragment.java | 5 + .../xdroidmvp/mvp/XPresentation.java | 6 +- .../xdroidmvp/net/ApiSubscriber.java | 24 ++- .../xdroidmvp/net/LogInterceptor.java | 5 + .../cn/droidlover/xdroidmvp/net/NetError.java | 14 ++ .../droidlover/xdroidmvp/router/Router.java | 15 +- nativelogger/build.gradle | 2 +- qrutill/build.gradle | 27 ++- settings.gradle | 11 +- utill/build.gradle | 4 +- .../com/lennon/cn/utill/base/BaseActivity.kt | 9 - .../com/lennon/cn/utill/base/BaseFragment.kt | 84 ++++++++- .../lennon/cn/utill/base/BaseLazyFragment.kt | 8 - .../lennon/cn/utill/base/BasePresentation.kt | 8 +- .../java/com/lennon/cn/utill/base/BaseView.kt | 8 +- .../com/lennon/cn/utill/cache/DataCache.java | 7 +- .../lennon/cn/utill/cache/DataListCache.java | 9 +- .../java/com/lennon/cn/utill/conf/Lennon.kt | 9 + .../lennon/cn/utill/conf/LennonProvider.kt | 14 +- .../lennon/cn/utill/dialog/CustomDialog.java | 75 ++++++++ .../com/lennon/cn/utill/utill/AmtUtils.java | 3 - .../com/lennon/cn/utill/utill/DensityUtils.kt | 2 +- .../com/lennon/cn/utill/utill/TimeUtill.kt | 2 +- .../com/lennon/cn/utill/utill/ZipUtils.java | 163 ++++++++++++++++++ .../utill/widget/ClearWithSpaceEditText.java | 108 +++++++++--- utill/src/main/res/layout/custom_dialog.xml | 24 +++ utill/src/main/res/values/attrs.xml | 1 + xrecycler/build.gradle | 2 +- 39 files changed, 672 insertions(+), 187 deletions(-) create mode 100644 utill/src/main/java/com/lennon/cn/utill/dialog/CustomDialog.java create mode 100644 utill/src/main/java/com/lennon/cn/utill/utill/ZipUtils.java create mode 100644 utill/src/main/res/layout/custom_dialog.xml diff --git a/app/build.gradle b/app/build.gradle index 3365b95..9f74e49 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion rootProject.ext.android.compileSdkVersion - buildToolsVersion "29.0.2" + buildToolsVersion "29.0.3" defaultConfig { applicationId 'cn.droidlover.xdroidmvp.demo' minSdkVersion rootProject.ext.android.minSdkVersion diff --git a/build.gradle b/build.gradle index 3e68816..ad31d92 100644 --- a/build.gradle +++ b/build.gradle @@ -2,41 +2,42 @@ apply from: "conf.gradle" buildscript { - ext.kotlin_version = '1.3.72' + ext.kotlin_version = '1.4.21' repositories { - maven {url 'https://dl.bintray.com/lennon-xia/lennon/'} - maven {url 'http://127.0.0.1:8081/repository/lennon/' } - jcenter() { url 'https://maven.aliyun.com/repository/jcenter' } - jcenter() - maven { - url 'https://maven.google.com/' - name 'Google' - } + maven {url 'http://127.0.0.1:8081/repository/lennon/' } +// jcenter() { url 'https://maven.aliyun.com/repository/public' } +// maven {url 'https://dl.bintray.com/lennon-xia/lennon/'} +// jcenter() + +// maven { +// url 'https://maven.google.com/' +// name 'Google' +// } } dependencies { //noinspection GradleDependency - classpath 'com.android.tools.build:gradle:3.6.3' + classpath 'com.android.tools.build:gradle:3.6.4' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' +// classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' classpath 'com.novoda:bintray-release:0.9.2' } } allprojects { - repositories { - maven {url 'https://dl.bintray.com/lennon-xia/lennon/'} - jcenter() { url 'https://maven.aliyun.com/repository/jcenter' } + +// jcenter() { url 'https://maven.aliyun.com/repository/public' } maven {url 'http://127.0.0.1:8081/repository/lennon/' } - mavenCentral() - - maven { - url 'https://maven.google.com/' - name 'Google' - } - jcenter() - maven { url "https://jitpack.io" } +// maven {url 'https://dl.bintray.com/lennon-xia/lennon/'} +// mavenCentral() + +// maven { +// url 'https://maven.google.com/' +// name 'Google' +// } +// jcenter() +// maven { url "https://jitpack.io" } } tasks.withType(Javadoc).all { enabled = false diff --git a/conf.gradle b/conf.gradle index a8be556..0441db8 100644 --- a/conf.gradle +++ b/conf.gradle @@ -37,78 +37,79 @@ ext { ] dependencies = [ - "annotations" : "androidx.annotation:annotation:1.1.0", + "eventBus" : "org.greenrobot:eventbus:3.2.0", + "eventbus-annotation-processor": "org.greenrobot:eventbus-annotation-processor:3.2.0", - "constraintlayout" : "androidx.constraintlayout:constraintlayout:1.1.3", + "annotations" : "androidx.annotation:annotation:1.1.0", - "glide" : "com.github.bumptech.glide:glide:4.11.0", - "glide-compiler" : "com.github.bumptech.glide:compiler:4.11.0", + "constraintlayout" : "androidx.constraintlayout:constraintlayout:1.1.3", + "glide" : "com.github.bumptech.glide:glide:4.11.0", + "glide-compiler" : "com.github.bumptech.glide:compiler:4.11.0", - "zxing" : "com.google.zxing:core:3.4.0", - "navigation-fragment-ktx" : "androidx.navigation:navigation-fragment-ktx:${android["NavigationSDK"]}", - "navigation-ui-ktx" : "androidx.navigation:navigation-ui-ktx:${android["NavigationSDK"]}", + "zxing" : "com.google.zxing:core:3.4.0", - "navigation-ui" : "androidx.navigation:navigation-ui:${android["NavigationSDK"]}", - "navigation-fragment" : "androidx.navigation:navigation-fragment:${android["NavigationSDK"]}", + "navigation-fragment-ktx" : "androidx.navigation:navigation-fragment-ktx:${android["NavigationSDK"]}", + "navigation-ui-ktx" : "androidx.navigation:navigation-ui-ktx:${android["NavigationSDK"]}", + "navigation-ui" : "androidx.navigation:navigation-ui:${android["NavigationSDK"]}", + "navigation-fragment" : "androidx.navigation:navigation-fragment:${android["NavigationSDK"]}", - "exifinterface" : "androidx.exifinterface:exifinterface:1.2.0", - "multidex" : "androidx.multidex:multidex:2.0.1", + "exifinterface" : "androidx.exifinterface:exifinterface:1.2.0", - "kotlin" : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version", + "multidex" : "androidx.multidex:multidex:2.0.1", - "PersistentCookieJar" : "com.github.franmontiel:PersistentCookieJar:1.0.1", + "kotlin" : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version", - "qiujuer-ui" : "net.qiujuer.genius:ui:2.1.1", + "PersistentCookieJar" : "com.github.franmontiel:PersistentCookieJar:1.0.1", - "appcompat-v7" : "androidx.appcompat:appcompat:1.1.0", + "qiujuer-ui" : "net.qiujuer.genius:ui:2.1.1", - "support-v4" : "androidx.legacy:legacy-support-v4:1.0.0", + "appcompat-v7" : "androidx.appcompat:appcompat:1.1.0", - "design" : "com.google.android.material:material:1.0.0", + "support-v4" : "androidx.legacy:legacy-support-v4:1.0.0", + "design" : "com.google.android.material:material:1.0.0", + "recyclerview-v7" : "androidx.recyclerview:recyclerview:1.1.0", - "recyclerview-v7" : "androidx.recyclerview:recyclerview:1.1.0", + "eventbus" : "org.greenrobot:eventbus:+", - "eventbus" : "org.greenrobot:eventbus:+", + "picasso" : "com.squareup.picasso:picasso:+", - "picasso" : "com.squareup.picasso:picasso:+", + "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.5", + "xdroid-mvp" : "com.lennon.utill:xdroid-mvp:1.3.5", - "xrecyclerview" : "com.lennon.utill:xrecycler:+", - "xdroid-mvp" : "com.lennon.utill:xdroid-mvp:1.1.9", + "avi-loading" : "com.wang.avi:library:+", + "rxbus" : "com.blankj:rxbus:+", - "avi-loading" : "com.wang.avi:library:+", - "rxbus" : "com.blankj:rxbus:+", + "gson" : "com.google.code.gson:gson:+", + "rxandroid" : "io.reactivex.rxjava2:rxandroid:+", + "rxjava" : "io.reactivex.rxjava2:rxjava:+", - "gson" : "com.google.code.gson:gson:+", - "rxandroid" : "io.reactivex.rxjava2:rxandroid:+", - "rxjava" : "io.reactivex.rxjava2:rxjava:+", + "retrofit" : "com.squareup.retrofit2:retrofit:${android["VRetrofitSdk"]}", + "retrofit-converter-gson" : "com.squareup.retrofit2:converter-gson:${android["VRetrofitSdk"]}", + "retrofit-adapter-rxjava" : "com.squareup.retrofit2:adapter-rxjava2:${android["VRetrofitSdk"]}", + "okhttp3-logging-interceptor" : "com.squareup.okhttp3:logging-interceptor:${android["VOkhttp"]}", + "okhttp3" : "com.squareup.okhttp3:okhttp:${android["VOkhttp"]}", - "retrofit" : "com.squareup.retrofit2:retrofit:${android["VRetrofitSdk"]}", - "retrofit-converter-gson" : "com.squareup.retrofit2:converter-gson:${android["VRetrofitSdk"]}", - "retrofit-adapter-rxjava" : "com.squareup.retrofit2:adapter-rxjava2:${android["VRetrofitSdk"]}", - "okhttp3-logging-interceptor": "com.squareup.okhttp3:logging-interceptor:${android["VOkhttp"]}", - "okhttp3" : "com.squareup.okhttp3:okhttp:${android["VOkhttp"]}", + "rxlifecycle" : "com.trello.rxlifecycle3:rxlifecycle:${android["VRxlifecycle"]}", - "rxlifecycle" : "com.trello.rxlifecycle3:rxlifecycle:${android["VRxlifecycle"]}", + "rxlifecycle-android" : "com.trello.rxlifecycle3:rxlifecycle-android:${android["VRxlifecycle"]}", - "rxlifecycle-android" : "com.trello.rxlifecycle3:rxlifecycle-android:${android["VRxlifecycle"]}", + "rxlifecycle-components" : "com.trello.rxlifecycle3:rxlifecycle-components:${android["VRxlifecycle"]}", - "rxlifecycle-components" : "com.trello.rxlifecycle3:rxlifecycle-components:${android["VRxlifecycle"]}", + "rxpermissions" : "com.tbruyelle.rxpermissions2:rxpermissions:latest.integration", - "rxpermissions" : "com.tbruyelle.rxpermissions2:rxpermissions:latest.integration", + "canary-debug" : "com.squareup.leakcanary:leakcanary-android:+", + "canary-release" : "com.squareup.leakcanary:leakcanary-android-no-op:+", - "canary-debug" : "com.squareup.leakcanary:leakcanary-android:+", - "canary-release" : "com.squareup.leakcanary:leakcanary-android-no-op:+", + "XStateController" : "com.github.limedroid:XStateController:+", - "XStateController" : "com.github.limedroid:XStateController:+", - - "nativelogger" : "com.lennon.utill:nativelogger:+" + "nativelogger" : "com.lennon.utill:nativelogger:1.1" ] } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 13372aef5e24af05341d49695ee84e5f9b594659..5c2d1cf016b3885f6930543d57b744ea8c220a1a 100644 GIT binary patch literal 55616 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�J+L&6O)2CL%pxI@Gv&XXKx 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 V&}jHG { boolean useEventBus(); P newP(); + + void showProgressDialog(String msg); + + void closeProgressDialog(); + + void onRefresh(Boolean bRefresh); } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java index ba1e88f..f8a2b15 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java @@ -2,13 +2,16 @@ import android.app.Activity; import android.os.Bundle; + import androidx.annotation.Nullable; + import android.view.Menu; import com.tbruyelle.rxpermissions2.RxPermissions; import cn.droidlover.xdroidmvp.XDroidConf; import cn.droidlover.xdroidmvp.event.BusProvider; + import com.trello.rxlifecycle3.components.support.RxAppCompatActivity; /** @@ -23,7 +26,10 @@ public abstract class XActivity

extends RxAppCompatActivity private RxPermissions rxPermissions; - + @Override + public void onRefresh(Boolean bRefresh) { + + } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java index 5d0dee2..474224b 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java @@ -28,6 +28,10 @@ public abstract class XFragment

extends RxFragment implement private RxPermissions rxPermissions; + @Override + public void onRefresh(Boolean bRefresh) { + + } @Nullable @Override diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java index adb7ae2..0ede4c9 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java @@ -19,6 +19,11 @@ public abstract class XLazyFragment

private RxPermissions rxPermissions; + @Override + public void onRefresh(Boolean bRefresh) { + + } + @Override protected void onCreateViewLazy(Bundle savedInstanceState) { super.onCreateViewLazy(savedInstanceState); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java index f4110e4..8c5a317 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java @@ -1,6 +1,5 @@ package cn.droidlover.xdroidmvp.mvp; -import android.app.Activity; import android.app.Presentation; import android.content.Context; import android.os.Bundle; @@ -31,6 +30,11 @@ public XPresentation(Context outerContext, Display display, int theme) { this.context=outerContext; } + @Override + public void onRefresh(Boolean bRefresh) { + + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/ApiSubscriber.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/ApiSubscriber.java index 155976a..d56487f 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/ApiSubscriber.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/ApiSubscriber.java @@ -5,9 +5,11 @@ import org.json.JSONException; -import java.net.UnknownHostException; +import java.io.IOException; +import cn.droidlover.xdroidmvp.mvp.IView; import io.reactivex.subscribers.ResourceSubscriber; +import retrofit2.HttpException; /** @@ -15,7 +17,14 @@ */ public abstract class ApiSubscriber extends ResourceSubscriber { + private IView iView; + public ApiSubscriber() { + } + + public ApiSubscriber(IView iView) { + this.iView = iView; + } @Override public void onError(Throwable e) { @@ -23,8 +32,12 @@ public void onError(Throwable e) { if (e != null) { e.printStackTrace(); if (!(e instanceof NetError)) { - if (e instanceof UnknownHostException) { + if (e instanceof IOException) { error = new NetError(e, NetError.NoConnectError); + } else if (e instanceof HttpException) { + error = new NetError(e, NetError.HttpError); + HttpException httpException = (HttpException) e; + error.setHttpCode(httpException.code()); } else if (e instanceof JSONException || e instanceof JsonParseException || e instanceof JsonSyntaxException) { @@ -39,6 +52,10 @@ public void onError(Throwable e) { if (useCommonErrorHandler() && XApi.getCommonProvider() != null) { if (XApi.getCommonProvider().handleError(error)) { //使用通用异常处理 + if (iView != null) { + iView.closeProgressDialog(); + } + afterHandleError(); return; } } @@ -47,6 +64,9 @@ public void onError(Throwable e) { } + protected void afterHandleError() { + } + protected abstract void onFail(NetError error); @Override diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/LogInterceptor.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/LogInterceptor.java index 2614010..f29117a 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/LogInterceptor.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/LogInterceptor.java @@ -66,8 +66,13 @@ private Response logResponse(Response response) { MediaType mediaType = body.contentType(); if (mediaType != null) { if (isText(mediaType)) { + XLog.e(TAG, "url : " + response.request().url()); String resp = body.string(); XLog.e(TAG, resp); + Headers headers = response.headers(); + if (headers != null && headers.size() > 0) { + XLog.e(TAG, "headers : " + headers.toString()); + } body = ResponseBody.create(mediaType, resp); return response.newBuilder().body(body).build(); } else { diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/NetError.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/NetError.java index 738ea24..a84edbf 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/NetError.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/NetError.java @@ -8,6 +8,7 @@ public class NetError extends Exception { private Throwable exception; private int type = NoConnectError; private String code; + private int httpCode; public static final int ParseError = 0; //数据解析异常 public static final int NoConnectError = 1; //无连接异常 @@ -15,6 +16,7 @@ public class NetError extends Exception { public static final int NoDataError = 3; //无数据返回异常 public static final int BusinessError = 4; //业务异常 public static final int OtherError = 5; //其他异常 + public static final int HttpError = 6; public NetError(Throwable exception, int type) { this.exception = exception; @@ -48,6 +50,10 @@ public int getType() { return type; } + public Throwable getThrowable() { + return exception; + } + public String getCode() { return code; } @@ -55,4 +61,12 @@ public String getCode() { public void setCode(String code) { this.code = code; } + + public int getHttpCode() { + return httpCode; + } + + public void setHttpCode(int httpCode) { + this.httpCode = httpCode; + } } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/router/Router.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/router/Router.java index d6e2c50..80e87ce 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/router/Router.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/router/Router.java @@ -11,6 +11,7 @@ import androidx.annotation.Nullable; import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityOptionsCompat; + import cn.droidlover.xdroidmvp.XDroidConf; /** @@ -29,7 +30,7 @@ public class Router { public static final int RES_NONE = -1; - private static RouterCallback callback; + private RouterCallback callback; private Router() { intent = new Intent(); @@ -63,6 +64,11 @@ public Router putBundle(@Nullable String key, Bundle bundle) { return this; } + public Router setBundle(@Nullable Bundle bundle) { + this.data = bundle; + return this; + } + public Router putByte(@Nullable String key, byte value) { getBundleData().putByte(key, value); return this; @@ -103,6 +109,7 @@ public Router putCharSequence(@Nullable String key, @Nullable CharSequence value return this; } + public Router putParcelable(@Nullable String key, @Nullable Parcelable value) { getBundleData().putParcelable(key, value); return this; @@ -206,11 +213,13 @@ private Bundle getBundleData() { return data; } + public static void pop(Activity activity) { activity.finish(); } - public static void setCallback(RouterCallback callback) { - Router.callback = callback; + public Router setCallback(RouterCallback callback) { + this.callback = callback; + return this; } } diff --git a/nativelogger/build.gradle b/nativelogger/build.gradle index dc328ee..257c3e3 100644 --- a/nativelogger/build.gradle +++ b/nativelogger/build.gradle @@ -6,7 +6,7 @@ apply plugin: 'kotlin-android' //publish { // artifactId = 'nativelogger' // artifactId // uploadName = 'NativeLogger' // 展示名字 -// publishVersion = '1.0' // 版本 +// publishVersion = '1.1' // 版本 // userOrg = rootProject.userOrg // groupId = rootProject.groupId // repoName = rootProject.repoName diff --git a/qrutill/build.gradle b/qrutill/build.gradle index b758314..978864d 100644 --- a/qrutill/build.gradle +++ b/qrutill/build.gradle @@ -1,19 +1,18 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' - -//apply plugin: 'com.novoda.bintray-release' -//publish { -// artifactId = 'lennon-qr-utill' // artifactId -// uploadName = 'LennonQRUtill' // 展示名字 -// publishVersion = '1.0.2' // 版本 -// userOrg = rootProject.userOrg -// groupId = rootProject.groupId -// repoName = rootProject.repoName -// desc = '' -// website = rootProject.website -// licences = rootProject.licences -//} + +apply plugin: 'com.novoda.bintray-release' +publish { + artifactId = 'lennon-qr-utill' // artifactId + uploadName = 'LennonQRUtill' // 展示名字 + publishVersion = '1.0.3' // 版本 + userOrg = rootProject.userOrg + groupId = rootProject.groupId + repoName = rootProject.repoName + desc = '' + website = rootProject.website + licences = rootProject.licences +} android { diff --git a/settings.gradle b/settings.gradle index 3264f30..24f7e11 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,7 +1,8 @@ -include ':app' -include':mvp' -include':test' -include':xrecycler' +//include ':mylibrary' +//include ':app' +//include':mvp' +//include':test' +//include':xrecycler' include':utill' -include':nativelogger' +//include':nativelogger' include ':qrutill' diff --git a/utill/build.gradle b/utill/build.gradle index 35f36df..08e85e8 100644 --- a/utill/build.gradle +++ b/utill/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-parcelize' apply plugin: 'com.novoda.bintray-release' publish { artifactId = 'lennon-utill' // artifactId uploadName = 'LennonUtill' // 展示名字 - publishVersion = '1.4.7' // 版本 + publishVersion = '1.6.16' // 版本 userOrg = rootProject.userOrg groupId = rootProject.groupId repoName = rootProject.repoName diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt index d75959e..110467a 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt @@ -80,10 +80,6 @@ abstract class BaseActivity

> : XActivity

(), BaseView

{ dialog!!.show() } - override fun useEventBus(): Boolean { - return true - } - override fun closeProgressDialog() { if (null != dialog) { dialog!!.dismiss() @@ -182,11 +178,6 @@ abstract class BaseActivity

> : XActivity

(), BaseView

{ return this } - override fun onRefresh(bRefresh: Boolean) { - XLog.e("$TAG:onRefresh()") - } - - override fun onCreate(savedInstanceState: Bundle?) { XLog.e("$TAG:onCreate") // Thread.setDefaultUncaughtExceptionHandler(CrashHandler(this)) diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt index bf77881..3b7f5a9 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt @@ -17,23 +17,34 @@ import com.lennon.cn.utill.dialog.CustomProgressDialog import com.lennon.cn.utill.dialog.OnAlertDialogListener abstract class BaseFragment

> : XFragment

(), BaseView

{ + private var isFragmentVisible: Boolean = false + private var isReuseView: Boolean = false + private var isFirstVisible: Boolean = true + private var rootView: View? = null private var dialog: CustomProgressDialog? = null private var mActivity: Activity? = null override fun showLoading(visibility: Int) { } - override fun useEventBus(): Boolean { - return true + protected fun findViewById(i: Int): T { + return rootView!!.findViewById(i) } private var isFirst = true private var stop = false - - override fun onRefresh(bRefresh: Boolean) { + private fun initVariable() { + isFirstVisible = true + isFragmentVisible = false + rootView = null + isReuseView = true } + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + userVisibleHint = userVisibleHint + } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -44,6 +55,71 @@ abstract class BaseFragment

> : XFragment

(), BaseView

{ return rootView } + /** + * 去除setUserVisibleHint()多余的回调场景,保证只有当fragment可见状态发生变化时才回调 + * 回调时机在view创建完后,所以支持ui操作,解决在setUserVisibleHint()里进行ui操作有可能报null异常的问题 + * + * 可在该回调方法里进行一些ui显示与隐藏 + * + * @param isVisible true 不可见 -> 可见 + * false 可见 -> 不可见 + */ + private fun onFragmentVisibleChange(isVisible: Boolean) { + if (isVisible) { + if (!isFragmentVisible) { + onVisible() + } + } else { + if (isFragmentVisible) { + onInvisible() + } + } + } + + override fun setUserVisibleHint(isVisibleToUser: Boolean) { + super.setUserVisibleHint(isVisibleToUser) + XLog.e(getName() + " setUserVisibleHint isVisibleToUser=${isVisibleToUser} isFirstVisible=${isFirstVisible} isFragmentVisible=${isFragmentVisible}") + if (rootView == null) { + return + } + if (isFirstVisible && isVisibleToUser) { + onFragmentFirstVisible() + isFirstVisible = false + isFragmentVisible = true + } else if (isVisibleToUser) { + onFragmentVisibleChange(true) + isFragmentVisible = true + return + } else if (isFragmentVisible) { + isFragmentVisible = false + onFragmentVisibleChange(false) + } + } + + /** + * 在fragment首次可见时回调,可用于加载数据,防止每次进入都重复加载数据 + */ + private fun onFragmentFirstVisible() { + onVisible() + } + + protected fun isFragmentVisible(): Boolean { + return isFragmentVisible + } + + /** + * 可见时的回调方法 + */ + open fun onVisible() { + XLog.e(getName() + " onVisible") + } + + /** + * 不可见时的回调方法 + */ + fun onInvisible() { + XLog.e(getName() + " onInvisible") + } override fun showProgressDialog(msg: String) { if (dialog != null) diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseLazyFragment.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseLazyFragment.kt index 8db71ff..1492c95 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseLazyFragment.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseLazyFragment.kt @@ -22,17 +22,9 @@ abstract class BaseLazyFragment

> : XLazyFragment

(), BaseVi override fun showLoading(visibility: Int) { } - override fun useEventBus(): Boolean { - return true - } - private var isFirst = true private var stop = false - - override fun onRefresh(bRefresh: Boolean) { - } - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt b/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt index 457f573..3e2ca7b 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt @@ -10,6 +10,7 @@ import cn.droidlover.xdroidmvp.log.XLog import cn.droidlover.xdroidmvp.mvp.XPresentation import cn.droidlover.xdroidmvp.net.NetError import com.lennon.cn.utill.bean.ToastRunnable +import com.lennon.cn.utill.conf.Lennon import com.lennon.cn.utill.dialog.CommonAlertDialog import com.lennon.cn.utill.dialog.CustomProgressDialog import com.lennon.cn.utill.dialog.OnAlertDialogListener @@ -44,10 +45,6 @@ abstract class BasePresentation

>(context: Context, display: D setOnShowListener { listener?.onShow(this) } } - override fun useEventBus(): Boolean { - return true - } - override fun closeProgressDialog() { if (null != dialog) dialog!!.dismiss() } @@ -150,7 +147,4 @@ abstract class BasePresentation

>(context: Context, display: D return 0 } - override fun onRefresh(bRefresh: Boolean) { - XLog.e("$TAG:onRefresh()") - } } \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt index edcf4a1..e12edbb 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt @@ -2,12 +2,12 @@ package com.lennon.cn.utill.base import android.app.Activity import android.content.Context +import cn.droidlover.xdroidmvp.mvp.IView import cn.droidlover.xdroidmvp.net.NetError import com.lennon.cn.utill.bean.ToastRunnable -interface BaseView

?> { - fun showProgressDialog(msg: String) - fun closeProgressDialog() +interface BaseView

?> :IView

{ + fun toast(msg: String, second: Int) fun toast(msg: String) fun toast(msg: String, runnable: ToastRunnable) @@ -16,6 +16,6 @@ interface BaseView

?> { fun showLoading(visibility: Int) fun showLoadingError(errorType: NetError) fun getContext(): Context? - fun onRefresh(bRefresh: Boolean) + fun getActivity(): Activity? } \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/cache/DataCache.java b/utill/src/main/java/com/lennon/cn/utill/cache/DataCache.java index 236c1d7..ef5b64c 100644 --- a/utill/src/main/java/com/lennon/cn/utill/cache/DataCache.java +++ b/utill/src/main/java/com/lennon/cn/utill/cache/DataCache.java @@ -87,7 +87,7 @@ void getDataFromNet() { flowable.compose(XApi.getApiTransformer()) .compose(XApi.getScheduler()) .compose(lifecycleProvider.bindToLifecycle()) - .subscribe(new ApiSubscriber() { + .subscribe(new ApiSubscriber(activity) { @Override protected void onFail(NetError error) { if (getLocalData() != null) { @@ -108,6 +108,11 @@ protected void function() { } } + @Override + protected boolean useCommonErrorHandler() { + return false; + } + @Override public void onNext(T o) { if (getLocalData() != null) { diff --git a/utill/src/main/java/com/lennon/cn/utill/cache/DataListCache.java b/utill/src/main/java/com/lennon/cn/utill/cache/DataListCache.java index 21f3aad..ea04b5b 100644 --- a/utill/src/main/java/com/lennon/cn/utill/cache/DataListCache.java +++ b/utill/src/main/java/com/lennon/cn/utill/cache/DataListCache.java @@ -3,6 +3,7 @@ import android.text.TextUtils; import cn.droidlover.xdroidmvp.net.NetError; + import com.google.gson.Gson; import com.lennon.cn.utill.base.BaseApplication; import com.lennon.cn.utill.base.BaseView; @@ -14,6 +15,7 @@ import java.lang.reflect.Type; import androidx.annotation.NonNull; + import cn.droidlover.xdroidmvp.cache.SharedPref; import cn.droidlover.xdroidmvp.net.ApiSubscriber; import cn.droidlover.xdroidmvp.net.XApi; @@ -96,7 +98,7 @@ void getDataFromNet(final int page) { dataCallBack.getFlowable(page).compose(XApi.getApiTransformer()) .compose(XApi.getScheduler()) .compose(lifecycleProvider.bindToLifecycle()) - .subscribe(new ApiSubscriber() { + .subscribe(new ApiSubscriber(activity) { @Override protected void onFail(NetError error) { if (getLocalData(page) != null) { @@ -117,6 +119,11 @@ protected void function() { } } + @Override + protected boolean useCommonErrorHandler() { + return false; + } + @Override public void onNext(T o) { if (getLocalData(page) != null) { diff --git a/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt b/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt index 50577e4..76722d5 100644 --- a/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt +++ b/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt @@ -1,6 +1,7 @@ package com.lennon.cn.utill.conf import android.app.Activity +import android.app.Presentation import cn.droidlover.xdroidmvp.net.NetError import com.lennon.cn.utill.utill.DensityUtils import lennon.com.utill.R @@ -102,11 +103,19 @@ class Lennon { } provider!!.restartApp() } + fun setDensity(orientation: DensityUtils.Density) { if (provider == null) { throw Throwable("请先注册provider") } provider!!.setDensity(orientation) } + + fun setPixels(pixels: Float) { + if (provider == null) { + throw Throwable("请先注册provider") + } + provider!!.setPixels(pixels) + } } } \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt b/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt index 1da5e7a..b2f04dd 100644 --- a/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt +++ b/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt @@ -1,10 +1,8 @@ package com.lennon.cn.utill.conf import android.app.Activity -import android.graphics.drawable.GradientDrawable import cn.droidlover.xdroidmvp.net.NetError import com.lennon.cn.utill.utill.DensityUtils -import com.lennon.cn.utill.utill.Utill abstract class LennonProvider { abstract fun appName(): String @@ -16,13 +14,23 @@ abstract class LennonProvider { abstract fun isTest(): Boolean abstract fun getFilePathName(): String private var orientation: DensityUtils.Density = DensityUtils.Density.WIDTH + private var pixels: Float = 0.0f + + fun setPixels(pixels: Float) { + this.pixels = pixels + } + fun setDensity(orientation: DensityUtils.Density) { this.orientation = orientation } open fun setDensity(activity: Activity) { if (useDensity()) { - DensityUtils.setOrientation(activity, orientation) + if (pixels > 0.0f) { + DensityUtils.setOrientation(activity, orientation, pixels) + } else { + DensityUtils.setOrientation(activity, orientation) + } } } diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/CustomDialog.java b/utill/src/main/java/com/lennon/cn/utill/dialog/CustomDialog.java new file mode 100644 index 0000000..14862a3 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/CustomDialog.java @@ -0,0 +1,75 @@ +package com.lennon.cn.utill.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.os.Build; +import android.view.View; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.widget.TextView; + +import lennon.com.utill.R; + +public class CustomDialog extends Dialog { + + private Context mContext; + + public CustomDialog(Context context) { + super(context, R.style.dialog_progress); + this.mContext = context; + setParams(); + applyCompat(); + } + + public CustomDialog(Context context, int msg_resid) { + super(context, R.style.dialog_progress); + this.mContext = context; + setParams(); + setMessage(msg_resid); + applyCompat(); + } + + private void applyCompat() { + if (Build.VERSION.SDK_INT < 19) { + return; + } + getWindow().setFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + + + /** + * setMessage 提示内容 + * + * @param strMessage + */ + public void setMessage(String strMessage) { + TextView tvMsg = (TextView) findViewById(R.id.progressdialog_ms_tv); + if (tvMsg != null) { + tvMsg.setVisibility(View.VISIBLE); + tvMsg.setText(strMessage); + } else { + tvMsg.setVisibility(View.GONE); + } + } + + /** + * setMessage 提示内容 + * + * @param resid + */ + public void setMessage(int resid) { + TextView tvMsg = (TextView) findViewById(R.id.progressdialog_ms_tv); + if (tvMsg != null) { + tvMsg.setText(resid); + } + } + + private void setParams() { + setContentView(R.layout.custom_dialog); + // 设置全屏 + getWindow().setLayout(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/AmtUtils.java b/utill/src/main/java/com/lennon/cn/utill/utill/AmtUtils.java index 6527716..580b40e 100644 --- a/utill/src/main/java/com/lennon/cn/utill/utill/AmtUtils.java +++ b/utill/src/main/java/com/lennon/cn/utill/utill/AmtUtils.java @@ -185,9 +185,6 @@ public static String changeF2Y(Long amount) throws Exception { } else { String intString = amString.substring(0, amString.length() - 2); for (int i = 1; i <= intString.length(); i++) { - if ((i - 1) % 3 == 0 && i != 1) { - result.append(","); - } result.append(intString.substring(intString.length() - i, intString.length() - i + 1)); } result.reverse().append(".").append(amString.substring(amString.length() - 2)); diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt b/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt index a88bf3f..ccb73bf 100644 --- a/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt +++ b/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt @@ -47,7 +47,6 @@ object DensityUtils { }) } } - /** * 此方法在BaseActivity中做初始化(如果不封装BaseActivity的话,直接用下面那个方法就好) * 在setContentView()之前设置 @@ -108,6 +107,7 @@ object DensityUtils { activityDisplayMetrics.densityDpi = targetDensityDpi } + /** * targetDensity * targetScaledDensity diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/TimeUtill.kt b/utill/src/main/java/com/lennon/cn/utill/utill/TimeUtill.kt index d6393cc..0100ed8 100644 --- a/utill/src/main/java/com/lennon/cn/utill/utill/TimeUtill.kt +++ b/utill/src/main/java/com/lennon/cn/utill/utill/TimeUtill.kt @@ -11,7 +11,7 @@ object TimeUtill { val hours = mss % (1000 * 60 * 60 * 24) / (1000 * 60 * 60) val minutes = mss % (1000 * 60 * 60) / (1000 * 60) val seconds = mss % (1000 * 60) / 1000 - val millis = mss and 1000 + val millis = mss % 1000 var s = "" if (days > 0) { s += days.toString() + "天" diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/ZipUtils.java b/utill/src/main/java/com/lennon/cn/utill/utill/ZipUtils.java new file mode 100644 index 0000000..59ff70a --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/ZipUtils.java @@ -0,0 +1,163 @@ +package com.lennon.cn.utill.utill; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +public class ZipUtils { + private static final int BUFFER_SIZE = 2 * 1024; + + + + /** + + * 压缩成ZIP 方法1 + + * @param srcDir 压缩文件夹路径 + + * @param out 压缩文件输出流 + + * @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构; + + * false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败) + + * @throws RuntimeException 压缩失败会抛出运行时异常 + + */ + + public static void toZip(String srcDir, OutputStream out, boolean KeepDirStructure) + + throws RuntimeException{ + + long start = System.currentTimeMillis(); + + ZipOutputStream zos = null ; + + try { + + zos = new ZipOutputStream(out); + + File sourceFile = new File(srcDir); + + compress(sourceFile,zos,sourceFile.getName(),KeepDirStructure); + long end = System.currentTimeMillis(); + System.out.println("压缩完成,耗时:" + (end - start) +" ms"); + } catch (Exception e) { + throw new RuntimeException("zip error from ZipUtils",e); + }finally{ + if(zos != null){ + try { + zos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + /** + * 压缩成ZIP 方法2 + * @param srcFiles 需要压缩的文件列表 + * @param out 压缩文件输出流 + * @throws RuntimeException 压缩失败会抛出运行时异常 + */ + public static void toZip(List srcFiles , OutputStream out)throws RuntimeException { + long start = System.currentTimeMillis(); + ZipOutputStream zos = null ; + try { + zos = new ZipOutputStream(out); + for (File srcFile : srcFiles) { + byte[] buf = new byte[BUFFER_SIZE]; + zos.putNextEntry(new ZipEntry(srcFile.getName())); + int len; + FileInputStream in = new FileInputStream(srcFile); + while ((len = in.read(buf)) != -1){ + zos.write(buf, 0, len); + } + zos.closeEntry(); + in.close(); + } + long end = System.currentTimeMillis(); + System.out.println("压缩完成,耗时:" + (end - start) +" ms"); + } catch (Exception e) { + throw new RuntimeException("zip error from ZipUtils",e); + }finally{ + if(zos != null){ + try { + zos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + /** + * 递归压缩方法 + * @param sourceFile 源文件 + * @param zos zip输出流 + * @param name 压缩后的名称 + * @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构; + * false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败) + * @throws Exception + */ + private static void compress(File sourceFile, ZipOutputStream zos, String name, + boolean KeepDirStructure) throws Exception{ + byte[] buf = new byte[BUFFER_SIZE]; + if(sourceFile.isFile()){ + // 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字 + zos.putNextEntry(new ZipEntry(name)); + // copy文件到zip输出流中 + int len; + FileInputStream in = new FileInputStream(sourceFile); + while ((len = in.read(buf)) != -1){ + zos.write(buf, 0, len); + } + // Complete the entry + zos.closeEntry(); + in.close(); + } else { + File[] listFiles = sourceFile.listFiles(); + if(listFiles == null || listFiles.length == 0){ + // 需要保留原来的文件结构时,需要对空文件夹进行处理 + if(KeepDirStructure){ + // 空文件夹的处理 + zos.putNextEntry(new ZipEntry(name + "/")); + // 没有文件,不需要文件的copy + zos.closeEntry(); + } + + }else { + for (File file : listFiles) { + // 判断是否需要保留原来的文件结构 + if (KeepDirStructure) { + // 注意:file.getName()前面需要带上父文件夹的名字加一斜杠, + // 不然最后压缩包中就不能保留原来的文件结构,即:所有文件都跑到压缩包根目录下了 + compress(file, zos, name + "/" + file.getName(),KeepDirStructure); + } else { + compress(file, zos, file.getName(),KeepDirStructure); + } + + } + } + } + } + +// public static void main(String[] args) throws Exception { +// /** 测试压缩方法1 */ +// FileOutputStream fos1 = new FileOutputStream(new File("c:/mytest01.zip")); +// ZipUtils.toZip("D:/log", fos1,true); +// +// /** 测试压缩方法2 */ +// List fileList = new ArrayList<>(); +// fileList.add(new File("D:/Java/jdk1.7.0_45_64bit/bin/jar.exe")); +// fileList.add(new File("D:/Java/jdk1.7.0_45_64bit/bin/java.exe")); +// FileOutputStream fos2 = new FileOutputStream(new File("c:/mytest02.zip")); +// ZipUtils.toZip(fileList, fos2); +// } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/ClearWithSpaceEditText.java b/utill/src/main/java/com/lennon/cn/utill/widget/ClearWithSpaceEditText.java index 6c26e6b..1a1d5b5 100644 --- a/utill/src/main/java/com/lennon/cn/utill/widget/ClearWithSpaceEditText.java +++ b/utill/src/main/java/com/lennon/cn/utill/widget/ClearWithSpaceEditText.java @@ -1,5 +1,6 @@ package com.lennon.cn.utill.widget; +import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.text.Editable; @@ -11,7 +12,8 @@ import android.util.AttributeSet; import android.widget.EditText; import android.widget.Toast; -import androidx.appcompat.widget.AppCompatEditText; + +import cn.droidlover.xdroidmvp.log.XLog; import lennon.com.utill.R; /** @@ -21,14 +23,16 @@ * 创建时间:16/10/28 */ -public class ClearWithSpaceEditText extends AppCompatEditText { +@SuppressLint("AppCompatCustomView") +public class ClearWithSpaceEditText extends EditText { private int contentType; public static final int TYPE_PHONE = 0; public static final int TYPE_CARD = 1; public static final int TYPE_IDCARD = 2; + public static final int TYPE_MANAGER = 3; private int maxLength = 100; - private int start, count,before; + private int start, count, before; private String digits; public ClearWithSpaceEditText(Context context) { @@ -54,7 +58,7 @@ private void parseAttributeSet(Context context, AttributeSet attrs) { addTextChangedListener(watcher); } - private void initType(){ + private void initType() { if (contentType == TYPE_PHONE) { maxLength = 13; digits = "0123456789 "; @@ -67,6 +71,10 @@ private void initType(){ maxLength = 21; digits = null; setInputType(InputType.TYPE_CLASS_TEXT); + } else if (contentType == TYPE_MANAGER) { + maxLength = 16; + digits = "YCyc0123456789 "; + setInputType(InputType.TYPE_CLASS_TEXT); } setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)}); } @@ -75,13 +83,25 @@ private void initType(){ public void setInputType(int type) { if (contentType == TYPE_PHONE || contentType == TYPE_CARD) { type = InputType.TYPE_CLASS_NUMBER; - }else if(contentType == TYPE_IDCARD){ + } else if (contentType == TYPE_IDCARD || contentType == TYPE_MANAGER) { type = InputType.TYPE_CLASS_TEXT; } super.setInputType(type); /* 非常重要:setKeyListener要在setInputType后面调用,否则无效。*/ - if(!TextUtils.isEmpty(digits)) { - setKeyListener(DigitsKeyListener.getInstance(digits)); + if (!TextUtils.isEmpty(digits)) { + final int finalType = type; + setKeyListener(new DigitsKeyListener() { + @Override + public int getInputType() { + return finalType; + } + + @Override + protected char[] getAcceptedChars() { + char[] data = digits.toCharArray(); + return data; + } + }); } } @@ -114,16 +134,48 @@ public void afterTextChanged(Editable s) { if (!isMiddle && isSpace(s.length())) { isNeedSpace = true; } - if (isMiddle || isNeedSpace || count > 1) { - String newStr = s.toString(); + boolean isNeedDelYC = false; + boolean yc = s.toString().toUpperCase().startsWith("YC"); + boolean y = s.toString().toUpperCase().startsWith("Y"); + if (yc) { + if (s.toString().indexOf("Y", 2) > 0 || s.toString().indexOf("C", 2) > 0) { + isNeedDelYC = true; + } + } else { + String s1 = s.toString().toUpperCase(); + if (y) { + s1 = s.toString().subSequence(1, s.length()).toString(); + } + if (s1.contains("Y") || s1.contains("C")) { + isNeedDelYC = true; + } + } + if (isMiddle || isNeedSpace || count > 1 || isNeedDelYC) { + String a = s.toString().toUpperCase(); + if (isNeedDelYC) { + String replace = a.replace("Y", ""); + replace = replace.replace("C", ""); + if (yc) { + a = "YC" + replace; + } else { + if (y) { + a = "Y" + replace; + } else { + a = replace; + } + } + } + String newStr = a; newStr = newStr.replace(" ", ""); StringBuilder sb = new StringBuilder(); int spaceCount = 0; for (int i = 0; i < newStr.length(); i++) { - sb.append(newStr.substring(i, i+1)); + sb.append(newStr.substring(i, i + 1)); //如果当前输入的字符下一位为空格(i+1+1+spaceCount),因为i是从0开始计算的,所以一开始的时候需要先加1 - if(isSpace(i + 2 + spaceCount)){ + XLog.e(sb.toString()); + if (isSpace(i + 2 + spaceCount)) { sb.append(" "); + XLog.e(sb.toString()); spaceCount += 1; } } @@ -133,24 +185,24 @@ public void afterTextChanged(Editable s) { * 参照网上解决方法,将该句话替换成s.replace(...) * 该种方法不会导致输入法的跳转。 * 造成输入法跳转的原因可能是setText会重新唤起输入法控件*/ - s.replace(0, s.length(),sb); + s.replace(0, s.length(), sb); //如果是在末尾的话,或者加入的字符个数大于零的话(输入或者粘贴) if (!isMiddle || count > 1) { - setSelection(s.length() <= maxLength ? s.length() : maxLength); + setSelection(Math.min(s.length(), maxLength)); } else if (isMiddle) { //如果是删除 if (count == 0) { //如果删除时,光标停留在空格的前面,光标则要往前移一位 if (isSpace(start - before + 1)) { - setSelection((start - before) > 0 ? start - before : 0); + setSelection(Math.max((start - before), 0)); } else { - setSelection((start - before + 1) > s.length() ? s.length() : (start - before + 1)); + setSelection(Math.min((start - before + 1), s.length())); } } //如果是增加 else { if (isSpace(start - before + count)) { - setSelection((start + count - before + 1) < s.length() ? (start + count - before + 1) : s.length()); + setSelection(Math.min((start + count - before + 1), s.length())); } else { setSelection(start + count - before); } @@ -163,21 +215,22 @@ public void afterTextChanged(Editable s) { /** * 获取无空格的字符串 + * * @return */ - public String getNoSpaceText(){ - String newStr = this.getText().toString(); - if (!TextUtils.isEmpty(newStr)){ + public String getNoSpaceText() { + String newStr = this.getText().toString().toUpperCase(); + if (!TextUtils.isEmpty(newStr)) { String strNOSpace = newStr.replace(" ", ""); - if(!TextUtils.isEmpty(strNOSpace)){ + if (!TextUtils.isEmpty(strNOSpace)) { strNOSpace = strNOSpace.replace(" ", ""); } - return strNOSpace; + return strNOSpace; } return ""; } - public boolean checkTextRight(){ + public boolean checkTextRight() { String text = getNoSpaceText(); //这里做个简单的内容判断 if (contentType == TYPE_PHONE) { @@ -215,10 +268,21 @@ private boolean isSpace(int length) { return isSpaceCard(length); } else if (contentType == TYPE_IDCARD) { return isSpaceIDCard(length); + } else if (contentType == TYPE_MANAGER) { + return isSpaceManager(length); } return false; } + private boolean isSpaceManager(int length) { + XLog.e("-----------" + length + "---------" + getText()); + if (getNoSpaceText().startsWith("YC")) { + return length == 3 || length == 7 || (length > 7 && length < 16 && (length - 2) % 5 == 0); + } else { + return length >= 4 && (length == 4 || (length + 1) % 5 == 0) && length < 13; + } + } + private boolean isSpacePhone(int length) { return length >= 4 && (length == 4 || (length + 1) % 5 == 0); } diff --git a/utill/src/main/res/layout/custom_dialog.xml b/utill/src/main/res/layout/custom_dialog.xml new file mode 100644 index 0000000..489c8e3 --- /dev/null +++ b/utill/src/main/res/layout/custom_dialog.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/values/attrs.xml b/utill/src/main/res/values/attrs.xml index 6832d20..f02d064 100644 --- a/utill/src/main/res/values/attrs.xml +++ b/utill/src/main/res/values/attrs.xml @@ -9,6 +9,7 @@ + diff --git a/xrecycler/build.gradle b/xrecycler/build.gradle index 3f76546..72279f3 100644 --- a/xrecycler/build.gradle +++ b/xrecycler/build.gradle @@ -5,7 +5,7 @@ apply plugin: 'com.android.library' //publish { // artifactId = 'xrecycler' // artifactId // uploadName = 'XRecycler' // 展示名字 -// publishVersion = '1.0.4' // 版本 +// publishVersion = '1.0.5' // 版本 // userOrg = rootProject.userOrg // groupId = rootProject.groupId // repoName = rootProject.repoName From 745982d5329c29b82a4688e2239d4c23c2df36f8 Mon Sep 17 00:00:00 2001 From: Edit <1136160757@qq.com> Date: Wed, 6 Jan 2021 11:00:57 +0800 Subject: [PATCH 18/24] =?UTF-8?q?=E6=9B=B4=E6=96=B0kotlin=E7=89=88?= =?UTF-8?q?=E6=9C=AC=EF=BC=8C=E6=94=AF=E6=8C=81=E6=9C=80=E6=96=B0=E7=9A=84?= =?UTF-8?q?viewBinding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Readme.md | 5 + .../xdroidmvp/demo/ui/BasePagerFragment.java | 1 - build.gradle | 6 +- conf.gradle | 5 +- gradle/wrapper/gradle-wrapper.properties | 4 +- mvp/build.gradle | 78 +++++- .../cn/droidlover/xdroidmvp/mvp/IView.java | 8 +- .../xdroidmvp/mvp/LazyFragment.java | 258 ------------------ .../droidlover/xdroidmvp/mvp/XActivity.java | 32 ++- .../droidlover/xdroidmvp/mvp/XFragment.java | 35 ++- .../xdroidmvp/mvp/XLazyFragment.java | 103 ------- .../xdroidmvp/mvp/XPresentation.java | 36 ++- nativelogger/build.gradle | 74 ++++- qrutill/build.gradle | 85 +++++- settings.gradle | 6 +- utill/build.gradle | 74 ++++- .../com/lennon/cn/utill/base/BaseActivity.kt | 4 +- .../com/lennon/cn/utill/base/BaseFragment.kt | 5 +- .../lennon/cn/utill/base/BaseLazyFragment.kt | 163 ----------- .../lennon/cn/utill/base/BasePresentation.kt | 11 +- .../java/com/lennon/cn/utill/base/BaseView.kt | 3 +- xrecycler/build.gradle | 70 ++++- 22 files changed, 432 insertions(+), 634 deletions(-) delete mode 100644 mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java delete mode 100644 mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java delete mode 100644 utill/src/main/java/com/lennon/cn/utill/base/BaseLazyFragment.kt diff --git a/Readme.md b/Readme.md index d38d682..7e970a2 100644 --- a/Readme.md +++ b/Readme.md @@ -200,9 +200,14 @@ dependencies { * [一定]()要修改当前项目的 gradle.properties添加 android.useAndroidX=true android.enableJetifier=true +* [注意]()使用viewBinding的模块需要在 build.gradle中的 android节点内添加 + buildFeatures { + viewBinding true + } ## 更新日志 +* 2021-01-06, 添加viewBinding支持 * 2019-11-27, 修改support依赖为AndroidX依赖 * 2017-04-23,新增proguard rules,upgrade to rx2 * 2016-12-23,新增mvp、base、cache、event、imageloader、log、router diff --git a/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/BasePagerFragment.java b/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/BasePagerFragment.java index 2372fc2..bfb6ddd 100644 --- a/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/BasePagerFragment.java +++ b/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/BasePagerFragment.java @@ -9,7 +9,6 @@ import cn.droidlover.xdroidmvp.demo.model.GankResults; import cn.droidlover.xdroidmvp.demo.present.PBasePager; import cn.droidlover.xdroidmvp.demo.widget.StateView; -import cn.droidlover.xdroidmvp.mvp.XLazyFragment; import cn.droidlover.xdroidmvp.net.NetError; import cn.droidlover.xrecyclerview.XRecyclerContentLayout; import cn.droidlover.xrecyclerview.XRecyclerView; diff --git a/build.gradle b/build.gradle index ad31d92..6f9b822 100644 --- a/build.gradle +++ b/build.gradle @@ -17,10 +17,12 @@ buildscript { } dependencies { //noinspection GradleDependency - classpath 'com.android.tools.build:gradle:3.6.4' + classpath 'com.android.tools.build:gradle:4.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' // classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' - classpath 'com.novoda:bintray-release:0.9.2' +// classpath 'com.novoda:bintray-release:0.9.2' } } diff --git a/conf.gradle b/conf.gradle index 0441db8..c70b4de 100644 --- a/conf.gradle +++ b/conf.gradle @@ -8,6 +8,7 @@ ext { userOrg = 'lennon-xia' // 用户名 repoName = "lennon" // 仓库名 groupId = 'com.lennon.utill' // groupId + email = '1136160757@qq.com' website = 'https://github.com/lennon19940306/XDroidMvp-AndroidX' // 地址 , 填写真实可访问地址 licences = ['Apache-2.0']// 描述 , 填写licences // ext{ @@ -36,6 +37,7 @@ ext { ] dependencies = [ + "viewpager2" : "androidx.viewpager2:viewpager2:1.0.0", "eventBus" : "org.greenrobot:eventbus:3.2.0", "eventbus-annotation-processor": "org.greenrobot:eventbus-annotation-processor:3.2.0", @@ -81,7 +83,8 @@ ext { "picasso" : "com.squareup.picasso:picasso:+", "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.5", - "xdroid-mvp" : "com.lennon.utill:xdroid-mvp:1.3.5", + + "xdroid-mvp" : "com.lennon.utill:mvp:1.3.8", "avi-loading" : "com.wang.avi:library:+", "rxbus" : "com.blankj:rxbus:+", diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d26808f..6559024 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Dec 29 11:00:56 CST 2020 +#Wed Dec 30 10:53:20 CST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip diff --git a/mvp/build.gradle b/mvp/build.gradle index 7ccd27e..e2243cb 100644 --- a/mvp/build.gradle +++ b/mvp/build.gradle @@ -2,21 +2,9 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' -//apply plugin: 'kotlin-parcelize' - -apply plugin: 'com.novoda.bintray-release' - -publish { - artifactId = 'xdroid-mvp' // artifactId - uploadName = 'XDroidMvp' // 展示名字 - publishVersion = '1.3.5' // 版本 - userOrg = rootProject.ext.userOrg - groupId =rootProject.ext.groupId - repoName = rootProject.ext.repoName - desc = '' - website = rootProject.ext.website - licences = rootProject.ext.licences -} +apply plugin: 'com.github.dcendents.android-maven' +apply plugin: 'com.jfrog.bintray' +version = '1.3.8' android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -27,6 +15,9 @@ android { versionCode rootProject.ext.android.versionCode versionName rootProject.ext.android.versionName } + buildFeatures { + viewBinding true + } buildTypes { release { minifyEnabled false @@ -71,6 +62,8 @@ dependencies { api rootProject.ext.dependencies["kotlin"] api rootProject.ext.dependencies["nativelogger"] + + api rootProject.ext.dependencies["viewpager2"] } tasks.withType(JavaCompile) { @@ -93,3 +86,58 @@ task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from javadoc.destinationDir } +artifacts { + archives javadocJar + archives sourcesJar +} +group = rootProject.groupId + +install { + repositories.mavenInstaller { + pom { + project { + packaging 'jar' + name 'XDroidMvp' + url rootProject.website + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id 'xdroid-mvp' + name rootProject.groupId + email rootProject.email + } + } + scm { + connection rootProject.gitUrl + developerConnection rootProject.gitUrl + url rootProject.siteUrl + } + } + } + } +} + +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null){ + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +bintray { + user = isHasFile ? properties.getProperty("bintray.user") : System.getenv("bintray.user") + key = isHasFile ? properties.getProperty("bintray.apikey") : System.getenv("bintray.apikey") + configurations = ['archives'] + pkg { + repo = rootProject.repoName + name = "XDroidMvp" + websiteUrl = siteUrl + vcsUrl = gitUrl + licenses = ["Apache-2.0"] + publish = true + } +} \ No newline at end of file diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java index 5b89b62..dd1fe00 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java @@ -3,11 +3,13 @@ import android.os.Bundle; import android.view.View; +import androidx.viewbinding.ViewBinding; + /** * Created by wanglei on 2016/12/29. */ -public interface IView

{ +public interface IView

{ void bindEvent(); @@ -15,12 +17,14 @@ public interface IView

{ int getOptionsMenuId(); - int getLayoutId(); + E getViewBinding(); boolean useEventBus(); P newP(); + Class getViewBindingClass(); + void showProgressDialog(String msg); void closeProgressDialog(); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java deleted file mode 100644 index d5a5320..0000000 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java +++ /dev/null @@ -1,258 +0,0 @@ -package cn.droidlover.xdroidmvp.mvp; - -import android.app.Activity; -import android.content.Context; -import android.os.Bundle; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import com.trello.rxlifecycle3.components.support.RxFragment; - - -import java.lang.reflect.Field; - -/** - * Created by wanglei on 2017/1/28. - */ - -public class LazyFragment extends RxFragment { - protected LayoutInflater layoutInflater; - protected Activity context; - - private View rootView; - private ViewGroup container; - - private boolean isInitReady = false; - private int isVisibleToUserState = STATE_NO_SET; - private Bundle saveInstanceState; - private boolean isLazyEnable = true; - private boolean isStart = false; - private FrameLayout layout; - - private static final int STATE_VISIBLE = 1; //用户可见 - private static final int STATE_NO_SET = -1; //未设置值 - private static final int STATE_NO_VISIBLE = 0; //用户不可见 - - private static final String TAG_ROOT_FRAMELAYOUT = "tag_root_framelayout"; - - - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - this.layoutInflater = inflater; - this.container = container; - onCreateView(savedInstanceState); - if (rootView == null) - return super.onCreateView(inflater, container, savedInstanceState); - - return rootView; - } - - private void onCreateView(Bundle savedInstanceState) { - this.saveInstanceState = savedInstanceState; - boolean isVisible; - if (isVisibleToUserState == STATE_NO_SET) { - isVisible = getUserVisibleHint(); - } else { - isVisible = isVisibleToUserState == STATE_VISIBLE; - } - if (isLazyEnable) { - if (isVisible && !isInitReady) { - onCreateViewLazy(savedInstanceState); - isInitReady = true; - } else { - LayoutInflater mInflater = layoutInflater; - if (mInflater == null && context != null) { - mInflater = LayoutInflater.from(context); - } - layout = new FrameLayout(context); - layout.setTag(TAG_ROOT_FRAMELAYOUT); - - View view = getPreviewLayout(mInflater, layout); - if (view != null) { - layout.addView(view); - } - layout.setLayoutParams( - new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - setContentView(layout); - } - } else { - onCreateViewLazy(savedInstanceState); - isInitReady = true; - } - } - - protected View getRealRootView() { - if (rootView != null) { - if (rootView instanceof FrameLayout - && TAG_ROOT_FRAMELAYOUT.equals(rootView.getTag())) { - return ((FrameLayout) rootView).getChildAt(0); - } - } - - return rootView; - } - - protected View getRootView() { - return rootView; - } - - protected View $(int id) { - if (rootView != null) { - return rootView.findViewById(id); - } - return null; - } - - protected void setContentView(int layoutResID) { - if (isLazyEnable && getRootView() != null && getRootView().getParent() != null) { - layout.removeAllViews(); - View view = layoutInflater.inflate(layoutResID, layout, false); - layout.addView(view); - } else { - rootView = layoutInflater.inflate(layoutResID, container, false); - } - } - - protected void setContentView(View view) { - if (isLazyEnable && getRootView() != null && getRootView().getParent() != null) { - layout.removeAllViews(); - layout.addView(view); - } else { - rootView = view; - } - } - - @Override - public void setUserVisibleHint(boolean isVisibleToUser) { - super.setUserVisibleHint(isVisibleToUser); - isVisibleToUserState = isVisibleToUser ? STATE_VISIBLE : STATE_NO_VISIBLE; - if (isVisibleToUser - && !isInitReady - && getRootView() != null) { - isInitReady = true; - onCreateViewLazy(saveInstanceState); - onResumeLazy(); - } - if (isInitReady && getRootView() != null) { - if (isVisibleToUser) { - isStart = true; - onStartLazy(); - } else { - isStart = false; - onStopLazy(); - } - } - } - - @Override - public void onResume() { - super.onResume(); - if (isInitReady) { - onResumeLazy(); - } - } - - @Override - public void onPause() { - super.onPause(); - if (isInitReady) { - onPauseLazy(); - } - } - - @Override - public void onStart() { - super.onStart(); - if (isInitReady - && !isStart - && getUserVisibleHint()) { - isStart = true; - onStartLazy(); - } - } - - @Override - public void onStop() { - super.onStop(); - if (isInitReady - && isStart - && getUserVisibleHint()) { - isStart = false; - onStopLazy(); - } - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - if (context instanceof Activity) { - this.context = (Activity) context; - } - } - - - @Override - public void onDetach() { - super.onDetach(); - context = null; - - try { - Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager"); - childFragmentManager.setAccessible(true); - childFragmentManager.set(this, null); - } catch (NoSuchFieldException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - - @Override - public void onDestroyView() { - super.onDestroyView(); - rootView = null; - container = null; - layoutInflater = null; - if (isInitReady) { - onDestoryLazy(); - } - isInitReady = false; - } - - protected View getPreviewLayout(LayoutInflater mInflater, FrameLayout layout) { - return null; - } - - protected void onCreateViewLazy(Bundle savedInstanceState) { - - } - - protected void onStartLazy() { - - } - - protected void onStopLazy() { - - } - - protected void onResumeLazy() { - - } - - protected void onPauseLazy() { - - } - - protected void onDestoryLazy() { - - } - - -} diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java index f8a2b15..0ca9305 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java @@ -4,7 +4,9 @@ import android.os.Bundle; import androidx.annotation.Nullable; +import androidx.viewbinding.ViewBinding; +import android.view.LayoutInflater; import android.view.Menu; import com.tbruyelle.rxpermissions2.RxPermissions; @@ -14,11 +16,15 @@ import com.trello.rxlifecycle3.components.support.RxAppCompatActivity; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + /** * Created by wanglei on 2016/12/29. */ -public abstract class XActivity

extends RxAppCompatActivity implements IView

{ +public abstract class XActivity

extends RxAppCompatActivity implements IView { private VDelegate vDelegate; private P p; @@ -26,22 +32,34 @@ public abstract class XActivity

extends RxAppCompatActivity private RxPermissions rxPermissions; + private E viewBinding; + @Override public void onRefresh(Boolean bRefresh) { - + + } + + @Override + public E getViewBinding() { + return viewBinding; } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); context = this; - getP(); - - if (getLayoutId() > 0) { - setContentView(getLayoutId()); - bindEvent(); + try { + Class eClass = getViewBindingClass(); + Method method2 = eClass.getMethod("inflate", LayoutInflater.class); + viewBinding = (E) method2.invoke(null, getLayoutInflater()); + } catch (Exception e) { + e.printStackTrace(); + } + if (viewBinding != null) { + setContentView(viewBinding.getRoot()); } + bindEvent(); initData(savedInstanceState); } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java index 474224b..8cbad45 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java @@ -3,7 +3,10 @@ import android.app.Activity; import android.content.Context; import android.os.Bundle; + import androidx.annotation.Nullable; +import androidx.viewbinding.ViewBinding; + import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -12,41 +15,55 @@ import cn.droidlover.xdroidmvp.XDroidConf; import cn.droidlover.xdroidmvp.event.BusProvider; + import com.trello.rxlifecycle3.components.support.RxFragment; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + /** * Created by wanglei on 2016/12/29. */ -public abstract class XFragment

extends RxFragment implements IView

{ +public abstract class XFragment

extends RxFragment implements IView { private VDelegate vDelegate; private P p; protected Activity context; - private View rootView; protected LayoutInflater layoutInflater; private RxPermissions rxPermissions; + private E viewBinding; @Override public void onRefresh(Boolean bRefresh) { } + @Override + public E getViewBinding() { + return viewBinding; + } + @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { layoutInflater = inflater; - if (rootView == null && getLayoutId() > 0) { - rootView = inflater.inflate(getLayoutId(), null); - } else { - ViewGroup viewGroup = (ViewGroup) rootView.getParent(); + try { + Class eClass = getViewBindingClass(); + Method method2 = eClass.getMethod("inflate", LayoutInflater.class, ViewGroup.class, Boolean.class); + viewBinding = (E) method2.invoke(null, inflater, container, false); + } catch (Exception e) { + e.printStackTrace(); + } + if (viewBinding != null) { + ViewGroup viewGroup = (ViewGroup) viewBinding.getRoot().getParent(); if (viewGroup != null) { - viewGroup.removeView(rootView); + viewGroup.removeView(viewBinding.getRoot()); } } - - return rootView; + return viewBinding.getRoot(); } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java deleted file mode 100644 index 0ede4c9..0000000 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java +++ /dev/null @@ -1,103 +0,0 @@ -package cn.droidlover.xdroidmvp.mvp; - -import android.os.Bundle; - -import com.tbruyelle.rxpermissions2.RxPermissions; - -import cn.droidlover.xdroidmvp.XDroidConf; -import cn.droidlover.xdroidmvp.event.BusProvider; - -/** - * Created by wanglei on 2017/1/26. - */ - -public abstract class XLazyFragment

- extends LazyFragment implements IView

{ - - private VDelegate vDelegate; - private P p; - - private RxPermissions rxPermissions; - - @Override - public void onRefresh(Boolean bRefresh) { - - } - - @Override - protected void onCreateViewLazy(Bundle savedInstanceState) { - super.onCreateViewLazy(savedInstanceState); - - getP(); - - if (getLayoutId() > 0) { - setContentView(getLayoutId()); - } - if (useEventBus()) { - BusProvider.getBus().register(this); - } - bindEvent(); - initData(savedInstanceState); - } - - @Override - public void bindEvent() { - - } - - - public VDelegate getvDelegate() { - if (vDelegate == null) { - vDelegate = VDelegateBase.create(context); - } - return vDelegate; - } - - protected P getP() { - if (p == null) { - p = newP(); - } - if (p != null) { - if (!p.hasV()) { - p.attachV(this); - } - } - return p; - } - - @Override - protected void onDestoryLazy() { - super.onDestoryLazy(); - if (useEventBus()) { - BusProvider.getBus().unregister(this); - } - if (getP() != null) { - getP().detachV(); - } - getvDelegate().destory(); - - p = null; - vDelegate = null; - } - - - protected RxPermissions getRxPermissions() { - rxPermissions = new RxPermissions(getActivity()); - rxPermissions.setLogging(XDroidConf.DEV); - return rxPermissions; - } - - - @Override - public int getOptionsMenuId() { - return 0; - } - - - @Override - public boolean useEventBus() { - return false; - } - - -} diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java index 8c5a317..45ae7e1 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java @@ -4,30 +4,40 @@ import android.content.Context; import android.os.Bundle; import android.view.Display; +import android.view.LayoutInflater; + +import androidx.viewbinding.ViewBinding; import com.tbruyelle.rxpermissions2.RxPermissions; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + import cn.droidlover.xdroidmvp.XDroidConf; import cn.droidlover.xdroidmvp.event.BusProvider; -public abstract class XPresentation

extends Presentation implements IView

{ +public abstract class XPresentation

extends Presentation implements IView { private P p; private VDelegate vDelegate; protected Context context; - + private E viewBinding; private RxPermissions rxPermissions; + public XPresentation(Context outerContext, Display display) { super(outerContext, display); - this.context=outerContext; + this.context = outerContext; } + protected RxPermissions getRxPermissions() { rxPermissions = new RxPermissions(getOwnerActivity()); rxPermissions.setLogging(XDroidConf.DEV); return rxPermissions; } + public XPresentation(Context outerContext, Display display, int theme) { super(outerContext, display, theme); - this.context=outerContext; + this.context = outerContext; } @Override @@ -35,17 +45,29 @@ public void onRefresh(Boolean bRefresh) { } + @Override + public E getViewBinding() { + return viewBinding; + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getP(); - - if (getLayoutId() > 0) { - setContentView(getLayoutId()); + try { + Class eClass = getViewBindingClass(); + Method method2 = eClass.getMethod("inflate", LayoutInflater.class); + viewBinding = (E) method2.invoke(null, getLayoutInflater()); + } catch (Exception e) { + e.printStackTrace(); + } + if (viewBinding != null) { + setContentView(viewBinding.getRoot()); bindEvent(); } initData(savedInstanceState); } + protected VDelegate getvDelegate() { if (vDelegate == null) { vDelegate = VDelegateBase.create(context); diff --git a/nativelogger/build.gradle b/nativelogger/build.gradle index 257c3e3..507568a 100644 --- a/nativelogger/build.gradle +++ b/nativelogger/build.gradle @@ -1,19 +1,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' - -//apply plugin: 'com.novoda.bintray-release' -// -//publish { -// artifactId = 'nativelogger' // artifactId -// uploadName = 'NativeLogger' // 展示名字 -// publishVersion = '1.1' // 版本 -// userOrg = rootProject.userOrg -// groupId = rootProject.groupId -// repoName = rootProject.repoName -// desc = '' -// website = rootProject.website -// licences = rootProject.licences -//} +apply plugin: 'com.github.dcendents.android-maven' +apply plugin: 'com.jfrog.bintray' android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -56,4 +44,62 @@ task javadoc(type: Javadoc) { task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from javadoc.destinationDir +} + +artifacts { + archives sourcesJar + archives javadocJar +} +version = '1.1' +group = rootProject.groupId + +install { + repositories.mavenInstaller { + // This generates POM.xml with proper parameters + pom { + project { + packaging 'jar' + name 'NativeLogger' + url rootProject.website + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id 'nativelogger' + name rootProject.groupId + email rootProject.email + } + } + scm { + connection rootProject.gitUrl + developerConnection rootProject.gitUrl + url rootProject.siteUrl + } + } + } + } +} + +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null){ + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +bintray { + user = isHasFile ? properties.getProperty("bintray.user") : System.getenv("bintray.user") + key = isHasFile ? properties.getProperty("bintray.apikey") : System.getenv("bintray.apikey") + configurations = ['archives'] + pkg { + repo = rootProject.repoName + name = "NativeLogger" + websiteUrl = siteUrl + vcsUrl = gitUrl + licenses = ["Apache-2.0"] + publish = true + } } \ No newline at end of file diff --git a/qrutill/build.gradle b/qrutill/build.gradle index 978864d..675a74f 100644 --- a/qrutill/build.gradle +++ b/qrutill/build.gradle @@ -1,18 +1,10 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' +apply plugin: 'com.github.dcendents.android-maven' +apply plugin: 'com.jfrog.bintray' + +version = '1.0.3' // 版本 -apply plugin: 'com.novoda.bintray-release' -publish { - artifactId = 'lennon-qr-utill' // artifactId - uploadName = 'LennonQRUtill' // 展示名字 - publishVersion = '1.0.3' // 版本 - userOrg = rootProject.userOrg - groupId = rootProject.groupId - repoName = rootProject.repoName - desc = '' - website = rootProject.website - licences = rootProject.licences -} android { @@ -55,4 +47,73 @@ task javadoc(type: Javadoc) { task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from javadoc.destinationDir +} +artifacts { + archives sourcesJar + archives javadocJar +} +//apply plugin: 'com.novoda.bintray-release' +//publish { +// artifactId = 'lennon-qr-utill' // artifactId +// uploadName = 'LennonQRUtill' // 展示名字 +// publishVersion = '1.0.3' // 版本 +// userOrg = rootProject.userOrg +// groupId = rootProject.groupId +// repoName = rootProject.repoName +// desc = '' +// website = rootProject.website +// licences = rootProject.licences +//} + +group = rootProject.groupId + +install { + repositories.mavenInstaller { + // This generates POM.xml with proper parameters + pom { + project { + packaging 'jar' + name 'LennonQRUtill' + url rootProject.website + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id 'lennon-qr-utill' + name rootProject.groupId + email rootProject.email + } + } + scm { + connection rootProject.gitUrl + developerConnection rootProject.gitUrl + url rootProject.siteUrl + } + } + } + } +} + +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null){ + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +bintray { + user = isHasFile ? properties.getProperty("bintray.user") : System.getenv("bintray.user") + key = isHasFile ? properties.getProperty("bintray.apikey") : System.getenv("bintray.apikey") + configurations = ['archives'] + pkg { + repo = rootProject.repoName + name = "LennonQRUtill" + websiteUrl = siteUrl + vcsUrl = gitUrl + licenses = ["Apache-2.0"] + publish = true + } } \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 24f7e11..24f5f4e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,8 +1,8 @@ //include ':mylibrary' //include ':app' -//include':mvp' +include':mvp' //include':test' -//include':xrecycler' +include':xrecycler' include':utill' -//include':nativelogger' +include':nativelogger' include ':qrutill' diff --git a/utill/build.gradle b/utill/build.gradle index 08e85e8..e48712b 100644 --- a/utill/build.gradle +++ b/utill/build.gradle @@ -3,18 +3,10 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-parcelize' -apply plugin: 'com.novoda.bintray-release' -publish { - artifactId = 'lennon-utill' // artifactId - uploadName = 'LennonUtill' // 展示名字 - publishVersion = '1.6.16' // 版本 - userOrg = rootProject.userOrg - groupId = rootProject.groupId - repoName = rootProject.repoName - desc = '' - website = rootProject.website - licences = rootProject.licences -} +apply plugin: 'com.github.dcendents.android-maven' +apply plugin: 'com.jfrog.bintray' + +version = '1.6.21' // 版本 android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -29,8 +21,6 @@ android { dependencies { api fileTree(include: ['*.jar'], dir: 'libs') -// api project(path: ':mvp') - api rootProject.ext.dependencies["xdroid-mvp"] api rootProject.ext.dependencies["PersistentCookieJar"] @@ -61,3 +51,59 @@ task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from javadoc.destinationDir } +artifacts { + archives javadocJar + archives sourcesJar +} + +group = rootProject.groupId + +install { + repositories.mavenInstaller { + pom { + project { + packaging 'jar' + name 'LennonUtill' + url rootProject.website + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id 'lennon-utill' + name rootProject.groupId + email rootProject.email + } + } + scm { + connection rootProject.gitUrl + developerConnection rootProject.gitUrl + url rootProject.siteUrl + } + } + } + } +} + +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null){ + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +bintray { + user = isHasFile ? properties.getProperty("bintray.user") : System.getenv("bintray.user") + key = isHasFile ? properties.getProperty("bintray.apikey") : System.getenv("bintray.apikey") + configurations = ['archives'] + pkg { + repo = rootProject.repoName + name = "LennonUtill" + websiteUrl = siteUrl + vcsUrl = gitUrl + licenses = ["Apache-2.0"] + publish = true + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt index 110467a..e19e91a 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt @@ -10,6 +10,7 @@ import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.widget.Toast +import androidx.viewbinding.ViewBinding import cn.droidlover.xdroidmvp.log.XLog import cn.droidlover.xdroidmvp.mvp.XActivity import cn.droidlover.xdroidmvp.net.NetError @@ -25,7 +26,8 @@ import com.lennon.cn.utill.utill.Utill import java.lang.Exception -abstract class BaseActivity

> : XActivity

(), BaseView

{ +abstract class BaseActivity

, E : ViewBinding> : XActivity(), + BaseView { private var TAG = javaClass.simpleName private var dialog: CustomProgressDialog? = null diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt index 3b7f5a9..959b882 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt @@ -1,3 +1,5 @@ +@file:Suppress("FINITE_BOUNDS_VIOLATION_IN_JAVA") + package com.lennon.cn.utill.base import android.app.Activity @@ -8,6 +10,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast +import androidx.viewbinding.ViewBinding import cn.droidlover.xdroidmvp.log.XLog import cn.droidlover.xdroidmvp.mvp.XFragment @@ -16,7 +19,7 @@ import com.lennon.cn.utill.dialog.CommonAlertDialog import com.lennon.cn.utill.dialog.CustomProgressDialog import com.lennon.cn.utill.dialog.OnAlertDialogListener -abstract class BaseFragment

> : XFragment

(), BaseView

{ +abstract class BaseFragment

,E:ViewBinding> : XFragment(), BaseView { private var isFragmentVisible: Boolean = false private var isReuseView: Boolean = false private var isFirstVisible: Boolean = true diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseLazyFragment.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseLazyFragment.kt deleted file mode 100644 index 1492c95..0000000 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseLazyFragment.kt +++ /dev/null @@ -1,163 +0,0 @@ -package com.lennon.cn.utill.base - -import android.app.Activity -import android.content.Context -import android.os.Bundle -import android.os.Handler -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import cn.droidlover.xdroidmvp.log.XLog -import cn.droidlover.xdroidmvp.mvp.XLazyFragment -import com.lennon.cn.utill.bean.ToastRunnable -import com.lennon.cn.utill.dialog.CommonAlertDialog -import com.lennon.cn.utill.dialog.CustomProgressDialog -import com.lennon.cn.utill.dialog.OnAlertDialogListener - -abstract class BaseLazyFragment

> : XLazyFragment

(), BaseView

{ - private var mView: View? = null - private var dialog: CustomProgressDialog? = null - private var mActivity: Activity? = null - override fun showLoading(visibility: Int) { - } - - private var isFirst = true - private var stop = false - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - XLog.e(getName() + " onCreateView") - mView = super.onCreateView(inflater, container, savedInstanceState) - return mView - } - - - override fun showProgressDialog(msg: String) { - if (dialog != null) - dialog!!.dismiss() - dialog = CustomProgressDialog(getContext()) - dialog!!.setMessage(msg) - dialog!!.show() - } - - - override fun onResume() { - super.onResume() - XLog.e(getName() + " onResume") - } - - - protected fun getName(): String { - return javaClass.simpleName - } - - override fun closeProgressDialog() { - if (null != dialog) - dialog!!.dismiss() - } - - override fun toast(msg: String) { - toast(msg, true) - } - - override fun toast(msg: String, second: Int, runnable: ToastRunnable) { - val dialog = CommonAlertDialog(getContext()) - dialog.setMsg(msg) - dialog.disableCancle() - dialog.setCanceledOnTouchOutside(false) - dialog.setDialogListener(object : OnAlertDialogListener() { - override fun onSure() { - super.onSure() - dialog.dismiss() - runnable.run() - } - }) - dialog.show() - Handler().postDelayed(Runnable { - dialog.dismiss() - runnable.run() - }, second * 1000L) - Toast.makeText(getContext(), msg, second) - .show() - } - - override fun toast(msg: String, runnable: ToastRunnable) { - val dialog = CommonAlertDialog(getContext()) - dialog.setMsg(msg) - dialog.disableCancle() - dialog.show() - dialog.setDialogListener(object : OnAlertDialogListener() { - override fun onSure() { - super.onSure() - dialog.dismiss() - runnable.run() - } - }) - Handler().postDelayed(Runnable { - dialog.dismiss() - runnable.run() - }, 2000L) - Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) - .show() - } - - override fun toast(msg: String, flag: Boolean) { - if (flag) { - val dialog = CommonAlertDialog(getContext()) - dialog.setMsg(msg) - dialog.disableCancle() - dialog.show() - dialog.setDialogListener(object : OnAlertDialogListener() { - override fun onSure() { - super.onSure() - dialog.dismiss() - } - }) - } - Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) - .show() - } - - override fun toast(msg: String, second: Int) { - val dialog = CommonAlertDialog(getContext()) - dialog.setMsg(msg) - dialog.disableCancle() - dialog.show() - dialog.setDialogListener(object : OnAlertDialogListener() { - override fun onSure() { - super.onSure() - dialog.dismiss() - } - }) - Toast.makeText(getContext(), msg, second).show() - } - - override fun onStop() { - XLog.e(getName() + " onStop") - stop = true - super.onStop() - } - - override fun onAttach(activity: Activity) { - XLog.e(getName() + " onAttach") - super.onAttach(activity) - mActivity = activity - } - - override fun onPause() { - XLog.e(getName() + " onPause") - super.onPause() - } - - override fun getContext(): Context? { - return if (mActivity != null) { - mActivity - } else { - BaseApplication.getCuttureActivity() - } - } -} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt b/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt index 3e2ca7b..48fb3dc 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt @@ -6,6 +6,7 @@ import android.os.Bundle import android.os.Handler import android.view.Display import android.widget.Toast +import androidx.viewbinding.ViewBinding import cn.droidlover.xdroidmvp.log.XLog import cn.droidlover.xdroidmvp.mvp.XPresentation import cn.droidlover.xdroidmvp.net.NetError @@ -15,16 +16,16 @@ import com.lennon.cn.utill.dialog.CommonAlertDialog import com.lennon.cn.utill.dialog.CustomProgressDialog import com.lennon.cn.utill.dialog.OnAlertDialogListener -abstract class BasePresentation

>(context: Context, display: Display) : - XPresentation

(context, display), BaseView

{ +abstract class BasePresentation

,E: ViewBinding>(context: Context, display: Display) : + XPresentation(context, display), BaseView { private var TAG = javaClass.simpleName private var dialog: CustomProgressDialog? = null private var listener: ChangeListener? = null interface ChangeListener { - fun onCancel(basePresentation: BasePresentation<*>) - fun onDismiss(basePresentation: BasePresentation<*>) - fun onShow(basePresentation: BasePresentation<*>) + fun onCancel(basePresentation: BasePresentation<*,*>) + fun onDismiss(basePresentation: BasePresentation<*,*>) + fun onShow(basePresentation: BasePresentation<*,*>) } fun setListener(listener: ChangeListener?) { diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt index e12edbb..032685c 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt @@ -2,11 +2,12 @@ package com.lennon.cn.utill.base import android.app.Activity import android.content.Context +import androidx.viewbinding.ViewBinding import cn.droidlover.xdroidmvp.mvp.IView import cn.droidlover.xdroidmvp.net.NetError import com.lennon.cn.utill.bean.ToastRunnable -interface BaseView

?> :IView

{ +interface BaseView

?, E : ViewBinding> : IView { fun toast(msg: String, second: Int) fun toast(msg: String) diff --git a/xrecycler/build.gradle b/xrecycler/build.gradle index 72279f3..8c7d85b 100644 --- a/xrecycler/build.gradle +++ b/xrecycler/build.gradle @@ -1,18 +1,9 @@ apply plugin: 'com.android.library' -//apply plugin: 'com.novoda.bintray-release' -// -//publish { -// artifactId = 'xrecycler' // artifactId -// uploadName = 'XRecycler' // 展示名字 -// publishVersion = '1.0.5' // 版本 -// userOrg = rootProject.userOrg -// groupId = rootProject.groupId -// repoName = rootProject.repoName -// desc = '' -// website = rootProject.website -// licences = rootProject.licences -//} +apply plugin: 'com.github.dcendents.android-maven' +apply plugin: 'com.jfrog.bintray' +version = '1.0.5' // 版本 + android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -69,3 +60,56 @@ artifacts { archives sourcesJar archives javadocJar } + +group = rootProject.groupId + +install { + repositories.mavenInstaller { + // This generates POM.xml with proper parameters + pom { + project { + packaging 'jar' + name 'XRecycler' + url rootProject.website + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id 'xrecycler' + name rootProject.groupId + email rootProject.email + } + } + scm { + connection rootProject.gitUrl + developerConnection rootProject.gitUrl + url rootProject.siteUrl + } + } + } + } +} + +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null){ + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +bintray { + user = isHasFile ? properties.getProperty("bintray.user") : System.getenv("bintray.user") + key = isHasFile ? properties.getProperty("bintray.apikey") : System.getenv("bintray.apikey") + configurations = ['archives'] + pkg { + repo = rootProject.repoName + name = "XRecycler" + websiteUrl = siteUrl + vcsUrl = gitUrl + licenses = ["Apache-2.0"] + publish = true + } +} \ No newline at end of file From dff091419b22310681debb84da52e25c18a65db6 Mon Sep 17 00:00:00 2001 From: Edit <1136160757@qq.com> Date: Tue, 16 Mar 2021 10:06:23 +0800 Subject: [PATCH 19/24] =?UTF-8?q?=E4=BD=BF=E7=94=A8viewbinding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf.gradle | 9 +- http/.gitignore | 1 + http/build.gradle | 118 ++++++++++++++++++ http/consumer-rules.pro | 0 http/proguard-rules.pro | 21 ++++ .../utill/http/ExampleInstrumentedTest.kt | 24 ++++ http/src/main/AndroidManifest.xml | 5 + .../com/lennon/utill/http/ExampleUnitTest.kt | 17 +++ mvp/build.gradle | 4 +- .../droidlover/xdroidmvp/XDroidMvpUtill.java | 46 ++++++- .../base/SimpleViewBindingAdapter.java | 74 +++++++++++ .../cn/droidlover/xdroidmvp/mvp/IView.java | 6 +- .../droidlover/xdroidmvp/mvp/XActivity.java | 16 ++- .../droidlover/xdroidmvp/mvp/XFragment.java | 17 ++- .../xdroidmvp/mvp/XPresentation.java | 10 +- nativelogger/build.gradle | 2 +- qrutill/build.gradle | 4 +- settings.gradle | 4 +- utill/build.gradle | 4 +- .../com/lennon/cn/utill/base/BaseFragment.kt | 79 +----------- .../java/com/lennon/cn/utill/base/BaseView.kt | 2 +- .../java/com/lennon/cn/utill/utill/Utill.kt | 32 ++--- xrecycler/build.gradle | 5 +- 23 files changed, 368 insertions(+), 132 deletions(-) create mode 100644 http/.gitignore create mode 100644 http/build.gradle create mode 100644 http/consumer-rules.pro create mode 100644 http/proguard-rules.pro create mode 100644 http/src/androidTest/java/com/lennon/utill/http/ExampleInstrumentedTest.kt create mode 100644 http/src/main/AndroidManifest.xml create mode 100644 http/src/test/java/com/lennon/utill/http/ExampleUnitTest.kt create mode 100644 mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleViewBindingAdapter.java diff --git a/conf.gradle b/conf.gradle index c70b4de..a2b4a00 100644 --- a/conf.gradle +++ b/conf.gradle @@ -24,7 +24,6 @@ ext { minSdkVersion : 17, targetSdkVersion : 29, - versionCode : 1, versionName : '1.0.0', @@ -76,15 +75,15 @@ ext { "design" : "com.google.android.material:material:1.0.0", - "recyclerview-v7" : "androidx.recyclerview:recyclerview:1.1.0", + "recyclerview-v7" : "androidx.recyclerview:recyclerview:1.0.0", "eventbus" : "org.greenrobot:eventbus:+", "picasso" : "com.squareup.picasso:picasso:+", - "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.5", + "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.7", - "xdroid-mvp" : "com.lennon.utill:mvp:1.3.8", + "xdroid-mvp" : "com.lennon.utill:mvp:1.3.23", "avi-loading" : "com.wang.avi:library:+", "rxbus" : "com.blankj:rxbus:+", @@ -110,7 +109,7 @@ ext { "canary-debug" : "com.squareup.leakcanary:leakcanary-android:+", "canary-release" : "com.squareup.leakcanary:leakcanary-android-no-op:+", - "XStateController" : "com.github.limedroid:XStateController:+", + "XStateController" : "com.github.limedroid:XStateController:v1.1.1", "nativelogger" : "com.lennon.utill:nativelogger:1.1" ] diff --git a/http/.gitignore b/http/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/http/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/http/build.gradle b/http/build.gradle new file mode 100644 index 0000000..f36c28c --- /dev/null +++ b/http/build.gradle @@ -0,0 +1,118 @@ +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'com.github.dcendents.android-maven' + id 'com.jfrog.bintray' +} +version = "0.0.1" +android { + + compileSdkVersion rootProject.ext.android.compileSdkVersion + + defaultConfig { + minSdkVersion rootProject.ext.android.minSdkVersion + targetSdkVersion rootProject.ext.android.targetSdkVersion + versionCode rootProject.ext.android.versionCode + versionName rootProject.ext.android.versionName + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + api rootProject.ext.dependencies["kotlin"] + api rootProject.ext.dependencies["retrofit"] + api rootProject.ext.dependencies["retrofit-converter-gson"] + api rootProject.ext.dependencies["okhttp3"] + testImplementation 'junit:junit:4.13.1' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' +} +// 生成sourceJar和javaDocJar构件 +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +task javadoc(type: Javadoc) { + failOnError false + source = android.sourceSets.main.java.sourceFiles + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + classpath += configurations.compile +} +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} +artifacts { + archives sourcesJar + archives javadocJar +} + +group = rootProject.groupId + +install { + repositories.mavenInstaller { + // This generates POM.xml with proper parameters + pom { + project { + packaging 'aar' + name 'LennonHttpUtill' + url rootProject.website + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id 'lennon-http-utill' + name rootProject.groupId + email rootProject.email + } + } + scm { + connection rootProject.gitUrl + developerConnection rootProject.gitUrl + url rootProject.siteUrl + } + } + } + } +} + +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +bintray { + user = isHasFile ? properties.getProperty("bintray.user") : System.getenv("bintray.user") + key = isHasFile ? properties.getProperty("bintray.apikey") : System.getenv("bintray.apikey") + configurations = ['archives'] + pkg { + repo = rootProject.repoName + name = "LennonHttpUtill" + websiteUrl = siteUrl + vcsUrl = gitUrl + licenses = ["Apache-2.0"] + publish = true + } +} \ No newline at end of file diff --git a/http/consumer-rules.pro b/http/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/http/proguard-rules.pro b/http/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/http/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/http/src/androidTest/java/com/lennon/utill/http/ExampleInstrumentedTest.kt b/http/src/androidTest/java/com/lennon/utill/http/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..9da237b --- /dev/null +++ b/http/src/androidTest/java/com/lennon/utill/http/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.lennon.utill.http + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.lennon.utill.http.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/http/src/main/AndroidManifest.xml b/http/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f4115ae --- /dev/null +++ b/http/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/http/src/test/java/com/lennon/utill/http/ExampleUnitTest.kt b/http/src/test/java/com/lennon/utill/http/ExampleUnitTest.kt new file mode 100644 index 0000000..6501c71 --- /dev/null +++ b/http/src/test/java/com/lennon/utill/http/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.lennon.utill.http + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/mvp/build.gradle b/mvp/build.gradle index e2243cb..39a4133 100644 --- a/mvp/build.gradle +++ b/mvp/build.gradle @@ -4,7 +4,7 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'com.jfrog.bintray' -version = '1.3.8' +version = '1.3.23' android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -96,7 +96,7 @@ install { repositories.mavenInstaller { pom { project { - packaging 'jar' + packaging 'aar' name 'XDroidMvp' url rootProject.website licenses { diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/XDroidMvpUtill.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/XDroidMvpUtill.java index 84f6534..04ec8aa 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/XDroidMvpUtill.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/XDroidMvpUtill.java @@ -4,11 +4,53 @@ import android.content.Context; import android.os.Vibrator; +import androidx.viewbinding.ViewBinding; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import cn.droidlover.xdroidmvp.mvp.XActivity; +import cn.droidlover.xdroidmvp.mvp.XFragment; +import cn.droidlover.xdroidmvp.mvp.XPresentation; + public class XDroidMvpUtill { public static void vibrator(Context context) { -//获取系统震动服务 + //获取系统震动服务 Vibrator vib = (Vibrator) context.getSystemService(Service.VIBRATOR_SERVICE); -//震动70毫秒 + //震动70毫秒 vib.vibrate(70); } + + public static Class getViewBindingClass(Class _class) { + if (_class == null) { + return null; + } + if (_class == XActivity.class || _class == XFragment.class || _class == XPresentation.class || _class == Object.class) { + return null; + } + Type genType = _class.getGenericSuperclass(); + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + if (params.length > 0) { + if (params.length == 1) { + return getViewBindingClass(_class.getSuperclass()); + } else { + for (int i = 1; i < params.length; i++) { + Class c = (Class) params[i]; + if (c == ViewBinding.class) { + return null; + } + if (ViewBinding.class.isAssignableFrom(c)){ + return (Class)c; + } +// if (cs == ViewBinding.class) { +// return (Class) c; +// } + } + } + } else { + return getViewBindingClass(_class.getSuperclass()); + } + return null; + } + } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleViewBindingAdapter.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleViewBindingAdapter.java new file mode 100644 index 0000000..2ffc413 --- /dev/null +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleViewBindingAdapter.java @@ -0,0 +1,74 @@ +package cn.droidlover.xdroidmvp.base; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.recyclerview.widget.RecyclerView; +import androidx.viewbinding.ViewBinding; + +import cn.droidlover.xdroidmvp.XDroidMvpUtill; +import cn.droidlover.xrecyclerview.RecyclerAdapter; + +public abstract class SimpleViewBindingAdapter extends RecyclerAdapter> { + public static final int TAG_VIEW = 0; + + public SimpleViewBindingAdapter(Context context) { + super(context); + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + F v; + + public ViewHolder(F viewBinding) { + super(viewBinding.getRoot()); + v = viewBinding; + } + + public F getV() { + return v; + } + } + + @Override + public void onBindViewHolder(final ViewHolder holder, final int position) { + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (getRecItemClick() != null) { + T d = null; + if (position < data.size() && position >= 0) { + d = data.get(position); + } + getRecItemClick().onItemClick(position, d, TAG_VIEW, holder); + } + } + }); + holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + XDroidMvpUtill.vibrator(context); + if (getRecItemClick() != null) { + T d = null; + if (position < data.size() && position >= 0) { + d = data.get(position); + } + getRecItemClick().onItemLongClick(position, d, TAG_VIEW, holder); + } + return true; + } + }); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return new ViewHolder(getViewBinding(viewType, LayoutInflater.from(context), parent)); + } + + + /* + * 获取 ViewBinding 实现类 + * */ + protected abstract E getViewBinding(int viewType, LayoutInflater from, ViewGroup parent); +} diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java index dd1fe00..b8ed80f 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java @@ -9,7 +9,7 @@ * Created by wanglei on 2016/12/29. */ -public interface IView

{ +public interface IView

{ void bindEvent(); @@ -17,14 +17,10 @@ public interface IView

{ int getOptionsMenuId(); - E getViewBinding(); - boolean useEventBus(); P newP(); - Class getViewBindingClass(); - void showProgressDialog(String msg); void closeProgressDialog(); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java index 0ca9305..729aef3 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java @@ -12,6 +12,7 @@ import com.tbruyelle.rxpermissions2.RxPermissions; import cn.droidlover.xdroidmvp.XDroidConf; +import cn.droidlover.xdroidmvp.XDroidMvpUtill; import cn.droidlover.xdroidmvp.event.BusProvider; import com.trello.rxlifecycle3.components.support.RxAppCompatActivity; @@ -24,7 +25,7 @@ * Created by wanglei on 2016/12/29. */ -public abstract class XActivity

extends RxAppCompatActivity implements IView { +public abstract class XActivity

extends RxAppCompatActivity implements IView

{ private VDelegate vDelegate; private P p; @@ -39,8 +40,7 @@ public void onRefresh(Boolean bRefresh) { } - @Override - public E getViewBinding() { + protected final E getViewBinding() { return viewBinding; } @@ -51,8 +51,10 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { getP(); try { Class eClass = getViewBindingClass(); - Method method2 = eClass.getMethod("inflate", LayoutInflater.class); - viewBinding = (E) method2.invoke(null, getLayoutInflater()); + if (eClass != null) { + Method method2 = eClass.getMethod("inflate", LayoutInflater.class); + viewBinding = (E) method2.invoke(null, getLayoutInflater()); + } } catch (Exception e) { e.printStackTrace(); } @@ -64,6 +66,10 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { } + protected Class getViewBindingClass() { + return XDroidMvpUtill.getViewBindingClass(getClass()); + } + protected VDelegate getvDelegate() { if (vDelegate == null) { vDelegate = VDelegateBase.create(context); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java index 8cbad45..e79a807 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java @@ -14,6 +14,7 @@ import com.tbruyelle.rxpermissions2.RxPermissions; import cn.droidlover.xdroidmvp.XDroidConf; +import cn.droidlover.xdroidmvp.XDroidMvpUtill; import cn.droidlover.xdroidmvp.event.BusProvider; import com.trello.rxlifecycle3.components.support.RxFragment; @@ -26,7 +27,7 @@ * Created by wanglei on 2016/12/29. */ -public abstract class XFragment

extends RxFragment implements IView { +public abstract class XFragment

extends RxFragment implements IView

{ private VDelegate vDelegate; private P p; @@ -41,8 +42,7 @@ public void onRefresh(Boolean bRefresh) { } - @Override - public E getViewBinding() { + protected final E getViewBinding() { return viewBinding; } @@ -52,8 +52,10 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa layoutInflater = inflater; try { Class eClass = getViewBindingClass(); - Method method2 = eClass.getMethod("inflate", LayoutInflater.class, ViewGroup.class, Boolean.class); - viewBinding = (E) method2.invoke(null, inflater, container, false); + if (eClass != null) { + Method method = eClass.getMethod("inflate", LayoutInflater.class, ViewGroup.class, Boolean.TYPE); + viewBinding = (E) method.invoke(null, inflater, container, false); + } } catch (Exception e) { e.printStackTrace(); } @@ -66,12 +68,15 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa return viewBinding.getRoot(); } + private Class getViewBindingClass() { + return XDroidMvpUtill.getViewBindingClass(getClass()); + } + @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); getP(); - if (useEventBus()) { BusProvider.getBus().register(this); } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java index 45ae7e1..df81c60 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java @@ -15,9 +15,10 @@ import java.lang.reflect.Type; import cn.droidlover.xdroidmvp.XDroidConf; +import cn.droidlover.xdroidmvp.XDroidMvpUtill; import cn.droidlover.xdroidmvp.event.BusProvider; -public abstract class XPresentation

extends Presentation implements IView { +public abstract class XPresentation

extends Presentation implements IView

{ private P p; private VDelegate vDelegate; protected Context context; @@ -45,8 +46,7 @@ public void onRefresh(Boolean bRefresh) { } - @Override - public E getViewBinding() { + protected final E getViewBinding() { return viewBinding; } @@ -68,6 +68,10 @@ protected void onCreate(Bundle savedInstanceState) { initData(savedInstanceState); } + private Class getViewBindingClass() { + return XDroidMvpUtill.getViewBindingClass(getClass()); + } + protected VDelegate getvDelegate() { if (vDelegate == null) { vDelegate = VDelegateBase.create(context); diff --git a/nativelogger/build.gradle b/nativelogger/build.gradle index 507568a..6d940c5 100644 --- a/nativelogger/build.gradle +++ b/nativelogger/build.gradle @@ -58,7 +58,7 @@ install { // This generates POM.xml with proper parameters pom { project { - packaging 'jar' + packaging 'aar' name 'NativeLogger' url rootProject.website licenses { diff --git a/qrutill/build.gradle b/qrutill/build.gradle index 675a74f..5b26841 100644 --- a/qrutill/build.gradle +++ b/qrutill/build.gradle @@ -3,7 +3,7 @@ apply plugin: 'kotlin-android' apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'com.jfrog.bintray' -version = '1.0.3' // 版本 +version = '1.0.4' // 版本 @@ -72,7 +72,7 @@ install { // This generates POM.xml with proper parameters pom { project { - packaging 'jar' + packaging 'aar' name 'LennonQRUtill' url rootProject.website licenses { diff --git a/settings.gradle b/settings.gradle index 24f5f4e..84d3c44 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,7 +1,5 @@ -//include ':mylibrary' -//include ':app' +include ':http' include':mvp' -//include':test' include':xrecycler' include':utill' include':nativelogger' diff --git a/utill/build.gradle b/utill/build.gradle index e48712b..f1ccf36 100644 --- a/utill/build.gradle +++ b/utill/build.gradle @@ -6,7 +6,7 @@ apply plugin: 'kotlin-parcelize' apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'com.jfrog.bintray' -version = '1.6.21' // 版本 +version = '1.6.30' // 版本 android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -62,7 +62,7 @@ install { repositories.mavenInstaller { pom { project { - packaging 'jar' + packaging 'aar' name 'LennonUtill' url rootProject.website licenses { diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt index 959b882..0be6328 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt @@ -19,10 +19,8 @@ import com.lennon.cn.utill.dialog.CommonAlertDialog import com.lennon.cn.utill.dialog.CustomProgressDialog import com.lennon.cn.utill.dialog.OnAlertDialogListener -abstract class BaseFragment

,E:ViewBinding> : XFragment(), BaseView { - private var isFragmentVisible: Boolean = false - private var isReuseView: Boolean = false - private var isFirstVisible: Boolean = true +abstract class BaseFragment

, E : ViewBinding> : XFragment(), + BaseView { private var rootView: View? = null private var dialog: CustomProgressDialog? = null @@ -34,20 +32,6 @@ abstract class BaseFragment

,E:ViewBinding> : XFragment() return rootView!!.findViewById(i) } - private var isFirst = true - private var stop = false - - private fun initVariable() { - isFirstVisible = true - isFragmentVisible = false - rootView = null - isReuseView = true - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - userVisibleHint = userVisibleHint - } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -58,58 +42,6 @@ abstract class BaseFragment

,E:ViewBinding> : XFragment() return rootView } - /** - * 去除setUserVisibleHint()多余的回调场景,保证只有当fragment可见状态发生变化时才回调 - * 回调时机在view创建完后,所以支持ui操作,解决在setUserVisibleHint()里进行ui操作有可能报null异常的问题 - * - * 可在该回调方法里进行一些ui显示与隐藏 - * - * @param isVisible true 不可见 -> 可见 - * false 可见 -> 不可见 - */ - private fun onFragmentVisibleChange(isVisible: Boolean) { - if (isVisible) { - if (!isFragmentVisible) { - onVisible() - } - } else { - if (isFragmentVisible) { - onInvisible() - } - } - } - - override fun setUserVisibleHint(isVisibleToUser: Boolean) { - super.setUserVisibleHint(isVisibleToUser) - XLog.e(getName() + " setUserVisibleHint isVisibleToUser=${isVisibleToUser} isFirstVisible=${isFirstVisible} isFragmentVisible=${isFragmentVisible}") - if (rootView == null) { - return - } - if (isFirstVisible && isVisibleToUser) { - onFragmentFirstVisible() - isFirstVisible = false - isFragmentVisible = true - } else if (isVisibleToUser) { - onFragmentVisibleChange(true) - isFragmentVisible = true - return - } else if (isFragmentVisible) { - isFragmentVisible = false - onFragmentVisibleChange(false) - } - } - - /** - * 在fragment首次可见时回调,可用于加载数据,防止每次进入都重复加载数据 - */ - private fun onFragmentFirstVisible() { - onVisible() - } - - protected fun isFragmentVisible(): Boolean { - return isFragmentVisible - } - /** * 可见时的回调方法 */ @@ -136,6 +68,7 @@ abstract class BaseFragment

,E:ViewBinding> : XFragment() override fun onResume() { super.onResume() XLog.e(getName() + " onResume") + onVisible() } @@ -165,7 +98,7 @@ abstract class BaseFragment

,E:ViewBinding> : XFragment() } }) dialog.show() - Handler().postDelayed(Runnable { + Handler().postDelayed({ dialog.dismiss() runnable.run() }, second * 1000L) @@ -185,7 +118,7 @@ abstract class BaseFragment

,E:ViewBinding> : XFragment() runnable.run() } }) - Handler().postDelayed(Runnable { + Handler().postDelayed({ dialog.dismiss() runnable.run() }, 2000) @@ -226,7 +159,6 @@ abstract class BaseFragment

,E:ViewBinding> : XFragment() override fun onStop() { XLog.e(getName() + " onStop") - stop = true super.onStop() } @@ -239,6 +171,7 @@ abstract class BaseFragment

,E:ViewBinding> : XFragment() override fun onPause() { XLog.e(getName() + " onPause") super.onPause() + onInvisible() } override fun getContext(): Context? { diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt index 032685c..fa0a698 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt @@ -7,7 +7,7 @@ import cn.droidlover.xdroidmvp.mvp.IView import cn.droidlover.xdroidmvp.net.NetError import com.lennon.cn.utill.bean.ToastRunnable -interface BaseView

?, E : ViewBinding> : IView { +interface BaseView

?, E : ViewBinding> : IView

{ fun toast(msg: String, second: Int) fun toast(msg: String) diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/Utill.kt b/utill/src/main/java/com/lennon/cn/utill/utill/Utill.kt index a24eaaf..dbd5f54 100644 --- a/utill/src/main/java/com/lennon/cn/utill/utill/Utill.kt +++ b/utill/src/main/java/com/lennon/cn/utill/utill/Utill.kt @@ -1,5 +1,6 @@ package com.lennon.cn.utill.utill +import android.annotation.SuppressLint import android.app.ActivityManager import android.content.Context import android.content.Intent @@ -19,13 +20,11 @@ import java.util.ArrayList import com.lennon.cn.utill.conf.Lennon import android.provider.MediaStore import android.content.ContentValues -import android.R.layout import android.graphics.Bitmap import android.graphics.Canvas -import android.graphics.Color -import android.opengl.ETC1.getHeight -import android.opengl.ETC1.getWidth import android.view.View +import kotlin.math.pow +import kotlin.math.sqrt object Utill { @@ -43,13 +42,11 @@ object Utill { * view转bitmap */ fun viewConversionBitmap(v: View): Bitmap { - val w = v.getWidth() - val h = v.getHeight() + val w = v.width + val h = v.height val bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888) val c = Canvas(bmp) - -// c.drawColor(Color.WHITE) /** 如果不设置canvas画布为白色,则生成透明 */ v.layout(0, 0, w, h) @@ -57,6 +54,7 @@ object Utill { return bmp } + fun getImageContentUri(context: Context, imageFile: File): Uri? { val filePath = imageFile.absolutePath val cursor = context.contentResolver.query( @@ -69,15 +67,15 @@ object Utill { val baseUri = Uri.parse("content://media/external/images/media") return Uri.withAppendedPath(baseUri, "" + id) } else { - if (imageFile.exists()) { + return if (imageFile.exists()) { val values = ContentValues() values.put(MediaStore.Images.Media.DATA, filePath) - return context.contentResolver.insert( + context.contentResolver.insert( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values ) } else { - return null + null } } } @@ -93,7 +91,7 @@ object Utill { //任务管理类 val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager //遍历所有应用 - val infos = manager.getRunningAppProcesses() + val infos = manager.runningAppProcesses for (info in infos) { if (info.pid == pid)//得到当前应用 return info.processName//返回包名 @@ -224,16 +222,12 @@ object Utill { fun isPad(context: Context): Boolean { val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager val display = wm.defaultDisplay - // 屏幕宽度 - val screenWidth = display.width.toFloat() - // 屏幕高度 - val screenHeight = display.height.toFloat() val dm = DisplayMetrics() display.getMetrics(dm) - val x = Math.pow((dm.widthPixels / dm.xdpi).toDouble(), 2.0) - val y = Math.pow((dm.heightPixels / dm.ydpi).toDouble(), 2.0) + val x = (dm.widthPixels / dm.xdpi).toDouble().pow(2.0) + val y = (dm.heightPixels / dm.ydpi).toDouble().pow(2.0) // 屏幕尺寸 - val screenInches = Math.sqrt(x + y) + val screenInches = sqrt(x + y) // 大于6尺寸则为Pad return screenInches >= 8.0 } diff --git a/xrecycler/build.gradle b/xrecycler/build.gradle index 8c7d85b..5502837 100644 --- a/xrecycler/build.gradle +++ b/xrecycler/build.gradle @@ -2,8 +2,7 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'com.jfrog.bintray' -version = '1.0.5' // 版本 - +version = '1.0.7' // 版本 android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -68,7 +67,7 @@ install { // This generates POM.xml with proper parameters pom { project { - packaging 'jar' + packaging 'aar' name 'XRecycler' url rootProject.website licenses { From df596891d493120a3f004f1c88fd6f957e3015ab Mon Sep 17 00:00:00 2001 From: Edit <1136160757@qq.com> Date: Fri, 4 Jun 2021 10:47:59 +0800 Subject: [PATCH 20/24] =?UTF-8?q?=E4=BF=AE=E6=94=B9gradle=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0maven=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 28 +---- conf.gradle | 3 +- http/.gitignore | 1 - http/build.gradle | 118 ------------------ http/consumer-rules.pro | 0 http/proguard-rules.pro | 21 ---- .../utill/http/ExampleInstrumentedTest.kt | 24 ---- http/src/main/AndroidManifest.xml | 5 - .../com/lennon/utill/http/ExampleUnitTest.kt | 17 --- mvp/build.gradle | 69 ++-------- mvp/uploadAliRelease.gradle | 27 ++++ mvp/uploadAliSnapshot.gradle | 27 ++++ mvp/uploadLocal.gradle | 26 ++++ nativelogger/build.gradle | 57 +-------- nativelogger/uploadAliRelease.gradle | 27 ++++ nativelogger/uploadAliSnapshot.gradle | 27 ++++ nativelogger/uploadLocal.gradle | 26 ++++ qrutill/build.gradle | 75 +---------- qrutill/uploadAliRelease.gradle | 27 ++++ qrutill/uploadAliSnapshot.gradle | 27 ++++ qrutill/uploadLocal.gradle | 26 ++++ settings.gradle | 7 +- utill/build.gradle | 63 +--------- utill/uploadAliRelease.gradle | 27 ++++ utill/uploadAliSnapshot.gradle | 27 ++++ utill/uploadLocal.gradle | 26 ++++ xrecycler/build.gradle | 58 +-------- xrecycler/uploadAliRelease.gradle | 27 ++++ xrecycler/uploadAliSnapshot.gradle | 27 ++++ xrecycler/uploadLocal.gradle | 26 ++++ 30 files changed, 433 insertions(+), 513 deletions(-) delete mode 100644 http/.gitignore delete mode 100644 http/build.gradle delete mode 100644 http/consumer-rules.pro delete mode 100644 http/proguard-rules.pro delete mode 100644 http/src/androidTest/java/com/lennon/utill/http/ExampleInstrumentedTest.kt delete mode 100644 http/src/main/AndroidManifest.xml delete mode 100644 http/src/test/java/com/lennon/utill/http/ExampleUnitTest.kt create mode 100644 mvp/uploadAliRelease.gradle create mode 100644 mvp/uploadAliSnapshot.gradle create mode 100644 mvp/uploadLocal.gradle create mode 100644 nativelogger/uploadAliRelease.gradle create mode 100644 nativelogger/uploadAliSnapshot.gradle create mode 100644 nativelogger/uploadLocal.gradle create mode 100644 qrutill/uploadAliRelease.gradle create mode 100644 qrutill/uploadAliSnapshot.gradle create mode 100644 qrutill/uploadLocal.gradle create mode 100644 utill/uploadAliRelease.gradle create mode 100644 utill/uploadAliSnapshot.gradle create mode 100644 utill/uploadLocal.gradle create mode 100644 xrecycler/uploadAliRelease.gradle create mode 100644 xrecycler/uploadAliSnapshot.gradle create mode 100644 xrecycler/uploadLocal.gradle diff --git a/build.gradle b/build.gradle index 6f9b822..407237b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,45 +1,19 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. apply from: "conf.gradle" buildscript { - ext.kotlin_version = '1.4.21' + ext.kotlin_version = '1.5.10' repositories { - maven {url 'http://127.0.0.1:8081/repository/lennon/' } -// jcenter() { url 'https://maven.aliyun.com/repository/public' } -// maven {url 'https://dl.bintray.com/lennon-xia/lennon/'} -// jcenter() - -// maven { -// url 'https://maven.google.com/' -// name 'Google' -// } } dependencies { - //noinspection GradleDependency classpath 'com.android.tools.build:gradle:4.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' - classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' -// classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' -// classpath 'com.novoda:bintray-release:0.9.2' } } allprojects { repositories { - -// jcenter() { url 'https://maven.aliyun.com/repository/public' } maven {url 'http://127.0.0.1:8081/repository/lennon/' } -// maven {url 'https://dl.bintray.com/lennon-xia/lennon/'} -// mavenCentral() - -// maven { -// url 'https://maven.google.com/' -// name 'Google' -// } -// jcenter() -// maven { url "https://jitpack.io" } } tasks.withType(Javadoc).all { enabled = false diff --git a/conf.gradle b/conf.gradle index a2b4a00..221731c 100644 --- a/conf.gradle +++ b/conf.gradle @@ -44,6 +44,7 @@ ext { "annotations" : "androidx.annotation:annotation:1.1.0", "constraintlayout" : "androidx.constraintlayout:constraintlayout:1.1.3", + "arch-lifecycle" : "android.arch.lifecycle:extensions:+", "glide" : "com.github.bumptech.glide:glide:4.11.0", "glide-compiler" : "com.github.bumptech.glide:compiler:4.11.0", @@ -83,7 +84,7 @@ ext { "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.7", - "xdroid-mvp" : "com.lennon.utill:mvp:1.3.23", + "xdroid-mvp" : "com.lennon.utill:mvp:1.3.24", "avi-loading" : "com.wang.avi:library:+", "rxbus" : "com.blankj:rxbus:+", diff --git a/http/.gitignore b/http/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/http/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/http/build.gradle b/http/build.gradle deleted file mode 100644 index f36c28c..0000000 --- a/http/build.gradle +++ /dev/null @@ -1,118 +0,0 @@ -plugins { - id 'com.android.library' - id 'kotlin-android' - id 'com.github.dcendents.android-maven' - id 'com.jfrog.bintray' -} -version = "0.0.1" -android { - - compileSdkVersion rootProject.ext.android.compileSdkVersion - - defaultConfig { - minSdkVersion rootProject.ext.android.minSdkVersion - targetSdkVersion rootProject.ext.android.targetSdkVersion - versionCode rootProject.ext.android.versionCode - versionName rootProject.ext.android.versionName - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles "consumer-rules.pro" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } -} - -dependencies { - api rootProject.ext.dependencies["kotlin"] - api rootProject.ext.dependencies["retrofit"] - api rootProject.ext.dependencies["retrofit-converter-gson"] - api rootProject.ext.dependencies["okhttp3"] - testImplementation 'junit:junit:4.13.1' - androidTestImplementation 'androidx.test.ext:junit:1.1.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' -} -// 生成sourceJar和javaDocJar构件 -task sourcesJar(type: Jar) { - from android.sourceSets.main.java.srcDirs - classifier = 'sources' -} - -task javadoc(type: Javadoc) { - failOnError false - source = android.sourceSets.main.java.sourceFiles - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - classpath += configurations.compile -} -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} -artifacts { - archives sourcesJar - archives javadocJar -} - -group = rootProject.groupId - -install { - repositories.mavenInstaller { - // This generates POM.xml with proper parameters - pom { - project { - packaging 'aar' - name 'LennonHttpUtill' - url rootProject.website - licenses { - license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' - } - } - developers { - developer { - id 'lennon-http-utill' - name rootProject.groupId - email rootProject.email - } - } - scm { - connection rootProject.gitUrl - developerConnection rootProject.gitUrl - url rootProject.siteUrl - } - } - } - } -} - -Properties properties = new Properties() -boolean isHasFile = false -if (project.rootProject.file('local.properties') != null) { - isHasFile = true - properties.load(project.rootProject.file('local.properties').newDataInputStream()) -} -bintray { - user = isHasFile ? properties.getProperty("bintray.user") : System.getenv("bintray.user") - key = isHasFile ? properties.getProperty("bintray.apikey") : System.getenv("bintray.apikey") - configurations = ['archives'] - pkg { - repo = rootProject.repoName - name = "LennonHttpUtill" - websiteUrl = siteUrl - vcsUrl = gitUrl - licenses = ["Apache-2.0"] - publish = true - } -} \ No newline at end of file diff --git a/http/consumer-rules.pro b/http/consumer-rules.pro deleted file mode 100644 index e69de29..0000000 diff --git a/http/proguard-rules.pro b/http/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/http/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/http/src/androidTest/java/com/lennon/utill/http/ExampleInstrumentedTest.kt b/http/src/androidTest/java/com/lennon/utill/http/ExampleInstrumentedTest.kt deleted file mode 100644 index 9da237b..0000000 --- a/http/src/androidTest/java/com/lennon/utill/http/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.lennon.utill.http - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.lennon.utill.http.test", appContext.packageName) - } -} \ No newline at end of file diff --git a/http/src/main/AndroidManifest.xml b/http/src/main/AndroidManifest.xml deleted file mode 100644 index f4115ae..0000000 --- a/http/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/http/src/test/java/com/lennon/utill/http/ExampleUnitTest.kt b/http/src/test/java/com/lennon/utill/http/ExampleUnitTest.kt deleted file mode 100644 index 6501c71..0000000 --- a/http/src/test/java/com/lennon/utill/http/ExampleUnitTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.lennon.utill.http - -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/mvp/build.gradle b/mvp/build.gradle index 39a4133..2031dd4 100644 --- a/mvp/build.gradle +++ b/mvp/build.gradle @@ -2,9 +2,9 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' -apply plugin: 'com.github.dcendents.android-maven' -apply plugin: 'com.jfrog.bintray' -version = '1.3.23' + +version = '1.3.25' +group = rootProject.ext.groupId android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -25,6 +25,11 @@ android { } } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + } dependencies { api fileTree(dir: 'libs', include: ['*.jar']) @@ -50,10 +55,6 @@ dependencies { api rootProject.ext.dependencies["rxbus"] -// api rootProject.ext.dependencies["eventBus"] -// annotationProcessor rootProject.ext.dependencies["eventbus-annotation-processor"] -// kapt rootProject.ext.dependencies["eventbus-annotation-processor"] - api rootProject.ext.dependencies["rxlifecycle"] api rootProject.ext.dependencies["rxlifecycle-android"] api rootProject.ext.dependencies["rxlifecycle-components"] @@ -64,6 +65,7 @@ dependencies { api rootProject.ext.dependencies["nativelogger"] api rootProject.ext.dependencies["viewpager2"] + } tasks.withType(JavaCompile) { @@ -90,54 +92,7 @@ artifacts { archives javadocJar archives sourcesJar } -group = rootProject.groupId - -install { - repositories.mavenInstaller { - pom { - project { - packaging 'aar' - name 'XDroidMvp' - url rootProject.website - licenses { - license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' - } - } - developers { - developer { - id 'xdroid-mvp' - name rootProject.groupId - email rootProject.email - } - } - scm { - connection rootProject.gitUrl - developerConnection rootProject.gitUrl - url rootProject.siteUrl - } - } - } - } -} -Properties properties = new Properties() -boolean isHasFile = false -if (project.rootProject.file('local.properties') != null){ - isHasFile = true - properties.load(project.rootProject.file('local.properties').newDataInputStream()) -} -bintray { - user = isHasFile ? properties.getProperty("bintray.user") : System.getenv("bintray.user") - key = isHasFile ? properties.getProperty("bintray.apikey") : System.getenv("bintray.apikey") - configurations = ['archives'] - pkg { - repo = rootProject.repoName - name = "XDroidMvp" - websiteUrl = siteUrl - vcsUrl = gitUrl - licenses = ["Apache-2.0"] - publish = true - } -} \ No newline at end of file +//apply from:'uploadLocal.gradle' +//apply from:'uploadAliRelease.gradle' +apply from:'uploadAliSnapshot.gradle' \ No newline at end of file diff --git a/mvp/uploadAliRelease.gradle b/mvp/uploadAliRelease.gradle new file mode 100644 index 0000000..9f68b7b --- /dev/null +++ b/mvp/uploadAliRelease.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'mvp' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-release-TFq04W/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/mvp/uploadAliSnapshot.gradle b/mvp/uploadAliSnapshot.gradle new file mode 100644 index 0000000..e93988c --- /dev/null +++ b/mvp/uploadAliSnapshot.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'mvp' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-snapshot-fEzFv7/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/mvp/uploadLocal.gradle b/mvp/uploadLocal.gradle new file mode 100644 index 0000000..ce193a1 --- /dev/null +++ b/mvp/uploadLocal.gradle @@ -0,0 +1,26 @@ +apply plugin: 'maven' + +def artifactId = 'mvp' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def userName = isHasFile ? properties.getProperty("userName") : System.getenv("userName") +def password = isHasFile ? properties.getProperty("password") : System.getenv("password") +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'http://127.0.0.1:8081/repository/maven-releases/') { + authentication( + userName: userName, + password: password + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/nativelogger/build.gradle b/nativelogger/build.gradle index 6d940c5..299e7a8 100644 --- a/nativelogger/build.gradle +++ b/nativelogger/build.gradle @@ -1,7 +1,5 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' -apply plugin: 'com.github.dcendents.android-maven' -apply plugin: 'com.jfrog.bintray' android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -50,56 +48,9 @@ artifacts { archives sourcesJar archives javadocJar } -version = '1.1' +version = '1.2' group = rootProject.groupId -install { - repositories.mavenInstaller { - // This generates POM.xml with proper parameters - pom { - project { - packaging 'aar' - name 'NativeLogger' - url rootProject.website - licenses { - license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' - } - } - developers { - developer { - id 'nativelogger' - name rootProject.groupId - email rootProject.email - } - } - scm { - connection rootProject.gitUrl - developerConnection rootProject.gitUrl - url rootProject.siteUrl - } - } - } - } -} - -Properties properties = new Properties() -boolean isHasFile = false -if (project.rootProject.file('local.properties') != null){ - isHasFile = true - properties.load(project.rootProject.file('local.properties').newDataInputStream()) -} -bintray { - user = isHasFile ? properties.getProperty("bintray.user") : System.getenv("bintray.user") - key = isHasFile ? properties.getProperty("bintray.apikey") : System.getenv("bintray.apikey") - configurations = ['archives'] - pkg { - repo = rootProject.repoName - name = "NativeLogger" - websiteUrl = siteUrl - vcsUrl = gitUrl - licenses = ["Apache-2.0"] - publish = true - } -} \ No newline at end of file +apply from:'uploadLocal.gradle' +//apply from:'uploadAliRelease.gradle' +//apply from:'uploadAliSnapshot.gradle' \ No newline at end of file diff --git a/nativelogger/uploadAliRelease.gradle b/nativelogger/uploadAliRelease.gradle new file mode 100644 index 0000000..14e70cd --- /dev/null +++ b/nativelogger/uploadAliRelease.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'nativelogger' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-release-TFq04W/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/nativelogger/uploadAliSnapshot.gradle b/nativelogger/uploadAliSnapshot.gradle new file mode 100644 index 0000000..cbf5026 --- /dev/null +++ b/nativelogger/uploadAliSnapshot.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'nativelogger' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-snapshot-fEzFv7/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/nativelogger/uploadLocal.gradle b/nativelogger/uploadLocal.gradle new file mode 100644 index 0000000..285ca29 --- /dev/null +++ b/nativelogger/uploadLocal.gradle @@ -0,0 +1,26 @@ +apply plugin: 'maven' + +def artifactId = 'nativelogger' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def userName = isHasFile ? properties.getProperty("userName") : System.getenv("userName") +def password = isHasFile ? properties.getProperty("password") : System.getenv("password") +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'http://127.0.0.1:8081/repository/maven-releases/') { + authentication( + userName: userName, + password: password + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/qrutill/build.gradle b/qrutill/build.gradle index 5b26841..d15e1c2 100644 --- a/qrutill/build.gradle +++ b/qrutill/build.gradle @@ -1,12 +1,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' -apply plugin: 'com.github.dcendents.android-maven' -apply plugin: 'com.jfrog.bintray' - version = '1.0.4' // 版本 - - - +group = rootProject.groupId android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -52,68 +47,6 @@ artifacts { archives sourcesJar archives javadocJar } -//apply plugin: 'com.novoda.bintray-release' -//publish { -// artifactId = 'lennon-qr-utill' // artifactId -// uploadName = 'LennonQRUtill' // 展示名字 -// publishVersion = '1.0.3' // 版本 -// userOrg = rootProject.userOrg -// groupId = rootProject.groupId -// repoName = rootProject.repoName -// desc = '' -// website = rootProject.website -// licences = rootProject.licences -//} - -group = rootProject.groupId - -install { - repositories.mavenInstaller { - // This generates POM.xml with proper parameters - pom { - project { - packaging 'aar' - name 'LennonQRUtill' - url rootProject.website - licenses { - license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' - } - } - developers { - developer { - id 'lennon-qr-utill' - name rootProject.groupId - email rootProject.email - } - } - scm { - connection rootProject.gitUrl - developerConnection rootProject.gitUrl - url rootProject.siteUrl - } - } - } - } -} - -Properties properties = new Properties() -boolean isHasFile = false -if (project.rootProject.file('local.properties') != null){ - isHasFile = true - properties.load(project.rootProject.file('local.properties').newDataInputStream()) -} -bintray { - user = isHasFile ? properties.getProperty("bintray.user") : System.getenv("bintray.user") - key = isHasFile ? properties.getProperty("bintray.apikey") : System.getenv("bintray.apikey") - configurations = ['archives'] - pkg { - repo = rootProject.repoName - name = "LennonQRUtill" - websiteUrl = siteUrl - vcsUrl = gitUrl - licenses = ["Apache-2.0"] - publish = true - } -} \ No newline at end of file +apply from:'uploadLocal.gradle' +//apply from:'uploadAliRelease.gradle' +//apply from:'uploadAliSnapshot.gradle' \ No newline at end of file diff --git a/qrutill/uploadAliRelease.gradle b/qrutill/uploadAliRelease.gradle new file mode 100644 index 0000000..5bea87b --- /dev/null +++ b/qrutill/uploadAliRelease.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'lennon-qr-utill' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-release-TFq04W/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/qrutill/uploadAliSnapshot.gradle b/qrutill/uploadAliSnapshot.gradle new file mode 100644 index 0000000..5cd5ee7 --- /dev/null +++ b/qrutill/uploadAliSnapshot.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'lennon-qr-utill' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-snapshot-fEzFv7/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/qrutill/uploadLocal.gradle b/qrutill/uploadLocal.gradle new file mode 100644 index 0000000..ffaca5f --- /dev/null +++ b/qrutill/uploadLocal.gradle @@ -0,0 +1,26 @@ +apply plugin: 'maven' + +def artifactId = 'lennon-qr-utill' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def userName = isHasFile ? properties.getProperty("userName") : System.getenv("userName") +def password = isHasFile ? properties.getProperty("password") : System.getenv("password") +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'http://127.0.0.1:8081/repository/maven-releases/') { + authentication( + userName: userName, + password: password + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 84d3c44..043ba28 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1 @@ -include ':http' -include':mvp' -include':xrecycler' -include':utill' -include':nativelogger' -include ':qrutill' +include ':mvp',':xrecycler',':utill',':nativelogger' ,':qrutill' diff --git a/utill/build.gradle b/utill/build.gradle index f1ccf36..dcf949d 100644 --- a/utill/build.gradle +++ b/utill/build.gradle @@ -2,12 +2,8 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-parcelize' - -apply plugin: 'com.github.dcendents.android-maven' -apply plugin: 'com.jfrog.bintray' - -version = '1.6.30' // 版本 - +version = '1.6.31' // 版本 +group = rootProject.groupId android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -55,55 +51,6 @@ artifacts { archives javadocJar archives sourcesJar } - -group = rootProject.groupId - -install { - repositories.mavenInstaller { - pom { - project { - packaging 'aar' - name 'LennonUtill' - url rootProject.website - licenses { - license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' - } - } - developers { - developer { - id 'lennon-utill' - name rootProject.groupId - email rootProject.email - } - } - scm { - connection rootProject.gitUrl - developerConnection rootProject.gitUrl - url rootProject.siteUrl - } - } - } - } -} - -Properties properties = new Properties() -boolean isHasFile = false -if (project.rootProject.file('local.properties') != null){ - isHasFile = true - properties.load(project.rootProject.file('local.properties').newDataInputStream()) -} -bintray { - user = isHasFile ? properties.getProperty("bintray.user") : System.getenv("bintray.user") - key = isHasFile ? properties.getProperty("bintray.apikey") : System.getenv("bintray.apikey") - configurations = ['archives'] - pkg { - repo = rootProject.repoName - name = "LennonUtill" - websiteUrl = siteUrl - vcsUrl = gitUrl - licenses = ["Apache-2.0"] - publish = true - } -} \ No newline at end of file +apply from:'uploadLocal.gradle' +//apply from:'uploadAliRelease.gradle' +//apply from:'uploadAliSnapshot.gradle' \ No newline at end of file diff --git a/utill/uploadAliRelease.gradle b/utill/uploadAliRelease.gradle new file mode 100644 index 0000000..b916e46 --- /dev/null +++ b/utill/uploadAliRelease.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'lennon-utill' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-release-TFq04W/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/utill/uploadAliSnapshot.gradle b/utill/uploadAliSnapshot.gradle new file mode 100644 index 0000000..72490f0 --- /dev/null +++ b/utill/uploadAliSnapshot.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'lennon-utill' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-snapshot-fEzFv7/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/utill/uploadLocal.gradle b/utill/uploadLocal.gradle new file mode 100644 index 0000000..e371e0d --- /dev/null +++ b/utill/uploadLocal.gradle @@ -0,0 +1,26 @@ +apply plugin: 'maven' + +def artifactId = 'lennon-utill' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def userName = isHasFile ? properties.getProperty("userName") : System.getenv("userName") +def password = isHasFile ? properties.getProperty("password") : System.getenv("password") +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'http://127.0.0.1:8081/repository/maven-releases/') { + authentication( + userName: userName, + password: password + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/xrecycler/build.gradle b/xrecycler/build.gradle index 5502837..8131bd2 100644 --- a/xrecycler/build.gradle +++ b/xrecycler/build.gradle @@ -1,7 +1,6 @@ apply plugin: 'com.android.library' -apply plugin: 'com.github.dcendents.android-maven' -apply plugin: 'com.jfrog.bintray' +group = rootProject.groupId version = '1.0.7' // 版本 android { @@ -60,55 +59,6 @@ artifacts { archives javadocJar } -group = rootProject.groupId - -install { - repositories.mavenInstaller { - // This generates POM.xml with proper parameters - pom { - project { - packaging 'aar' - name 'XRecycler' - url rootProject.website - licenses { - license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' - } - } - developers { - developer { - id 'xrecycler' - name rootProject.groupId - email rootProject.email - } - } - scm { - connection rootProject.gitUrl - developerConnection rootProject.gitUrl - url rootProject.siteUrl - } - } - } - } -} - -Properties properties = new Properties() -boolean isHasFile = false -if (project.rootProject.file('local.properties') != null){ - isHasFile = true - properties.load(project.rootProject.file('local.properties').newDataInputStream()) -} -bintray { - user = isHasFile ? properties.getProperty("bintray.user") : System.getenv("bintray.user") - key = isHasFile ? properties.getProperty("bintray.apikey") : System.getenv("bintray.apikey") - configurations = ['archives'] - pkg { - repo = rootProject.repoName - name = "XRecycler" - websiteUrl = siteUrl - vcsUrl = gitUrl - licenses = ["Apache-2.0"] - publish = true - } -} \ No newline at end of file +apply from:'uploadLocal.gradle' +//apply from:'uploadAliRelease.gradle' +//apply from:'uploadAliSnapshot.gradle' \ No newline at end of file diff --git a/xrecycler/uploadAliRelease.gradle b/xrecycler/uploadAliRelease.gradle new file mode 100644 index 0000000..fb178ed --- /dev/null +++ b/xrecycler/uploadAliRelease.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'xrecycler' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-release-TFq04W/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/xrecycler/uploadAliSnapshot.gradle b/xrecycler/uploadAliSnapshot.gradle new file mode 100644 index 0000000..6f2f686 --- /dev/null +++ b/xrecycler/uploadAliSnapshot.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'xrecycler' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-snapshot-fEzFv7/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/xrecycler/uploadLocal.gradle b/xrecycler/uploadLocal.gradle new file mode 100644 index 0000000..fd90855 --- /dev/null +++ b/xrecycler/uploadLocal.gradle @@ -0,0 +1,26 @@ +apply plugin: 'maven' + +def artifactId = 'xrecycler' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def userName = isHasFile ? properties.getProperty("userName") : System.getenv("userName") +def password = isHasFile ? properties.getProperty("password") : System.getenv("password") +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'http://127.0.0.1:8081/repository/maven-releases/') { + authentication( + userName: userName, + password: password + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file From 52dcb692605de3e4c9b91efdbad77537843f14da Mon Sep 17 00:00:00 2001 From: lennon <1136160757@qq.com> Date: Thu, 17 Jun 2021 15:22:25 +0800 Subject: [PATCH 21/24] Update Readme.md --- Readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Readme.md b/Readme.md index 7e970a2..26d1b10 100644 --- a/Readme.md +++ b/Readme.md @@ -240,4 +240,5 @@ dependencies { 若您在使用过程中遇到任何问题,欢迎加入 **153569290** 群或者是邮件反馈,谢谢您的关注。**XDroidMvp**会持续维护,如果喜欢,记得star fork。 +[![996.icu](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu) From 57f51916f39efe56a6f1f44cdf89f0048e7b4100 Mon Sep 17 00:00:00 2001 From: Edit <1136160757@qq.com> Date: Thu, 24 Jun 2021 09:30:44 +0800 Subject: [PATCH 22/24] =?UTF-8?q?=E4=BF=AE=E6=94=B9gradle=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0maven=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 -- mvp/build.gradle | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 407237b..8266870 100644 --- a/build.gradle +++ b/build.gradle @@ -18,8 +18,6 @@ allprojects { tasks.withType(Javadoc).all { enabled = false } - - } task clean(type: Delete) { diff --git a/mvp/build.gradle b/mvp/build.gradle index 2031dd4..5c600ca 100644 --- a/mvp/build.gradle +++ b/mvp/build.gradle @@ -93,6 +93,6 @@ artifacts { archives sourcesJar } -//apply from:'uploadLocal.gradle' -//apply from:'uploadAliRelease.gradle' -apply from:'uploadAliSnapshot.gradle' \ No newline at end of file +//apply from: 'uploadLocal.gradle' +//apply from: 'uploadAliRelease.gradle' +apply from: 'uploadAliSnapshot.gradle' \ No newline at end of file From 3c4d954267df89fdce6639898869c4eb61402432 Mon Sep 17 00:00:00 2001 From: lennon <1136160757@qq.com> Date: Fri, 25 Jun 2021 16:53:00 +0800 Subject: [PATCH 23/24] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=83=A8=E5=88=86SDK?= =?UTF-8?q?=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf.gradle | 91 +++++++++++++++++++++------------------------- mvp/build.gradle | 2 +- utill/build.gradle | 6 +-- 3 files changed, 46 insertions(+), 53 deletions(-) diff --git a/conf.gradle b/conf.gradle index 221731c..818e0ef 100644 --- a/conf.gradle +++ b/conf.gradle @@ -27,92 +27,85 @@ ext { versionCode : 1, versionName : '1.0.0', - VSupportSdk : '27.1.1', VRetrofitSdk : '2.9.0', - VOkhttp : '4.7.2', + VOkhttp : '4.9.1', VRxlifecycle : '3.1.0', - NavigationSDK : "2.2.2", + NavigationSDK : "2.3.5", ] dependencies = [ - "viewpager2" : "androidx.viewpager2:viewpager2:1.0.0", + "viewpager2" : "androidx.viewpager2:viewpager2:1.0.0", - "eventBus" : "org.greenrobot:eventbus:3.2.0", - "eventbus-annotation-processor": "org.greenrobot:eventbus-annotation-processor:3.2.0", + "annotations" : "androidx.annotation:annotation:1.2.0", - "annotations" : "androidx.annotation:annotation:1.1.0", + "constraintlayout" : "androidx.constraintlayout:constraintlayout:2.0.4", - "constraintlayout" : "androidx.constraintlayout:constraintlayout:1.1.3", - "arch-lifecycle" : "android.arch.lifecycle:extensions:+", + "glide" : "com.github.bumptech.glide:glide:4.12.0", + "glide-compiler" : "com.github.bumptech.glide:compiler:4.12.0", - "glide" : "com.github.bumptech.glide:glide:4.11.0", - "glide-compiler" : "com.github.bumptech.glide:compiler:4.11.0", + "zxing" : "com.google.zxing:core:3.4.1", - "zxing" : "com.google.zxing:core:3.4.0", + "navigation-fragment-ktx" : "androidx.navigation:navigation-fragment-ktx:${android["NavigationSDK"]}", + "navigation-ui-ktx" : "androidx.navigation:navigation-ui-ktx:${android["NavigationSDK"]}", - "navigation-fragment-ktx" : "androidx.navigation:navigation-fragment-ktx:${android["NavigationSDK"]}", - "navigation-ui-ktx" : "androidx.navigation:navigation-ui-ktx:${android["NavigationSDK"]}", + "navigation-ui" : "androidx.navigation:navigation-ui:${android["NavigationSDK"]}", + "navigation-fragment" : "androidx.navigation:navigation-fragment:${android["NavigationSDK"]}", - "navigation-ui" : "androidx.navigation:navigation-ui:${android["NavigationSDK"]}", - "navigation-fragment" : "androidx.navigation:navigation-fragment:${android["NavigationSDK"]}", + "exifinterface" : "androidx.exifinterface:exifinterface:1.3.2", - "exifinterface" : "androidx.exifinterface:exifinterface:1.2.0", + "multidex" : "androidx.multidex:multidex:2.0.1", - "multidex" : "androidx.multidex:multidex:2.0.1", + "kotlin" : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version", - "kotlin" : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version", + "PersistentCookieJar" : "com.github.franmontiel:PersistentCookieJar:1.0.1", - "PersistentCookieJar" : "com.github.franmontiel:PersistentCookieJar:1.0.1", + "qiujuer-ui" : "net.qiujuer.genius:ui:2.1.1", - "qiujuer-ui" : "net.qiujuer.genius:ui:2.1.1", + "appcompat-v7" : "androidx.appcompat:appcompat:1.2.0", - "appcompat-v7" : "androidx.appcompat:appcompat:1.1.0", + "support-v4" : "androidx.legacy:legacy-support-v4:1.0.0", - "support-v4" : "androidx.legacy:legacy-support-v4:1.0.0", + "design" : "com.google.android.material:material:1.2.1", - "design" : "com.google.android.material:material:1.0.0", + "recyclerview-v7" : "androidx.recyclerview:recyclerview:1.2.1", - "recyclerview-v7" : "androidx.recyclerview:recyclerview:1.0.0", + "picasso" : "com.squareup.picasso:picasso:2.8", - "eventbus" : "org.greenrobot:eventbus:+", + "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.7", - "picasso" : "com.squareup.picasso:picasso:+", + "xdroid-mvp" : "com.lennon.utill:mvp:1.3.27", - "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.7", + "avi-loading" : "com.wang.avi:library:2.1.3",//https://github.com/81813780/AVLoadingIndicatorView + "rxbus" : "com.blankj:rxbus:1.6", - "xdroid-mvp" : "com.lennon.utill:mvp:1.3.24", + "gson" : "com.google.code.gson:gson:2.8.7", + "rxandroid" : "io.reactivex.rxjava2:rxandroid:2.1.1", + "rxjava" : "io.reactivex.rxjava2:rxjava:2.2.21", - "avi-loading" : "com.wang.avi:library:+", - "rxbus" : "com.blankj:rxbus:+", + "retrofit" : "com.squareup.retrofit2:retrofit:${android["VRetrofitSdk"]}", + "retrofit-converter-gson" : "com.squareup.retrofit2:converter-gson:${android["VRetrofitSdk"]}", + "retrofit-adapter-rxjava" : "com.squareup.retrofit2:adapter-rxjava2:${android["VRetrofitSdk"]}", + "okhttp3-logging-interceptor": "com.squareup.okhttp3:logging-interceptor:${android["VOkhttp"]}", + "okhttp3" : "com.squareup.okhttp3:okhttp:${android["VOkhttp"]}", - "gson" : "com.google.code.gson:gson:+", - "rxandroid" : "io.reactivex.rxjava2:rxandroid:+", - "rxjava" : "io.reactivex.rxjava2:rxjava:+", + "rxlifecycle" : "com.trello.rxlifecycle3:rxlifecycle:${android["VRxlifecycle"]}", - "retrofit" : "com.squareup.retrofit2:retrofit:${android["VRetrofitSdk"]}", - "retrofit-converter-gson" : "com.squareup.retrofit2:converter-gson:${android["VRetrofitSdk"]}", - "retrofit-adapter-rxjava" : "com.squareup.retrofit2:adapter-rxjava2:${android["VRetrofitSdk"]}", - "okhttp3-logging-interceptor" : "com.squareup.okhttp3:logging-interceptor:${android["VOkhttp"]}", - "okhttp3" : "com.squareup.okhttp3:okhttp:${android["VOkhttp"]}", + "rxlifecycle-android" : "com.trello.rxlifecycle3:rxlifecycle-android:${android["VRxlifecycle"]}", - "rxlifecycle" : "com.trello.rxlifecycle3:rxlifecycle:${android["VRxlifecycle"]}", + "rxlifecycle-components" : "com.trello.rxlifecycle3:rxlifecycle-components:${android["VRxlifecycle"]}", - "rxlifecycle-android" : "com.trello.rxlifecycle3:rxlifecycle-android:${android["VRxlifecycle"]}", + "rxpermissions" : "com.tbruyelle.rxpermissions2:rxpermissions:0.9.5", - "rxlifecycle-components" : "com.trello.rxlifecycle3:rxlifecycle-components:${android["VRxlifecycle"]}", + "canary-debug" : "com.squareup.leakcanary:leakcanary-android:2.7", + "canary-release" : "com.squareup.leakcanary:leakcanary-android-no-op:1.6.3", - "rxpermissions" : "com.tbruyelle.rxpermissions2:rxpermissions:latest.integration", + "XStateController" : "com.github.limedroid:XStateController:v1.1.1", - "canary-debug" : "com.squareup.leakcanary:leakcanary-android:+", - "canary-release" : "com.squareup.leakcanary:leakcanary-android-no-op:+", - - "XStateController" : "com.github.limedroid:XStateController:v1.1.1", - - "nativelogger" : "com.lennon.utill:nativelogger:1.1" + "nativelogger" : "com.lennon.utill:nativelogger:1.1" ] } \ No newline at end of file diff --git a/mvp/build.gradle b/mvp/build.gradle index 5c600ca..933a688 100644 --- a/mvp/build.gradle +++ b/mvp/build.gradle @@ -3,7 +3,7 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' -version = '1.3.25' +version = '1.3.27' group = rootProject.ext.groupId android { diff --git a/utill/build.gradle b/utill/build.gradle index dcf949d..b7e5a06 100644 --- a/utill/build.gradle +++ b/utill/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-parcelize' -version = '1.6.31' // 版本 +version = '1.6.33' // 版本 group = rootProject.groupId android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -51,6 +51,6 @@ artifacts { archives javadocJar archives sourcesJar } -apply from:'uploadLocal.gradle' +//apply from:'uploadLocal.gradle' //apply from:'uploadAliRelease.gradle' -//apply from:'uploadAliSnapshot.gradle' \ No newline at end of file +apply from:'uploadAliSnapshot.gradle' \ No newline at end of file From 4e0b4da53b87c6afa0c4787357b75ac1edd76a22 Mon Sep 17 00:00:00 2001 From: lennon <1136160757@qq.com> Date: Wed, 30 Jun 2021 14:08:14 +0800 Subject: [PATCH 24/24] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf.gradle | 2 +- mvp/build.gradle | 2 +- .../droidlover/xdroidmvp/base/SimpleViewBindingAdapter.java | 3 ++- utill/build.gradle | 2 +- .../src/main/java/com/lennon/cn/utill/base/BaseActivity.kt | 3 ++- .../src/main/java/com/lennon/cn/utill/base/BaseFragment.kt | 6 +++++- .../main/java/com/lennon/cn/utill/base/BasePresentation.kt | 2 +- utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt | 3 +-- 8 files changed, 14 insertions(+), 9 deletions(-) diff --git a/conf.gradle b/conf.gradle index 818e0ef..140efcb 100644 --- a/conf.gradle +++ b/conf.gradle @@ -77,7 +77,7 @@ ext { "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.7", - "xdroid-mvp" : "com.lennon.utill:mvp:1.3.27", + "xdroid-mvp" : "com.lennon.utill:mvp:1.3.35", "avi-loading" : "com.wang.avi:library:2.1.3",//https://github.com/81813780/AVLoadingIndicatorView "rxbus" : "com.blankj:rxbus:1.6", diff --git a/mvp/build.gradle b/mvp/build.gradle index 933a688..de2d498 100644 --- a/mvp/build.gradle +++ b/mvp/build.gradle @@ -3,7 +3,7 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' -version = '1.3.27' +version = '1.3.35' group = rootProject.ext.groupId android { diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleViewBindingAdapter.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleViewBindingAdapter.java index 2ffc413..53a5fff 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleViewBindingAdapter.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleViewBindingAdapter.java @@ -5,6 +5,7 @@ import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import androidx.viewbinding.ViewBinding; @@ -70,5 +71,5 @@ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { /* * 获取 ViewBinding 实现类 * */ - protected abstract E getViewBinding(int viewType, LayoutInflater from, ViewGroup parent); + protected abstract E getViewBinding(@NonNull int viewType, @NonNull LayoutInflater from, @NonNull ViewGroup parent); } diff --git a/utill/build.gradle b/utill/build.gradle index b7e5a06..3f664ad 100644 --- a/utill/build.gradle +++ b/utill/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-parcelize' -version = '1.6.33' // 版本 +version = '1.6.41' // 版本 group = rootProject.groupId android { compileSdkVersion rootProject.ext.android.compileSdkVersion diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt index e19e91a..7fde0cc 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt @@ -13,6 +13,7 @@ import android.widget.Toast import androidx.viewbinding.ViewBinding import cn.droidlover.xdroidmvp.log.XLog import cn.droidlover.xdroidmvp.mvp.XActivity +import cn.droidlover.xdroidmvp.mvp.XPresent import cn.droidlover.xdroidmvp.net.NetError import com.lennon.cn.utill.bean.ToastRunnable @@ -27,7 +28,7 @@ import java.lang.Exception abstract class BaseActivity

, E : ViewBinding> : XActivity(), - BaseView { + BaseView

{ private var TAG = javaClass.simpleName private var dialog: CustomProgressDialog? = null diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt index 0be6328..fce9864 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt @@ -14,13 +14,14 @@ import androidx.viewbinding.ViewBinding import cn.droidlover.xdroidmvp.log.XLog import cn.droidlover.xdroidmvp.mvp.XFragment +import cn.droidlover.xdroidmvp.net.NetError import com.lennon.cn.utill.bean.ToastRunnable import com.lennon.cn.utill.dialog.CommonAlertDialog import com.lennon.cn.utill.dialog.CustomProgressDialog import com.lennon.cn.utill.dialog.OnAlertDialogListener abstract class BaseFragment

, E : ViewBinding> : XFragment(), - BaseView { + BaseView

{ private var rootView: View? = null private var dialog: CustomProgressDialog? = null @@ -28,6 +29,9 @@ abstract class BaseFragment

, E : ViewBinding> : XFragment findViewById(i: Int): T { return rootView!!.findViewById(i) } diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt b/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt index 48fb3dc..116b1fa 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt @@ -17,7 +17,7 @@ import com.lennon.cn.utill.dialog.CustomProgressDialog import com.lennon.cn.utill.dialog.OnAlertDialogListener abstract class BasePresentation

,E: ViewBinding>(context: Context, display: Display) : - XPresentation(context, display), BaseView { + XPresentation(context, display), BaseView

{ private var TAG = javaClass.simpleName private var dialog: CustomProgressDialog? = null private var listener: ChangeListener? = null diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt index fa0a698..a20f887 100644 --- a/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt @@ -2,12 +2,11 @@ package com.lennon.cn.utill.base import android.app.Activity import android.content.Context -import androidx.viewbinding.ViewBinding import cn.droidlover.xdroidmvp.mvp.IView import cn.droidlover.xdroidmvp.net.NetError import com.lennon.cn.utill.bean.ToastRunnable -interface BaseView

?, E : ViewBinding> : IView

{ +interface BaseView

?> : IView

{ fun toast(msg: String, second: Int) fun toast(msg: String)