From d62f7ea04285d2b2e5bf3a89ad8eb95cbc1b0fcd Mon Sep 17 00:00:00 2001 From: tongtongstudio Date: Tue, 3 Mar 2026 16:58:44 +0800 Subject: [PATCH] =?UTF-8?q?version:3.4.8=20fix:=20update:=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E4=B8=BB=E9=A1=B5ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FlycoTabLayoutZ_Lib/.gitignore | 1 + FlycoTabLayoutZ_Lib/build.gradle | 58 + FlycoTabLayoutZ_Lib/proguard-rules.pro | 17 + .../src/main/AndroidManifest.xml | 1 + .../com/flyco/tablayout/CommonTabLayout.java | 986 +++++++++++++ .../com/flyco/tablayout/SegmentTabLayout.java | 776 +++++++++++ .../tablayout/SlidingScaleTabLayout.java | 1216 +++++++++++++++++ .../com/flyco/tablayout/SlidingTabLayout.java | 1012 ++++++++++++++ .../tablayout/listener/CustomTabEntity.java | 13 + .../listener/OnTabSelectListener.java | 7 + .../transformer/ExtendTransformer.java | 53 + .../transformer/ITabScaleTransformer.java | 7 + .../transformer/IViewPagerTransformer.java | 12 + .../transformer/TabScaleTransformer.java | 110 ++ .../utils/FragmentChangeManager.java | 63 + .../flyco/tablayout/utils/UnreadMsgUtils.java | 58 + .../com/flyco/tablayout/utils/ViewUtils.java | 40 + .../com/flyco/tablayout/widget/MsgView.java | 159 +++ .../src/main/res/layout/layout_scale_tab.xml | 38 + .../src/main/res/layout/layout_tab.xml | 29 + .../src/main/res/layout/layout_tab_bottom.xml | 43 + .../src/main/res/layout/layout_tab_left.xml | 42 + .../src/main/res/layout/layout_tab_right.xml | 43 + .../main/res/layout/layout_tab_segment.xml | 37 + .../src/main/res/layout/layout_tab_top.xml | 42 + .../src/main/res/values/attrs.xml | 319 +++++ app/build.gradle | 84 +- app/src/main/AndroidManifest.xml | 66 +- .../fuying/sn/activity/home/HomeActivity.java | 250 ++++ .../sn/activity/home/HomeViewModel.java | 109 ++ .../fuying/sn/activity/main/MainActivity.java | 12 +- .../sn/activity/main/MainViewModel.java | 3 +- .../sn/activity/update/UpdateActivity.java | 90 ++ .../sn/activity/update/UpdateViewModel.java | 18 + .../com/fuying/sn/base/BaseApplication.java | 47 +- .../java/com/fuying/sn/base/BaseFragment.java | 44 + .../mvvm/fragment/BaseMvvmDialogFragment.java | 184 +++ .../base/mvvm/fragment/BaseMvvmFragment.java | 279 ++++ .../main/java/com/fuying/sn/bean/MapBean.java | 134 ++ .../com/fuying/sn/config/CommonConfig.java | 14 + .../fuying/sn/desktop/RunningAppManager.java | 16 +- .../sn/fragment/device/DeviceFragment.java | 156 +++ .../sn/fragment/device/DeviceViewModel.java | 102 ++ .../sn/fragment/usage/UsageFragment.java | 51 + .../sn/fragment/usage/UsageViewModel.java | 18 + .../com/fuying/sn/manager/AmapManager.java | 287 ++-- .../com/fuying/sn/manager/ControlManager.java | 26 +- .../sn/network/NetInterfaceManager.java | 209 ++- .../com/fuying/sn/network/UrlAddress.java | 116 +- .../sn/network/api/NewestAppUpdate.java | 2 +- .../api/{SNInfoApi.java => SnApi.java} | 4 +- .../fuying/sn/network/api/UpdateAdminSn.java | 2 +- .../interceptor/PostCacheInterceptor.java | 9 +- .../java/com/fuying/sn/push/PushManager.java | 24 +- .../com/fuying/sn/push/tpush/Constants.java | 17 - .../fuying/sn/push/tpush/MessageReceiver.java | 297 ---- .../sn/push/tpush/common/DBOpenHelper.java | 23 - .../tpush/common/NotificationService.java | 134 -- .../sn/push/tpush/po/XGNotification.java | 83 -- .../sn/receiver/APKinstallReceiver.java | 12 +- .../com/fuying/sn/receiver/BootReceiver.java | 11 +- .../fuying/sn/service/DownloadService.java | 25 +- .../com/fuying/sn/service/RemoteService.java | 40 - .../com/fuying/sn/service/StepService.java | 12 +- .../sn/service/main/MainSPresenter.java | 4 +- .../fuying/sn/service/main/MainService.java | 62 +- .../java/com/fuying/sn/utils/ApkUtils.java | 90 +- .../java/com/fuying/sn/utils/FileUtils.java | 6 +- .../sn/utils/{JGYUtils.java => JgyUtils.java} | 42 +- .../main/java/com/fuying/sn/utils/Utils.java | 41 +- .../main/res/drawable-hdpi/icon_blue_back.png | Bin 0 -> 2613 bytes app/src/main/res/drawable-hdpi/icon_close.png | Bin 0 -> 1701 bytes .../res/drawable-hdpi/main_background.png | Bin 0 -> 191563 bytes app/src/main/res/drawable/bt_normnl.xml | 3 +- app/src/main/res/drawable/bt_pressed.xml | 3 +- app/src/main/res/drawable/card_background.xml | 2 +- .../main/res/drawable/dialog_background.xml | 9 + app/src/main/res/drawable/radio_checked.xml | 3 +- .../main/res/drawable/update_background.xml | 13 + .../res/drawable/update_cancel_background.xml | 13 + app/src/main/res/layout/activity_home.xml | 82 +- .../res/layout/activity_selecte_grade.xml | 2 +- app/src/main/res/layout/activity_update.xml | 164 +++ app/src/main/res/layout/fragment_device.xml | 648 +++++++++ .../res/layout/fragment_dialog_restart.xml | 2 +- app/src/main/res/layout/fragment_usage.xml | 183 +++ app/src/main/res/values/colors.xml | 257 +++- app/src/main/res/values/styles.xml | 21 + app/tpns-configs.json | 14 - build.gradle | 1 - settings.gradle | 2 +- 91 files changed, 8580 insertions(+), 1279 deletions(-) create mode 100644 FlycoTabLayoutZ_Lib/.gitignore create mode 100644 FlycoTabLayoutZ_Lib/build.gradle create mode 100644 FlycoTabLayoutZ_Lib/proguard-rules.pro create mode 100644 FlycoTabLayoutZ_Lib/src/main/AndroidManifest.xml create mode 100644 FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/CommonTabLayout.java create mode 100644 FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/SegmentTabLayout.java create mode 100644 FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/SlidingScaleTabLayout.java create mode 100644 FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/SlidingTabLayout.java create mode 100644 FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/listener/CustomTabEntity.java create mode 100644 FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/listener/OnTabSelectListener.java create mode 100644 FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/transformer/ExtendTransformer.java create mode 100644 FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/transformer/ITabScaleTransformer.java create mode 100644 FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/transformer/IViewPagerTransformer.java create mode 100644 FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/transformer/TabScaleTransformer.java create mode 100644 FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/utils/FragmentChangeManager.java create mode 100644 FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/utils/UnreadMsgUtils.java create mode 100644 FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/utils/ViewUtils.java create mode 100644 FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/widget/MsgView.java create mode 100644 FlycoTabLayoutZ_Lib/src/main/res/layout/layout_scale_tab.xml create mode 100644 FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab.xml create mode 100644 FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_bottom.xml create mode 100644 FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_left.xml create mode 100644 FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_right.xml create mode 100644 FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_segment.xml create mode 100644 FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_top.xml create mode 100644 FlycoTabLayoutZ_Lib/src/main/res/values/attrs.xml create mode 100644 app/src/main/java/com/fuying/sn/activity/home/HomeActivity.java create mode 100644 app/src/main/java/com/fuying/sn/activity/home/HomeViewModel.java create mode 100644 app/src/main/java/com/fuying/sn/activity/update/UpdateActivity.java create mode 100644 app/src/main/java/com/fuying/sn/activity/update/UpdateViewModel.java create mode 100644 app/src/main/java/com/fuying/sn/base/BaseFragment.java create mode 100644 app/src/main/java/com/fuying/sn/base/mvvm/fragment/BaseMvvmDialogFragment.java create mode 100644 app/src/main/java/com/fuying/sn/base/mvvm/fragment/BaseMvvmFragment.java create mode 100644 app/src/main/java/com/fuying/sn/bean/MapBean.java create mode 100644 app/src/main/java/com/fuying/sn/fragment/device/DeviceFragment.java create mode 100644 app/src/main/java/com/fuying/sn/fragment/device/DeviceViewModel.java create mode 100644 app/src/main/java/com/fuying/sn/fragment/usage/UsageFragment.java create mode 100644 app/src/main/java/com/fuying/sn/fragment/usage/UsageViewModel.java rename app/src/main/java/com/fuying/sn/network/api/{SNInfoApi.java => SnApi.java} (85%) delete mode 100644 app/src/main/java/com/fuying/sn/push/tpush/Constants.java delete mode 100644 app/src/main/java/com/fuying/sn/push/tpush/MessageReceiver.java delete mode 100644 app/src/main/java/com/fuying/sn/push/tpush/common/DBOpenHelper.java delete mode 100644 app/src/main/java/com/fuying/sn/push/tpush/common/NotificationService.java delete mode 100644 app/src/main/java/com/fuying/sn/push/tpush/po/XGNotification.java rename app/src/main/java/com/fuying/sn/utils/{JGYUtils.java => JgyUtils.java} (97%) create mode 100644 app/src/main/res/drawable-hdpi/icon_blue_back.png create mode 100644 app/src/main/res/drawable-hdpi/icon_close.png create mode 100644 app/src/main/res/drawable-hdpi/main_background.png create mode 100644 app/src/main/res/drawable/dialog_background.xml create mode 100644 app/src/main/res/drawable/update_background.xml create mode 100644 app/src/main/res/drawable/update_cancel_background.xml create mode 100644 app/src/main/res/layout/activity_update.xml create mode 100644 app/src/main/res/layout/fragment_device.xml create mode 100644 app/src/main/res/layout/fragment_usage.xml delete mode 100644 app/tpns-configs.json diff --git a/FlycoTabLayoutZ_Lib/.gitignore b/FlycoTabLayoutZ_Lib/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/FlycoTabLayoutZ_Lib/.gitignore @@ -0,0 +1 @@ +/build diff --git a/FlycoTabLayoutZ_Lib/build.gradle b/FlycoTabLayoutZ_Lib/build.gradle new file mode 100644 index 0000000..09c8a19 --- /dev/null +++ b/FlycoTabLayoutZ_Lib/build.gradle @@ -0,0 +1,58 @@ +apply plugin: 'com.android.library' +//apply plugin: 'com.novoda.bintray-release' + +version = "1.3.3" +android { + compileSdkVersion 28 + buildToolsVersion "28.0.3" + + defaultConfig { + minSdkVersion 17 + targetSdkVersion 28 + versionCode 5 + versionName version + } + buildTypes { + MT6789Debug {} + MT6789Release {} + Huaruian8768Debug {} + Huaruian8768Release {} + iPlay50SEDebug {} + iPlay50SERelease {} + unisocDebug {} + unisocRelease {} + mtk11Debug {} + mtk11Release {} + teclastMTKDebug {} + teclastMTKRelease {} + G6Debug {} + G6Release {} + G13Debug {} + G13Release {} + Teclast8515Debug {} + Teclast8515Release {} + teclastUnisocdebug {} + teclastUnisocrelease {} + teclastUnisocUserdebug {} + zhanRuiDebug {} + zhanRuiRelease {} + zhanRuiUserdebugReleas {} + debug {} + release {} + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'androidx.legacy:legacy-support-v4:1.0.0' +} + +//// jcenter发布的信息 +//publish { +// userOrg = 'lizp' // 创建repo的位置 +// groupId = 'com.lzp' // 引用的分组名称 +// artifactId = 'FlycoTabLayoutZ'//项目名称 +// publishVersion = version//版本号 +// desc = '在FlycoTabLayout的基础上,扩展出SlidingScaleTabLayout,实现滑动可以改变tab字体的大小的切换效果' +// website = 'https://github.com/li504799868/FlycoTabLayoutZ' +//} \ No newline at end of file diff --git a/FlycoTabLayoutZ_Lib/proguard-rules.pro b/FlycoTabLayoutZ_Lib/proguard-rules.pro new file mode 100644 index 0000000..48100e3 --- /dev/null +++ b/FlycoTabLayoutZ_Lib/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/lihui/work/AndroidStudio/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/FlycoTabLayoutZ_Lib/src/main/AndroidManifest.xml b/FlycoTabLayoutZ_Lib/src/main/AndroidManifest.xml new file mode 100644 index 0000000..e0a940b --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/CommonTabLayout.java b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/CommonTabLayout.java new file mode 100644 index 0000000..302ec2a --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/CommonTabLayout.java @@ -0,0 +1,986 @@ +package com.flyco.tablayout; + +import android.animation.TypeEvaluator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.drawable.GradientDrawable; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.OvershootInterpolator; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; + +import com.flyco.tablayout.listener.CustomTabEntity; +import com.flyco.tablayout.listener.OnTabSelectListener; +import com.flyco.tablayout.utils.FragmentChangeManager; +import com.flyco.tablayout.utils.UnreadMsgUtils; +import com.flyco.tablayout.widget.MsgView; + +import java.util.ArrayList; + +/** + * 没有继承HorizontalScrollView不能滑动,对于ViewPager无依赖 + */ +public class CommonTabLayout extends FrameLayout implements ValueAnimator.AnimatorUpdateListener { + private Context mContext; + private ArrayList mTabEntitys = new ArrayList<>(); + private LinearLayout mTabsContainer; + private int mCurrentTab; + private int mLastTab; + private int mTabCount; + /** + * 用于绘制显示器 + */ + private Rect mIndicatorRect = new Rect(); + private GradientDrawable mIndicatorDrawable = new GradientDrawable(); + + private Paint mRectPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Paint mDividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Paint mTrianglePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Path mTrianglePath = new Path(); + private static final int STYLE_NORMAL = 0; + private static final int STYLE_TRIANGLE = 1; + private static final int STYLE_BLOCK = 2; + private int mIndicatorStyle = STYLE_NORMAL; + + private float mTabPadding; + private boolean mTabSpaceEqual; + private float mTabWidth; + + /** + * indicator + */ + private int mIndicatorColor; + private float mIndicatorHeight; + private float mIndicatorWidth; + private float mIndicatorCornerRadius; + private float mIndicatorMarginLeft; + private float mIndicatorMarginTop; + private float mIndicatorMarginRight; + private float mIndicatorMarginBottom; + private long mIndicatorAnimDuration; + private boolean mIndicatorAnimEnable; + private boolean mIndicatorBounceEnable; + private int mIndicatorGravity; + + /** + * underline + */ + private int mUnderlineColor; + private float mUnderlineHeight; + private int mUnderlineGravity; + + /** + * divider + */ + private int mDividerColor; + private float mDividerWidth; + private float mDividerPadding; + + /** + * title + */ + private static final int TEXT_BOLD_NONE = 0; + private static final int TEXT_BOLD_WHEN_SELECT = 1; + private static final int TEXT_BOLD_BOTH = 2; + private float mTextsize; + private int mTextSelectColor; + private int mTextUnselectColor; + private int mTextBold; + private boolean mTextAllCaps; + + /** + * icon + */ + private boolean mIconVisible; + private int mIconGravity; + private float mIconWidth; + private float mIconHeight; + private float mIconMargin; + + private int mHeight; + + /** + * anim + */ + private ValueAnimator mValueAnimator; + private OvershootInterpolator mInterpolator = new OvershootInterpolator(1.5f); + + private FragmentChangeManager mFragmentChangeManager; + + public CommonTabLayout(Context context) { + this(context, null, 0); + } + + public CommonTabLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public CommonTabLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setWillNotDraw(false);//重写onDraw方法,需要调用这个方法来清除flag + setClipChildren(false); + setClipToPadding(false); + + this.mContext = context; + mTabsContainer = new LinearLayout(context); + addView(mTabsContainer); + + obtainAttributes(context, attrs); + + //get layout_height + String height = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "layout_height"); + + //create ViewPager + if (height.equals(ViewGroup.LayoutParams.MATCH_PARENT + "")) { + } else if (height.equals(ViewGroup.LayoutParams.WRAP_CONTENT + "")) { + } else { + int[] systemAttrs = {android.R.attr.layout_height}; + TypedArray a = context.obtainStyledAttributes(attrs, systemAttrs); + mHeight = a.getDimensionPixelSize(0, ViewGroup.LayoutParams.WRAP_CONTENT); + a.recycle(); + } + + mValueAnimator = ValueAnimator.ofObject(new PointEvaluator(), mLastP, mCurrentP); + mValueAnimator.addUpdateListener(this); + } + + private void obtainAttributes(Context context, AttributeSet attrs) { + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CommonTabLayout); + + mIndicatorStyle = ta.getInt(R.styleable.CommonTabLayout_tl_indicator_style, 0); + mIndicatorColor = ta.getColor(R.styleable.CommonTabLayout_tl_indicator_color, Color.parseColor(mIndicatorStyle == STYLE_BLOCK ? "#4B6A87" : "#ffffff")); + mIndicatorHeight = ta.getDimension(R.styleable.CommonTabLayout_tl_indicator_height, + dp2px(mIndicatorStyle == STYLE_TRIANGLE ? 4 : (mIndicatorStyle == STYLE_BLOCK ? -1 : 2))); + mIndicatorWidth = ta.getDimension(R.styleable.CommonTabLayout_tl_indicator_width, dp2px(mIndicatorStyle == STYLE_TRIANGLE ? 10 : -1)); + mIndicatorCornerRadius = ta.getDimension(R.styleable.CommonTabLayout_tl_indicator_corner_radius, dp2px(mIndicatorStyle == STYLE_BLOCK ? -1 : 0)); + mIndicatorMarginLeft = ta.getDimension(R.styleable.CommonTabLayout_tl_indicator_margin_left, dp2px(0)); + mIndicatorMarginTop = ta.getDimension(R.styleable.CommonTabLayout_tl_indicator_margin_top, dp2px(mIndicatorStyle == STYLE_BLOCK ? 7 : 0)); + mIndicatorMarginRight = ta.getDimension(R.styleable.CommonTabLayout_tl_indicator_margin_right, dp2px(0)); + mIndicatorMarginBottom = ta.getDimension(R.styleable.CommonTabLayout_tl_indicator_margin_bottom, dp2px(mIndicatorStyle == STYLE_BLOCK ? 7 : 0)); + mIndicatorAnimEnable = ta.getBoolean(R.styleable.CommonTabLayout_tl_indicator_anim_enable, true); + mIndicatorBounceEnable = ta.getBoolean(R.styleable.CommonTabLayout_tl_indicator_bounce_enable, true); + mIndicatorAnimDuration = ta.getInt(R.styleable.CommonTabLayout_tl_indicator_anim_duration, -1); + mIndicatorGravity = ta.getInt(R.styleable.CommonTabLayout_tl_indicator_gravity, Gravity.BOTTOM); + + mUnderlineColor = ta.getColor(R.styleable.CommonTabLayout_tl_underline_color, Color.parseColor("#ffffff")); + mUnderlineHeight = ta.getDimension(R.styleable.CommonTabLayout_tl_underline_height, dp2px(0)); + mUnderlineGravity = ta.getInt(R.styleable.CommonTabLayout_tl_underline_gravity, Gravity.BOTTOM); + + mDividerColor = ta.getColor(R.styleable.CommonTabLayout_tl_divider_color, Color.parseColor("#ffffff")); + mDividerWidth = ta.getDimension(R.styleable.CommonTabLayout_tl_divider_width, dp2px(0)); + mDividerPadding = ta.getDimension(R.styleable.CommonTabLayout_tl_divider_padding, dp2px(12)); + + mTextsize = ta.getDimension(R.styleable.CommonTabLayout_tl_textSize, sp2px(13f)); + mTextSelectColor = ta.getColor(R.styleable.CommonTabLayout_tl_textSelectColor, Color.parseColor("#ffffff")); + mTextUnselectColor = ta.getColor(R.styleable.CommonTabLayout_tl_textUnSelectColor, Color.parseColor("#AAffffff")); + mTextBold = ta.getInt(R.styleable.CommonTabLayout_tl_textBold, TEXT_BOLD_NONE); + mTextAllCaps = ta.getBoolean(R.styleable.CommonTabLayout_tl_textAllCaps, false); + + mIconVisible = ta.getBoolean(R.styleable.CommonTabLayout_tl_iconVisible, true); + mIconGravity = ta.getInt(R.styleable.CommonTabLayout_tl_iconGravity, Gravity.TOP); + mIconWidth = ta.getDimension(R.styleable.CommonTabLayout_tl_iconWidth, dp2px(0)); + mIconHeight = ta.getDimension(R.styleable.CommonTabLayout_tl_iconHeight, dp2px(0)); + mIconMargin = ta.getDimension(R.styleable.CommonTabLayout_tl_iconMargin, dp2px(2.5f)); + + mTabSpaceEqual = ta.getBoolean(R.styleable.CommonTabLayout_tl_tab_space_equal, true); + mTabWidth = ta.getDimension(R.styleable.CommonTabLayout_tl_tab_width, dp2px(-1)); + mTabPadding = ta.getDimension(R.styleable.CommonTabLayout_tl_tab_padding, mTabSpaceEqual || mTabWidth > 0 ? dp2px(0) : dp2px(10)); + + ta.recycle(); + } + + public void setTabData(ArrayList tabEntitys) { + if (tabEntitys == null || tabEntitys.size() == 0) { + throw new IllegalStateException("TabEntitys can not be NULL or EMPTY !"); + } + + this.mTabEntitys.clear(); + this.mTabEntitys.addAll(tabEntitys); + + notifyDataSetChanged(); + } + + /** + * 关联数据支持同时切换fragments + */ + public void setTabData(ArrayList tabEntitys, FragmentActivity fa, int containerViewId, ArrayList fragments) { + mFragmentChangeManager = new FragmentChangeManager(fa.getSupportFragmentManager(), containerViewId, fragments); + setTabData(tabEntitys); + } + + /** + * 更新数据 + */ + public void notifyDataSetChanged() { + mTabsContainer.removeAllViews(); + this.mTabCount = mTabEntitys.size(); + View tabView; + for (int i = 0; i < mTabCount; i++) { + if (mIconGravity == Gravity.LEFT) { + tabView = View.inflate(mContext, R.layout.layout_tab_left, null); + } else if (mIconGravity == Gravity.RIGHT) { + tabView = View.inflate(mContext, R.layout.layout_tab_right, null); + } else if (mIconGravity == Gravity.BOTTOM) { + tabView = View.inflate(mContext, R.layout.layout_tab_bottom, null); + } else { + tabView = View.inflate(mContext, R.layout.layout_tab_top, null); + } + + tabView.setTag(i); + addTab(i, tabView); + } + + updateTabStyles(); + } + + /** + * 创建并添加tab + */ + private void addTab(final int position, View tabView) { + TextView tv_tab_title = tabView.findViewById(R.id.tv_tab_title); + tv_tab_title.setText(mTabEntitys.get(position).getTabTitle()); + ImageView iv_tab_icon = tabView.findViewById(R.id.iv_tab_icon); + iv_tab_icon.setImageResource(mTabEntitys.get(position).getTabUnselectedIcon()); + + tabView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + int position = (Integer) v.getTag(); + if (mCurrentTab != position) { + setCurrentTab(position); + if (mListener != null) { + mListener.onTabSelect(position); + } + } else { + if (mListener != null) { + mListener.onTabReselect(position); + } + } + } + }); + + /** 每一个Tab的布局参数 */ + LinearLayout.LayoutParams lp_tab = mTabSpaceEqual ? + new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f) : + new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); + if (mTabWidth > 0) { + lp_tab = new LinearLayout.LayoutParams((int) mTabWidth, LayoutParams.MATCH_PARENT); + } + mTabsContainer.addView(tabView, position, lp_tab); + } + + private void updateTabStyles() { + for (int i = 0; i < mTabCount; i++) { + View tabView = mTabsContainer.getChildAt(i); + tabView.setPadding((int) mTabPadding, 0, (int) mTabPadding, 0); + TextView tv_tab_title = tabView.findViewById(R.id.tv_tab_title); + tv_tab_title.setTextColor(i == mCurrentTab ? mTextSelectColor : mTextUnselectColor); + tv_tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextsize); +// tv_tab_title.setPadding((int) mTabPadding, 0, (int) mTabPadding, 0); + if (mTextAllCaps) { + tv_tab_title.setText(tv_tab_title.getText().toString().toUpperCase()); + } + + if (mTextBold == TEXT_BOLD_BOTH) { + tv_tab_title.getPaint().setFakeBoldText(true); + } + // 被选中设置为粗体 + else if (mTextBold == TEXT_BOLD_WHEN_SELECT && i == mCurrentTab) { + tv_tab_title.getPaint().setFakeBoldText(true); + } else if (mTextBold == TEXT_BOLD_NONE) { + tv_tab_title.getPaint().setFakeBoldText(false); + } + + ImageView iv_tab_icon = tabView.findViewById(R.id.iv_tab_icon); + if (mIconVisible) { + iv_tab_icon.setVisibility(View.VISIBLE); + CustomTabEntity tabEntity = mTabEntitys.get(i); + iv_tab_icon.setImageResource(i == mCurrentTab ? tabEntity.getTabSelectedIcon() : tabEntity.getTabUnselectedIcon()); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( + mIconWidth <= 0 ? LinearLayout.LayoutParams.WRAP_CONTENT : (int) mIconWidth, + mIconHeight <= 0 ? LinearLayout.LayoutParams.WRAP_CONTENT : (int) mIconHeight); + if (mIconGravity == Gravity.LEFT) { + lp.rightMargin = (int) mIconMargin; + } else if (mIconGravity == Gravity.RIGHT) { + lp.leftMargin = (int) mIconMargin; + } else if (mIconGravity == Gravity.BOTTOM) { + lp.topMargin = (int) mIconMargin; + } else { + lp.bottomMargin = (int) mIconMargin; + } + + iv_tab_icon.setLayoutParams(lp); + } else { + iv_tab_icon.setVisibility(View.GONE); + } + } + } + + private void updateTabSelection(int position) { + for (int i = 0; i < mTabCount; ++i) { + View tabView = mTabsContainer.getChildAt(i); + final boolean isSelect = i == position; + TextView tab_title = tabView.findViewById(R.id.tv_tab_title); + tab_title.setTextColor(isSelect ? mTextSelectColor : mTextUnselectColor); + ImageView iv_tab_icon = tabView.findViewById(R.id.iv_tab_icon); + CustomTabEntity tabEntity = mTabEntitys.get(i); + iv_tab_icon.setImageResource(isSelect ? tabEntity.getTabSelectedIcon() : tabEntity.getTabUnselectedIcon()); + if (mTextBold == TEXT_BOLD_WHEN_SELECT) { + tab_title.getPaint().setFakeBoldText(isSelect); + } + } + } + + private void calcOffset() { + final View currentTabView = mTabsContainer.getChildAt(this.mCurrentTab); + mCurrentP.left = currentTabView.getLeft(); + mCurrentP.right = currentTabView.getRight(); + + final View lastTabView = mTabsContainer.getChildAt(this.mLastTab); + mLastP.left = lastTabView.getLeft(); + mLastP.right = lastTabView.getRight(); + +// Log.d("AAA", "mLastP--->" + mLastP.left + "&" + mLastP.right); +// Log.d("AAA", "mCurrentP--->" + mCurrentP.left + "&" + mCurrentP.right); + if (mLastP.left == mCurrentP.left && mLastP.right == mCurrentP.right) { + invalidate(); + } else { + mValueAnimator.setObjectValues(mLastP, mCurrentP); + if (mIndicatorBounceEnable) { + mValueAnimator.setInterpolator(mInterpolator); + } + + if (mIndicatorAnimDuration < 0) { + mIndicatorAnimDuration = mIndicatorBounceEnable ? 500 : 250; + } + mValueAnimator.setDuration(mIndicatorAnimDuration); + mValueAnimator.start(); + } + } + + private void calcIndicatorRect() { + View currentTabView = mTabsContainer.getChildAt(this.mCurrentTab); + float left = currentTabView.getLeft(); + float right = currentTabView.getRight(); + + mIndicatorRect.left = (int) left; + mIndicatorRect.right = (int) right; + + if (mIndicatorWidth < 0) { //indicatorWidth小于0时,原jpardogo's PagerSlidingTabStrip + + } else {//indicatorWidth大于0时,圆角矩形以及三角形 + float indicatorLeft = currentTabView.getLeft() + (currentTabView.getWidth() - mIndicatorWidth) / 2; + + mIndicatorRect.left = (int) indicatorLeft; + mIndicatorRect.right = (int) (mIndicatorRect.left + mIndicatorWidth); + } + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + View currentTabView = mTabsContainer.getChildAt(this.mCurrentTab); + IndicatorPoint p = (IndicatorPoint) animation.getAnimatedValue(); + mIndicatorRect.left = (int) p.left; + mIndicatorRect.right = (int) p.right; + + if (mIndicatorWidth < 0) { //indicatorWidth小于0时,原jpardogo's PagerSlidingTabStrip + + } else {//indicatorWidth大于0时,圆角矩形以及三角形 + float indicatorLeft = p.left + (currentTabView.getWidth() - mIndicatorWidth) / 2; + + mIndicatorRect.left = (int) indicatorLeft; + mIndicatorRect.right = (int) (mIndicatorRect.left + mIndicatorWidth); + } + invalidate(); + } + + private boolean mIsFirstDraw = true; + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (isInEditMode() || mTabCount <= 0) { + return; + } + + int height = getHeight(); + int paddingLeft = getPaddingLeft(); + // draw divider + if (mDividerWidth > 0) { + mDividerPaint.setStrokeWidth(mDividerWidth); + mDividerPaint.setColor(mDividerColor); + for (int i = 0; i < mTabCount - 1; i++) { + View tab = mTabsContainer.getChildAt(i); + canvas.drawLine(paddingLeft + tab.getRight(), mDividerPadding, paddingLeft + tab.getRight(), height - mDividerPadding, mDividerPaint); + } + } + + // draw underline + if (mUnderlineHeight > 0) { + mRectPaint.setColor(mUnderlineColor); + if (mUnderlineGravity == Gravity.BOTTOM) { + canvas.drawRect(paddingLeft, height - mUnderlineHeight, mTabsContainer.getWidth() + paddingLeft, height, mRectPaint); + } else { + canvas.drawRect(paddingLeft, 0, mTabsContainer.getWidth() + paddingLeft, mUnderlineHeight, mRectPaint); + } + } + + //draw indicator line + if (mIndicatorAnimEnable) { + if (mIsFirstDraw) { + mIsFirstDraw = false; + calcIndicatorRect(); + } + } else { + calcIndicatorRect(); + } + + + if (mIndicatorStyle == STYLE_TRIANGLE) { + if (mIndicatorHeight > 0) { + mTrianglePaint.setColor(mIndicatorColor); + mTrianglePath.reset(); + mTrianglePath.moveTo(paddingLeft + mIndicatorRect.left, height); + mTrianglePath.lineTo(paddingLeft + mIndicatorRect.left / 2 + mIndicatorRect.right / 2, height - mIndicatorHeight); + mTrianglePath.lineTo(paddingLeft + mIndicatorRect.right, height); + mTrianglePath.close(); + canvas.drawPath(mTrianglePath, mTrianglePaint); + } + } else if (mIndicatorStyle == STYLE_BLOCK) { + if (mIndicatorHeight < 0) { + mIndicatorHeight = height - mIndicatorMarginTop - mIndicatorMarginBottom; + } else { + + } + + if (mIndicatorHeight > 0) { + if (mIndicatorCornerRadius < 0 || mIndicatorCornerRadius > mIndicatorHeight / 2) { + mIndicatorCornerRadius = mIndicatorHeight / 2; + } + + mIndicatorDrawable.setColor(mIndicatorColor); + mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left, + (int) mIndicatorMarginTop, (int) (paddingLeft + mIndicatorRect.right - mIndicatorMarginRight), + (int) (mIndicatorMarginTop + mIndicatorHeight)); + mIndicatorDrawable.setCornerRadius(mIndicatorCornerRadius); + mIndicatorDrawable.draw(canvas); + } + } else { + /* mRectPaint.setColor(mIndicatorColor); + calcIndicatorRect(); + canvas.drawRect(getPaddingLeft() + mIndicatorRect.left, getHeight() - mIndicatorHeight, + mIndicatorRect.right + getPaddingLeft(), getHeight(), mRectPaint);*/ + + if (mIndicatorHeight > 0) { + mIndicatorDrawable.setColor(mIndicatorColor); + if (mIndicatorGravity == Gravity.BOTTOM) { + mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left, + height - (int) mIndicatorHeight - (int) mIndicatorMarginBottom, + paddingLeft + mIndicatorRect.right - (int) mIndicatorMarginRight, + height - (int) mIndicatorMarginBottom); + } else { + mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left, + (int) mIndicatorMarginTop, + paddingLeft + mIndicatorRect.right - (int) mIndicatorMarginRight, + (int) mIndicatorHeight + (int) mIndicatorMarginTop); + } + mIndicatorDrawable.setCornerRadius(mIndicatorCornerRadius); + mIndicatorDrawable.draw(canvas); + } + } + } + + //setter and getter + public void setCurrentTab(int currentTab) { + mLastTab = this.mCurrentTab; + this.mCurrentTab = currentTab; + updateTabSelection(currentTab); + if (mFragmentChangeManager != null) { + mFragmentChangeManager.setFragments(currentTab); + } + if (mIndicatorAnimEnable) { + calcOffset(); + } else { + invalidate(); + } + } + + public void setIndicatorStyle(int indicatorStyle) { + this.mIndicatorStyle = indicatorStyle; + invalidate(); + } + + public void setTabPadding(float tabPadding) { + this.mTabPadding = dp2px(tabPadding); + updateTabStyles(); + } + + public void setTabSpaceEqual(boolean tabSpaceEqual) { + this.mTabSpaceEqual = tabSpaceEqual; + updateTabStyles(); + } + + public void setTabWidth(float tabWidth) { + this.mTabWidth = dp2px(tabWidth); + updateTabStyles(); + } + + public void setIndicatorColor(int indicatorColor) { + this.mIndicatorColor = indicatorColor; + invalidate(); + } + + public void setIndicatorHeight(float indicatorHeight) { + this.mIndicatorHeight = dp2px(indicatorHeight); + invalidate(); + } + + public void setIndicatorWidth(float indicatorWidth) { + this.mIndicatorWidth = dp2px(indicatorWidth); + invalidate(); + } + + public void setIndicatorCornerRadius(float indicatorCornerRadius) { + this.mIndicatorCornerRadius = dp2px(indicatorCornerRadius); + invalidate(); + } + + public void setIndicatorGravity(int indicatorGravity) { + this.mIndicatorGravity = indicatorGravity; + invalidate(); + } + + public void setIndicatorMargin(float indicatorMarginLeft, float indicatorMarginTop, + float indicatorMarginRight, float indicatorMarginBottom) { + this.mIndicatorMarginLeft = dp2px(indicatorMarginLeft); + this.mIndicatorMarginTop = dp2px(indicatorMarginTop); + this.mIndicatorMarginRight = dp2px(indicatorMarginRight); + this.mIndicatorMarginBottom = dp2px(indicatorMarginBottom); + invalidate(); + } + + public void setIndicatorAnimDuration(long indicatorAnimDuration) { + this.mIndicatorAnimDuration = indicatorAnimDuration; + } + + public void setIndicatorAnimEnable(boolean indicatorAnimEnable) { + this.mIndicatorAnimEnable = indicatorAnimEnable; + } + + public void setIndicatorBounceEnable(boolean indicatorBounceEnable) { + this.mIndicatorBounceEnable = indicatorBounceEnable; + } + + public void setUnderlineColor(int underlineColor) { + this.mUnderlineColor = underlineColor; + invalidate(); + } + + public void setUnderlineHeight(float underlineHeight) { + this.mUnderlineHeight = dp2px(underlineHeight); + invalidate(); + } + + public void setUnderlineGravity(int underlineGravity) { + this.mUnderlineGravity = underlineGravity; + invalidate(); + } + + public void setDividerColor(int dividerColor) { + this.mDividerColor = dividerColor; + invalidate(); + } + + public void setDividerWidth(float dividerWidth) { + this.mDividerWidth = dp2px(dividerWidth); + invalidate(); + } + + public void setDividerPadding(float dividerPadding) { + this.mDividerPadding = dp2px(dividerPadding); + invalidate(); + } + + public void setTextsize(float textsize) { + this.mTextsize = sp2px(textsize); + updateTabStyles(); + } + + public void setTextSelectColor(int textSelectColor) { + this.mTextSelectColor = textSelectColor; + updateTabStyles(); + } + + public void setTextUnselectColor(int textUnselectColor) { + this.mTextUnselectColor = textUnselectColor; + updateTabStyles(); + } + + public void setTextBold(int textBold) { + this.mTextBold = textBold; + updateTabStyles(); + } + + public void setIconVisible(boolean iconVisible) { + this.mIconVisible = iconVisible; + updateTabStyles(); + } + + public void setIconGravity(int iconGravity) { + this.mIconGravity = iconGravity; + notifyDataSetChanged(); + } + + public void setIconWidth(float iconWidth) { + this.mIconWidth = dp2px(iconWidth); + updateTabStyles(); + } + + public void setIconHeight(float iconHeight) { + this.mIconHeight = dp2px(iconHeight); + updateTabStyles(); + } + + public void setIconMargin(float iconMargin) { + this.mIconMargin = dp2px(iconMargin); + updateTabStyles(); + } + + public void setTextAllCaps(boolean textAllCaps) { + this.mTextAllCaps = textAllCaps; + updateTabStyles(); + } + + + public int getTabCount() { + return mTabCount; + } + + public int getCurrentTab() { + return mCurrentTab; + } + + public int getIndicatorStyle() { + return mIndicatorStyle; + } + + public float getTabPadding() { + return mTabPadding; + } + + public boolean isTabSpaceEqual() { + return mTabSpaceEqual; + } + + public float getTabWidth() { + return mTabWidth; + } + + public int getIndicatorColor() { + return mIndicatorColor; + } + + public float getIndicatorHeight() { + return mIndicatorHeight; + } + + public float getIndicatorWidth() { + return mIndicatorWidth; + } + + public float getIndicatorCornerRadius() { + return mIndicatorCornerRadius; + } + + public float getIndicatorMarginLeft() { + return mIndicatorMarginLeft; + } + + public float getIndicatorMarginTop() { + return mIndicatorMarginTop; + } + + public float getIndicatorMarginRight() { + return mIndicatorMarginRight; + } + + public float getIndicatorMarginBottom() { + return mIndicatorMarginBottom; + } + + public long getIndicatorAnimDuration() { + return mIndicatorAnimDuration; + } + + public boolean isIndicatorAnimEnable() { + return mIndicatorAnimEnable; + } + + public boolean isIndicatorBounceEnable() { + return mIndicatorBounceEnable; + } + + public int getUnderlineColor() { + return mUnderlineColor; + } + + public float getUnderlineHeight() { + return mUnderlineHeight; + } + + public int getDividerColor() { + return mDividerColor; + } + + public float getDividerWidth() { + return mDividerWidth; + } + + public float getDividerPadding() { + return mDividerPadding; + } + + public float getTextsize() { + return mTextsize; + } + + public int getTextSelectColor() { + return mTextSelectColor; + } + + public int getTextUnselectColor() { + return mTextUnselectColor; + } + + public int getTextBold() { + return mTextBold; + } + + public boolean isTextAllCaps() { + return mTextAllCaps; + } + + public int getIconGravity() { + return mIconGravity; + } + + public float getIconWidth() { + return mIconWidth; + } + + public float getIconHeight() { + return mIconHeight; + } + + public float getIconMargin() { + return mIconMargin; + } + + public boolean isIconVisible() { + return mIconVisible; + } + + + public ImageView getIconView(int tab) { + View tabView = mTabsContainer.getChildAt(tab); + ImageView iv_tab_icon = tabView.findViewById(R.id.iv_tab_icon); + return iv_tab_icon; + } + + public TextView getTitleView(int tab) { + View tabView = mTabsContainer.getChildAt(tab); + TextView tv_tab_title = tabView.findViewById(R.id.tv_tab_title); + return tv_tab_title; + } + + //setter and getter + + // show MsgTipView + private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private SparseArray mInitSetMap = new SparseArray<>(); + + /** + * 显示未读消息 + * + * @param position 显示tab位置 + * @param num num小于等于0显示红点,num大于0显示数字 + */ + public void showMsg(int position, int num) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + + View tabView = mTabsContainer.getChildAt(position); + MsgView tipView = tabView.findViewById(R.id.rtv_msg_tip); + if (tipView != null) { + UnreadMsgUtils.show(tipView, num); + + if (mInitSetMap.get(position) != null && mInitSetMap.get(position)) { + return; + } + + if (!mIconVisible) { + setMsgMargin(position, 2, 2); + } else { + setMsgMargin(position, 0, + mIconGravity == Gravity.LEFT || mIconGravity == Gravity.RIGHT ? 4 : 0); + } + + mInitSetMap.put(position, true); + } + } + + /** + * 显示未读红点 + * + * @param position 显示tab位置 + */ + public void showDot(int position) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + showMsg(position, 0); + } + + public void hideMsg(int position) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + + View tabView = mTabsContainer.getChildAt(position); + MsgView tipView = tabView.findViewById(R.id.rtv_msg_tip); + if (tipView != null) { + tipView.setVisibility(View.GONE); + } + } + + /** + * 设置提示红点偏移,注意 + * 1.控件为固定高度:参照点为tab内容的右上角 + * 2.控件高度不固定(WRAP_CONTENT):参照点为tab内容的右上角,此时高度已是红点的最高显示范围,所以这时bottomPadding其实就是topPadding + */ + public void setMsgMargin(int position, float leftPadding, float bottomPadding) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + View tabView = mTabsContainer.getChildAt(position); + MsgView tipView = tabView.findViewById(R.id.rtv_msg_tip); + if (tipView != null) { + TextView tv_tab_title = tabView.findViewById(R.id.tv_tab_title); + mTextPaint.setTextSize(mTextsize); + float textWidth = mTextPaint.measureText(tv_tab_title.getText().toString()); + float textHeight = mTextPaint.descent() - mTextPaint.ascent(); + MarginLayoutParams lp = (MarginLayoutParams) tipView.getLayoutParams(); + + float iconH = mIconHeight; + float margin = 0; + if (mIconVisible) { + if (iconH <= 0) { + iconH = mContext.getResources().getDrawable(mTabEntitys.get(position).getTabSelectedIcon()).getIntrinsicHeight(); + } + margin = mIconMargin; + } + + if (mIconGravity == Gravity.TOP || mIconGravity == Gravity.BOTTOM) { + lp.leftMargin = dp2px(leftPadding); + lp.topMargin = mHeight > 0 ? (int) (mHeight - textHeight - iconH - margin) / 2 - dp2px(bottomPadding) : dp2px(bottomPadding); + } else { + lp.leftMargin = dp2px(leftPadding); + lp.topMargin = mHeight > 0 ? (int) (mHeight - Math.max(textHeight, iconH)) / 2 - dp2px(bottomPadding) : dp2px(bottomPadding); + } + + tipView.setLayoutParams(lp); + } + } + + /** + * 当前类只提供了少许设置未读消息属性的方法,可以通过该方法获取MsgView对象从而各种设置 + */ + public MsgView getMsgView(int position) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + View tabView = mTabsContainer.getChildAt(position); + MsgView tipView = tabView.findViewById(R.id.rtv_msg_tip); + return tipView; + } + + private OnTabSelectListener mListener; + + public void setOnTabSelectListener(OnTabSelectListener listener) { + this.mListener = listener; + } + + + @Override + protected Parcelable onSaveInstanceState() { + Bundle bundle = new Bundle(); + bundle.putParcelable("instanceState", super.onSaveInstanceState()); + bundle.putInt("mCurrentTab", mCurrentTab); + return bundle; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (state instanceof Bundle) { + Bundle bundle = (Bundle) state; + mCurrentTab = bundle.getInt("mCurrentTab"); + state = bundle.getParcelable("instanceState"); + if (mCurrentTab != 0 && mTabsContainer.getChildCount() > 0) { + updateTabSelection(mCurrentTab); + } + } + super.onRestoreInstanceState(state); + } + + class IndicatorPoint { + public float left; + public float right; + } + + private IndicatorPoint mCurrentP = new IndicatorPoint(); + private IndicatorPoint mLastP = new IndicatorPoint(); + + class PointEvaluator implements TypeEvaluator { + @Override + public IndicatorPoint evaluate(float fraction, IndicatorPoint startValue, IndicatorPoint endValue) { + float left = startValue.left + fraction * (endValue.left - startValue.left); + float right = startValue.right + fraction * (endValue.right - startValue.right); + IndicatorPoint point = new IndicatorPoint(); + point.left = left; + point.right = right; + return point; + } + } + + + protected int dp2px(float dp) { + final float scale = mContext.getResources().getDisplayMetrics().density; + return (int) (dp * scale + 0.5f); + } + + protected int sp2px(float sp) { + final float scale = this.mContext.getResources().getDisplayMetrics().scaledDensity; + return (int) (sp * scale + 0.5f); + } + +} diff --git a/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/SegmentTabLayout.java b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/SegmentTabLayout.java new file mode 100644 index 0000000..314e20b --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/SegmentTabLayout.java @@ -0,0 +1,776 @@ +package com.flyco.tablayout; + +import android.animation.TypeEvaluator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.GradientDrawable; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.OvershootInterpolator; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; + +import com.flyco.tablayout.listener.OnTabSelectListener; +import com.flyco.tablayout.utils.FragmentChangeManager; +import com.flyco.tablayout.utils.UnreadMsgUtils; +import com.flyco.tablayout.widget.MsgView; + +import java.util.ArrayList; + +public class SegmentTabLayout extends FrameLayout implements ValueAnimator.AnimatorUpdateListener { + private Context mContext; + private String[] mTitles; + private LinearLayout mTabsContainer; + private int mCurrentTab; + private int mLastTab; + private int mTabCount; + /** + * 用于绘制显示器 + */ + private Rect mIndicatorRect = new Rect(); + private GradientDrawable mIndicatorDrawable = new GradientDrawable(); + private GradientDrawable mRectDrawable = new GradientDrawable(); + + private Paint mDividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private float mTabPadding; + private boolean mTabSpaceEqual; + private float mTabWidth; + + /** + * indicator + */ + private int mIndicatorColor; + private float mIndicatorHeight; + private float mIndicatorCornerRadius; + private float mIndicatorMarginLeft; + private float mIndicatorMarginTop; + private float mIndicatorMarginRight; + private float mIndicatorMarginBottom; + private long mIndicatorAnimDuration; + private boolean mIndicatorAnimEnable; + private boolean mIndicatorBounceEnable; + + /** + * divider + */ + private int mDividerColor; + private float mDividerWidth; + private float mDividerPadding; + + /** + * title + */ + private static final int TEXT_BOLD_NONE = 0; + private static final int TEXT_BOLD_WHEN_SELECT = 1; + private static final int TEXT_BOLD_BOTH = 2; + private float mTextsize; + private int mTextSelectColor; + private int mTextUnselectColor; + private int mTextBold; + private boolean mTextAllCaps; + + private int mBarColor; + private int mBarStrokeColor; + private float mBarStrokeWidth; + + private int mHeight; + + /** + * anim + */ + private ValueAnimator mValueAnimator; + private OvershootInterpolator mInterpolator = new OvershootInterpolator(0.8f); + + private FragmentChangeManager mFragmentChangeManager; + private float[] mRadiusArr = new float[8]; + + public SegmentTabLayout(Context context) { + this(context, null, 0); + } + + public SegmentTabLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SegmentTabLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setWillNotDraw(false);//重写onDraw方法,需要调用这个方法来清除flag + setClipChildren(false); + setClipToPadding(false); + + this.mContext = context; + mTabsContainer = new LinearLayout(context); + addView(mTabsContainer); + + obtainAttributes(context, attrs); + + //get layout_height + String height = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "layout_height"); + + //create ViewPager + if (height.equals(ViewGroup.LayoutParams.MATCH_PARENT + "")) { + } else if (height.equals(ViewGroup.LayoutParams.WRAP_CONTENT + "")) { + } else { + int[] systemAttrs = {android.R.attr.layout_height}; + TypedArray a = context.obtainStyledAttributes(attrs, systemAttrs); + mHeight = a.getDimensionPixelSize(0, ViewGroup.LayoutParams.WRAP_CONTENT); + a.recycle(); + } + + mValueAnimator = ValueAnimator.ofObject(new PointEvaluator(), mLastP, mCurrentP); + mValueAnimator.addUpdateListener(this); + } + + private void obtainAttributes(Context context, AttributeSet attrs) { + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SegmentTabLayout); + + mIndicatorColor = ta.getColor(R.styleable.SegmentTabLayout_tl_indicator_color, Color.parseColor("#222831")); + mIndicatorHeight = ta.getDimension(R.styleable.SegmentTabLayout_tl_indicator_height, -1); + mIndicatorCornerRadius = ta.getDimension(R.styleable.SegmentTabLayout_tl_indicator_corner_radius, -1); + mIndicatorMarginLeft = ta.getDimension(R.styleable.SegmentTabLayout_tl_indicator_margin_left, dp2px(0)); + mIndicatorMarginTop = ta.getDimension(R.styleable.SegmentTabLayout_tl_indicator_margin_top, 0); + mIndicatorMarginRight = ta.getDimension(R.styleable.SegmentTabLayout_tl_indicator_margin_right, dp2px(0)); + mIndicatorMarginBottom = ta.getDimension(R.styleable.SegmentTabLayout_tl_indicator_margin_bottom, 0); + mIndicatorAnimEnable = ta.getBoolean(R.styleable.SegmentTabLayout_tl_indicator_anim_enable, false); + mIndicatorBounceEnable = ta.getBoolean(R.styleable.SegmentTabLayout_tl_indicator_bounce_enable, true); + mIndicatorAnimDuration = ta.getInt(R.styleable.SegmentTabLayout_tl_indicator_anim_duration, -1); + + mDividerColor = ta.getColor(R.styleable.SegmentTabLayout_tl_divider_color, mIndicatorColor); + mDividerWidth = ta.getDimension(R.styleable.SegmentTabLayout_tl_divider_width, dp2px(1)); + mDividerPadding = ta.getDimension(R.styleable.SegmentTabLayout_tl_divider_padding, 0); + + mTextsize = ta.getDimension(R.styleable.SegmentTabLayout_tl_textSize, sp2px(13f)); + mTextSelectColor = ta.getColor(R.styleable.SegmentTabLayout_tl_textSelectColor, Color.parseColor("#ffffff")); + mTextUnselectColor = ta.getColor(R.styleable.SegmentTabLayout_tl_textUnSelectColor, mIndicatorColor); + mTextBold = ta.getInt(R.styleable.SegmentTabLayout_tl_textBold, TEXT_BOLD_NONE); + mTextAllCaps = ta.getBoolean(R.styleable.SegmentTabLayout_tl_textAllCaps, false); + + mTabSpaceEqual = ta.getBoolean(R.styleable.SegmentTabLayout_tl_tab_space_equal, true); + mTabWidth = ta.getDimension(R.styleable.SegmentTabLayout_tl_tab_width, dp2px(-1)); + mTabPadding = ta.getDimension(R.styleable.SegmentTabLayout_tl_tab_padding, mTabSpaceEqual || mTabWidth > 0 ? dp2px(0) : dp2px(10)); + + mBarColor = ta.getColor(R.styleable.SegmentTabLayout_tl_bar_color, Color.TRANSPARENT); + mBarStrokeColor = ta.getColor(R.styleable.SegmentTabLayout_tl_bar_stroke_color, mIndicatorColor); + mBarStrokeWidth = ta.getDimension(R.styleable.SegmentTabLayout_tl_bar_stroke_width, dp2px(1)); + + ta.recycle(); + } + + public void setTabData(String[] titles) { + if (titles == null || titles.length == 0) { + throw new IllegalStateException("Titles can not be NULL or EMPTY !"); + } + + this.mTitles = titles; + + notifyDataSetChanged(); + } + + /** + * 关联数据支持同时切换fragments + */ + public void setTabData(String[] titles, FragmentActivity fa, int containerViewId, ArrayList fragments) { + mFragmentChangeManager = new FragmentChangeManager(fa.getSupportFragmentManager(), containerViewId, fragments); + setTabData(titles); + } + + /** + * 更新数据 + */ + public void notifyDataSetChanged() { + mTabsContainer.removeAllViews(); + this.mTabCount = mTitles.length; + View tabView; + for (int i = 0; i < mTabCount; i++) { + tabView = View.inflate(mContext, R.layout.layout_tab_segment, null); + tabView.setTag(i); + addTab(i, tabView); + } + + updateTabStyles(); + } + + /** + * 创建并添加tab + */ + private void addTab(final int position, View tabView) { + TextView tv_tab_title = tabView.findViewById(R.id.tv_tab_title); + tv_tab_title.setText(mTitles[position]); + + tabView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + int position = (Integer) v.getTag(); + if (mCurrentTab != position) { + setCurrentTab(position); + if (mListener != null) { + mListener.onTabSelect(position); + } + } else { + if (mListener != null) { + mListener.onTabReselect(position); + } + } + } + }); + + /** 每一个Tab的布局参数 */ + LinearLayout.LayoutParams lp_tab = mTabSpaceEqual ? + new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f) : + new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); + if (mTabWidth > 0) { + lp_tab = new LinearLayout.LayoutParams((int) mTabWidth, LayoutParams.MATCH_PARENT); + } + mTabsContainer.addView(tabView, position, lp_tab); + } + + private void updateTabStyles() { + for (int i = 0; i < mTabCount; i++) { + View tabView = mTabsContainer.getChildAt(i); + tabView.setPadding((int) mTabPadding, 0, (int) mTabPadding, 0); + TextView tv_tab_title = tabView.findViewById(R.id.tv_tab_title); + tv_tab_title.setTextColor(i == mCurrentTab ? mTextSelectColor : mTextUnselectColor); + tv_tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextsize); +// tv_tab_title.setPadding((int) mTabPadding, 0, (int) mTabPadding, 0); + if (mTextAllCaps) { + tv_tab_title.setText(tv_tab_title.getText().toString().toUpperCase()); + } + + if (mTextBold == TEXT_BOLD_BOTH) { + tv_tab_title.getPaint().setFakeBoldText(true); + } + // 被选中设置为粗体 + else if (mTextBold == TEXT_BOLD_WHEN_SELECT && i == mCurrentTab) { + tv_tab_title.getPaint().setFakeBoldText(true); + } else if (mTextBold == TEXT_BOLD_NONE) { + tv_tab_title.getPaint().setFakeBoldText(false); + } + } + } + + private void updateTabSelection(int position) { + for (int i = 0; i < mTabCount; ++i) { + View tabView = mTabsContainer.getChildAt(i); + final boolean isSelect = i == position; + TextView tab_title = tabView.findViewById(R.id.tv_tab_title); + tab_title.setTextColor(isSelect ? mTextSelectColor : mTextUnselectColor); + if (mTextBold == TEXT_BOLD_WHEN_SELECT) { + tab_title.getPaint().setFakeBoldText(isSelect); + } + } + } + + private void calcOffset() { + final View currentTabView = mTabsContainer.getChildAt(this.mCurrentTab); + mCurrentP.left = currentTabView.getLeft(); + mCurrentP.right = currentTabView.getRight(); + + final View lastTabView = mTabsContainer.getChildAt(this.mLastTab); + mLastP.left = lastTabView.getLeft(); + mLastP.right = lastTabView.getRight(); + +// Log.d("AAA", "mLastP--->" + mLastP.left + "&" + mLastP.right); +// Log.d("AAA", "mCurrentP--->" + mCurrentP.left + "&" + mCurrentP.right); + if (mLastP.left == mCurrentP.left && mLastP.right == mCurrentP.right) { + invalidate(); + } else { + mValueAnimator.setObjectValues(mLastP, mCurrentP); + if (mIndicatorBounceEnable) { + mValueAnimator.setInterpolator(mInterpolator); + } + + if (mIndicatorAnimDuration < 0) { + mIndicatorAnimDuration = mIndicatorBounceEnable ? 500 : 250; + } + mValueAnimator.setDuration(mIndicatorAnimDuration); + mValueAnimator.start(); + } + } + + private void calcIndicatorRect() { + View currentTabView = mTabsContainer.getChildAt(this.mCurrentTab); + float left = currentTabView.getLeft(); + float right = currentTabView.getRight(); + + mIndicatorRect.left = (int) left; + mIndicatorRect.right = (int) right; + + if (!mIndicatorAnimEnable) { + if (mCurrentTab == 0) { + /**The corners are ordered top-left, top-right, bottom-right, bottom-left*/ + mRadiusArr[0] = mIndicatorCornerRadius; + mRadiusArr[1] = mIndicatorCornerRadius; + mRadiusArr[2] = 0; + mRadiusArr[3] = 0; + mRadiusArr[4] = 0; + mRadiusArr[5] = 0; + mRadiusArr[6] = mIndicatorCornerRadius; + mRadiusArr[7] = mIndicatorCornerRadius; + } else if (mCurrentTab == mTabCount - 1) { + /**The corners are ordered top-left, top-right, bottom-right, bottom-left*/ + mRadiusArr[0] = 0; + mRadiusArr[1] = 0; + mRadiusArr[2] = mIndicatorCornerRadius; + mRadiusArr[3] = mIndicatorCornerRadius; + mRadiusArr[4] = mIndicatorCornerRadius; + mRadiusArr[5] = mIndicatorCornerRadius; + mRadiusArr[6] = 0; + mRadiusArr[7] = 0; + } else { + /**The corners are ordered top-left, top-right, bottom-right, bottom-left*/ + mRadiusArr[0] = 0; + mRadiusArr[1] = 0; + mRadiusArr[2] = 0; + mRadiusArr[3] = 0; + mRadiusArr[4] = 0; + mRadiusArr[5] = 0; + mRadiusArr[6] = 0; + mRadiusArr[7] = 0; + } + } else { + /**The corners are ordered top-left, top-right, bottom-right, bottom-left*/ + mRadiusArr[0] = mIndicatorCornerRadius; + mRadiusArr[1] = mIndicatorCornerRadius; + mRadiusArr[2] = mIndicatorCornerRadius; + mRadiusArr[3] = mIndicatorCornerRadius; + mRadiusArr[4] = mIndicatorCornerRadius; + mRadiusArr[5] = mIndicatorCornerRadius; + mRadiusArr[6] = mIndicatorCornerRadius; + mRadiusArr[7] = mIndicatorCornerRadius; + } + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + IndicatorPoint p = (IndicatorPoint) animation.getAnimatedValue(); + mIndicatorRect.left = (int) p.left; + mIndicatorRect.right = (int) p.right; + invalidate(); + } + + private boolean mIsFirstDraw = true; + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (isInEditMode() || mTabCount <= 0) { + return; + } + + int height = getHeight(); + int paddingLeft = getPaddingLeft(); + + if (mIndicatorHeight < 0) { + mIndicatorHeight = height - mIndicatorMarginTop - mIndicatorMarginBottom; + } + + if (mIndicatorCornerRadius < 0 || mIndicatorCornerRadius > mIndicatorHeight / 2) { + mIndicatorCornerRadius = mIndicatorHeight / 2; + } + + //draw rect + mRectDrawable.setColor(mBarColor); + mRectDrawable.setStroke((int) mBarStrokeWidth, mBarStrokeColor); + mRectDrawable.setCornerRadius(mIndicatorCornerRadius); + mRectDrawable.setBounds(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom()); + mRectDrawable.draw(canvas); + + // draw divider + if (!mIndicatorAnimEnable && mDividerWidth > 0) { + mDividerPaint.setStrokeWidth(mDividerWidth); + mDividerPaint.setColor(mDividerColor); + for (int i = 0; i < mTabCount - 1; i++) { + View tab = mTabsContainer.getChildAt(i); + canvas.drawLine(paddingLeft + tab.getRight(), mDividerPadding, paddingLeft + tab.getRight(), height - mDividerPadding, mDividerPaint); + } + } + + + //draw indicator line + if (mIndicatorAnimEnable) { + if (mIsFirstDraw) { + mIsFirstDraw = false; + calcIndicatorRect(); + } + } else { + calcIndicatorRect(); + } + + mIndicatorDrawable.setColor(mIndicatorColor); + mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left, + (int) mIndicatorMarginTop, (int) (paddingLeft + mIndicatorRect.right - mIndicatorMarginRight), + (int) (mIndicatorMarginTop + mIndicatorHeight)); + mIndicatorDrawable.setCornerRadii(mRadiusArr); + mIndicatorDrawable.draw(canvas); + + } + + //setter and getter + public void setCurrentTab(int currentTab) { + mLastTab = this.mCurrentTab; + this.mCurrentTab = currentTab; + updateTabSelection(currentTab); + if (mFragmentChangeManager != null) { + mFragmentChangeManager.setFragments(currentTab); + } + if (mIndicatorAnimEnable) { + calcOffset(); + } else { + invalidate(); + } + } + + public void setTabPadding(float tabPadding) { + this.mTabPadding = dp2px(tabPadding); + updateTabStyles(); + } + + public void setTabSpaceEqual(boolean tabSpaceEqual) { + this.mTabSpaceEqual = tabSpaceEqual; + updateTabStyles(); + } + + public void setTabWidth(float tabWidth) { + this.mTabWidth = dp2px(tabWidth); + updateTabStyles(); + } + + public void setIndicatorColor(int indicatorColor) { + this.mIndicatorColor = indicatorColor; + invalidate(); + } + + public void setIndicatorHeight(float indicatorHeight) { + this.mIndicatorHeight = dp2px(indicatorHeight); + invalidate(); + } + + public void setIndicatorCornerRadius(float indicatorCornerRadius) { + this.mIndicatorCornerRadius = dp2px(indicatorCornerRadius); + invalidate(); + } + + public void setIndicatorMargin(float indicatorMarginLeft, float indicatorMarginTop, + float indicatorMarginRight, float indicatorMarginBottom) { + this.mIndicatorMarginLeft = dp2px(indicatorMarginLeft); + this.mIndicatorMarginTop = dp2px(indicatorMarginTop); + this.mIndicatorMarginRight = dp2px(indicatorMarginRight); + this.mIndicatorMarginBottom = dp2px(indicatorMarginBottom); + invalidate(); + } + + public void setIndicatorAnimDuration(long indicatorAnimDuration) { + this.mIndicatorAnimDuration = indicatorAnimDuration; + } + + public void setIndicatorAnimEnable(boolean indicatorAnimEnable) { + this.mIndicatorAnimEnable = indicatorAnimEnable; + } + + public void setIndicatorBounceEnable(boolean indicatorBounceEnable) { + this.mIndicatorBounceEnable = indicatorBounceEnable; + } + + public void setDividerColor(int dividerColor) { + this.mDividerColor = dividerColor; + invalidate(); + } + + public void setDividerWidth(float dividerWidth) { + this.mDividerWidth = dp2px(dividerWidth); + invalidate(); + } + + public void setDividerPadding(float dividerPadding) { + this.mDividerPadding = dp2px(dividerPadding); + invalidate(); + } + + public void setTextsize(float textsize) { + this.mTextsize = sp2px(textsize); + updateTabStyles(); + } + + public void setTextSelectColor(int textSelectColor) { + this.mTextSelectColor = textSelectColor; + updateTabStyles(); + } + + public void setTextUnselectColor(int textUnselectColor) { + this.mTextUnselectColor = textUnselectColor; + updateTabStyles(); + } + + public void setTextBold(int textBold) { + this.mTextBold = textBold; + updateTabStyles(); + } + + public void setTextAllCaps(boolean textAllCaps) { + this.mTextAllCaps = textAllCaps; + updateTabStyles(); + } + + public int getTabCount() { + return mTabCount; + } + + public int getCurrentTab() { + return mCurrentTab; + } + + public float getTabPadding() { + return mTabPadding; + } + + public boolean isTabSpaceEqual() { + return mTabSpaceEqual; + } + + public float getTabWidth() { + return mTabWidth; + } + + public int getIndicatorColor() { + return mIndicatorColor; + } + + public float getIndicatorHeight() { + return mIndicatorHeight; + } + + public float getIndicatorCornerRadius() { + return mIndicatorCornerRadius; + } + + public float getIndicatorMarginLeft() { + return mIndicatorMarginLeft; + } + + public float getIndicatorMarginTop() { + return mIndicatorMarginTop; + } + + public float getIndicatorMarginRight() { + return mIndicatorMarginRight; + } + + public float getIndicatorMarginBottom() { + return mIndicatorMarginBottom; + } + + public long getIndicatorAnimDuration() { + return mIndicatorAnimDuration; + } + + public boolean isIndicatorAnimEnable() { + return mIndicatorAnimEnable; + } + + public boolean isIndicatorBounceEnable() { + return mIndicatorBounceEnable; + } + + public int getDividerColor() { + return mDividerColor; + } + + public float getDividerWidth() { + return mDividerWidth; + } + + public float getDividerPadding() { + return mDividerPadding; + } + + public float getTextsize() { + return mTextsize; + } + + public int getTextSelectColor() { + return mTextSelectColor; + } + + public int getTextUnselectColor() { + return mTextUnselectColor; + } + + public int getTextBold() { + return mTextBold; + } + + public boolean isTextAllCaps() { + return mTextAllCaps; + } + + public TextView getTitleView(int tab) { + View tabView = mTabsContainer.getChildAt(tab); + TextView tv_tab_title = tabView.findViewById(R.id.tv_tab_title); + return tv_tab_title; + } + + //setter and getter + // show MsgTipView + private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private SparseArray mInitSetMap = new SparseArray<>(); + + /** + * 显示未读消息 + * + * @param position 显示tab位置 + * @param num num小于等于0显示红点,num大于0显示数字 + */ + public void showMsg(int position, int num) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + + View tabView = mTabsContainer.getChildAt(position); + MsgView tipView = tabView.findViewById(R.id.rtv_msg_tip); + if (tipView != null) { + UnreadMsgUtils.show(tipView, num); + + if (mInitSetMap.get(position) != null && mInitSetMap.get(position)) { + return; + } + + setMsgMargin(position, 2, 2); + + mInitSetMap.put(position, true); + } + } + + /** + * 显示未读红点 + * + * @param position 显示tab位置 + */ + public void showDot(int position) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + showMsg(position, 0); + } + + public void hideMsg(int position) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + + View tabView = mTabsContainer.getChildAt(position); + MsgView tipView = tabView.findViewById(R.id.rtv_msg_tip); + if (tipView != null) { + tipView.setVisibility(View.GONE); + } + } + + /** + * 设置提示红点偏移,注意 + * 1.控件为固定高度:参照点为tab内容的右上角 + * 2.控件高度不固定(WRAP_CONTENT):参照点为tab内容的右上角,此时高度已是红点的最高显示范围,所以这时bottomPadding其实就是topPadding + */ + public void setMsgMargin(int position, float leftPadding, float bottomPadding) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + View tabView = mTabsContainer.getChildAt(position); + MsgView tipView = tabView.findViewById(R.id.rtv_msg_tip); + if (tipView != null) { + TextView tv_tab_title = tabView.findViewById(R.id.tv_tab_title); + mTextPaint.setTextSize(mTextsize); + float textWidth = mTextPaint.measureText(tv_tab_title.getText().toString()); + float textHeight = mTextPaint.descent() - mTextPaint.ascent(); + MarginLayoutParams lp = (MarginLayoutParams) tipView.getLayoutParams(); + + lp.leftMargin = dp2px(leftPadding); + lp.topMargin = mHeight > 0 ? (int) (mHeight - textHeight) / 2 - dp2px(bottomPadding) : dp2px(bottomPadding); + + tipView.setLayoutParams(lp); + } + } + + /** + * 当前类只提供了少许设置未读消息属性的方法,可以通过该方法获取MsgView对象从而各种设置 + */ + public MsgView getMsgView(int position) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + View tabView = mTabsContainer.getChildAt(position); + MsgView tipView = tabView.findViewById(R.id.rtv_msg_tip); + return tipView; + } + + private OnTabSelectListener mListener; + + public void setOnTabSelectListener(OnTabSelectListener listener) { + this.mListener = listener; + } + + @Override + protected Parcelable onSaveInstanceState() { + Bundle bundle = new Bundle(); + bundle.putParcelable("instanceState", super.onSaveInstanceState()); + bundle.putInt("mCurrentTab", mCurrentTab); + return bundle; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (state instanceof Bundle) { + Bundle bundle = (Bundle) state; + mCurrentTab = bundle.getInt("mCurrentTab"); + state = bundle.getParcelable("instanceState"); + if (mCurrentTab != 0 && mTabsContainer.getChildCount() > 0) { + updateTabSelection(mCurrentTab); + } + } + super.onRestoreInstanceState(state); + } + + class IndicatorPoint { + public float left; + public float right; + } + + private IndicatorPoint mCurrentP = new IndicatorPoint(); + private IndicatorPoint mLastP = new IndicatorPoint(); + + class PointEvaluator implements TypeEvaluator { + @Override + public IndicatorPoint evaluate(float fraction, IndicatorPoint startValue, IndicatorPoint endValue) { + float left = startValue.left + fraction * (endValue.left - startValue.left); + float right = startValue.right + fraction * (endValue.right - startValue.right); + IndicatorPoint point = new IndicatorPoint(); + point.left = left; + point.right = right; + return point; + } + } + + protected int dp2px(float dp) { + final float scale = mContext.getResources().getDisplayMetrics().density; + return (int) (dp * scale + 0.5f); + } + + protected int sp2px(float sp) { + final float scale = this.mContext.getResources().getDisplayMetrics().scaledDensity; + return (int) (sp * scale + 0.5f); + } +} diff --git a/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/SlidingScaleTabLayout.java b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/SlidingScaleTabLayout.java new file mode 100644 index 0000000..069b132 --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/SlidingScaleTabLayout.java @@ -0,0 +1,1216 @@ +package com.flyco.tablayout; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.drawable.GradientDrawable; +import android.os.Bundle; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.SparseBooleanArray; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.HorizontalScrollView; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; + +import com.flyco.tablayout.listener.OnTabSelectListener; +import com.flyco.tablayout.transformer.ExtendTransformer; +import com.flyco.tablayout.transformer.ITabScaleTransformer; +import com.flyco.tablayout.transformer.IViewPagerTransformer; +import com.flyco.tablayout.transformer.TabScaleTransformer; +import com.flyco.tablayout.utils.UnreadMsgUtils; +import com.flyco.tablayout.utils.ViewUtils; +import com.flyco.tablayout.widget.MsgView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 滑动切换TabLayout,tab的文字大小会发生变化 + */ +public class SlidingScaleTabLayout extends HorizontalScrollView implements ViewPager.OnPageChangeListener { + + private static final int TOP = 0; + private static final int BOTTOM = 1; + private static final int CENTER = 2; + private static final int LEFT = 0; + private static final int RIGHT = 1; + + private Context mContext; + private ViewPager mViewPager; + private ArrayList mTitles; + private LinearLayout mTabsContainer; + private int mCurrentTab; + private float mCurrentPositionOffset; + private int mTabCount; + /** + * 用于绘制显示器 + */ + private Rect mIndicatorRect = new Rect(); + /** + * 用于实现滚动居中 + */ + private Rect mTabRect = new Rect(); + private GradientDrawable mIndicatorDrawable = new GradientDrawable(); + + private Paint mRectPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Paint mDividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Paint mTrianglePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Path mTrianglePath = new Path(); + private static final int STYLE_NORMAL = 0; + private static final int STYLE_TRIANGLE = 1; + private static final int STYLE_BLOCK = 2; + private int mIndicatorStyle = STYLE_NORMAL; + + private float mTabPadding; + private boolean mTabSpaceEqual; + private float mTabWidth; + + /** + * indicator + */ + private int mIndicatorColor; + private float mIndicatorHeight; + private float mIndicatorWidth; + private float mIndicatorCornerRadius; + private float mIndicatorMarginLeft; + private float mIndicatorMarginTop; + private float mIndicatorMarginRight; + private float mIndicatorMarginBottom; + private int mIndicatorGravity; + private boolean mIndicatorWidthEqualTitle; + + /** + * underline + */ + private int mUnderlineColor; + private float mUnderlineHeight; + private int mUnderlineGravity; + + /** + * divider + */ + private int mDividerColor; + private float mDividerWidth; + private float mDividerPadding; + + /** + * title + */ + private static final int TEXT_BOLD_NONE = 0; + private static final int TEXT_BOLD_WHEN_SELECT = 1; + private static final int TEXT_BOLD_BOTH = 2; + private float mTextSelectSize; + private float mTextUnSelectSize; + private int mTextSelectColor; + private int mTextUnSelectColor; + private int mTextBold; + private boolean mTextAllCaps; + + private int mLastScrollX; + private int mHeight; + private boolean mSnapOnTabClick; + + /** + * tab的上下间距 + */ + private int mTabMarginTop; + private int mTabMarginBottom; + + private int mTabMsgMarginTop; + private int mTabMsgMarginRight; + private int mTabDotMarginTop; + private int mTabDotMarginRight; + private int mTabBackgroundId; + + private boolean openDmg = true; + + /** + * tab中的内容的位置 + */ + private int mTabHorizontalGravity; + + private int mTabVerticalGravity; + + private ITabScaleTransformer iTabScaleTransformer; + + private ExtendTransformer extendTransformer; + + public SlidingScaleTabLayout(Context context) { + this(context, null, 0); + } + + public SlidingScaleTabLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SlidingScaleTabLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setFillViewport(true);//设置滚动视图是否可以伸缩其内容以填充视口 + setWillNotDraw(false);//重写onDraw方法,需要调用这个方法来清除flag + setClipChildren(false); + setClipToPadding(false); + + this.mContext = context; + mTabsContainer = new LinearLayout(context); + addView(mTabsContainer); + + obtainAttributes(context, attrs); + + //get layout_height + String height = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "layout_height"); + + if (height.equals(ViewGroup.LayoutParams.MATCH_PARENT + "")) { + } else if (height.equals(ViewGroup.LayoutParams.WRAP_CONTENT + "")) { + } else { + int[] systemAttrs = {android.R.attr.layout_height}; + TypedArray a = context.obtainStyledAttributes(attrs, systemAttrs); + mHeight = a.getDimensionPixelSize(0, ViewGroup.LayoutParams.WRAP_CONTENT); + a.recycle(); + } + } + + private void obtainAttributes(Context context, AttributeSet attrs) { + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlidingScaleTabLayout); + + mIndicatorStyle = ta.getInt(R.styleable.SlidingScaleTabLayout_tl_indicator_style, STYLE_NORMAL); + mIndicatorColor = ta.getColor(R.styleable.SlidingScaleTabLayout_tl_indicator_color, Color.parseColor(mIndicatorStyle == STYLE_BLOCK ? "#4B6A87" : "#ffffff")); + mIndicatorHeight = ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_indicator_height, + dp2px(mIndicatorStyle == STYLE_TRIANGLE ? 4 : (mIndicatorStyle == STYLE_BLOCK ? -1 : 2))); + mIndicatorWidth = ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_indicator_width, dp2px(mIndicatorStyle == STYLE_TRIANGLE ? 10 : -1)); + mIndicatorCornerRadius = ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_indicator_corner_radius, dp2px(mIndicatorStyle == STYLE_BLOCK ? -1 : 0)); + mIndicatorMarginLeft = ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_indicator_margin_left, dp2px(0)); + mIndicatorMarginTop = ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_indicator_margin_top, dp2px(mIndicatorStyle == STYLE_BLOCK ? 7 : 0)); + mIndicatorMarginRight = ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_indicator_margin_right, dp2px(0)); + mIndicatorMarginBottom = ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_indicator_margin_bottom, dp2px(mIndicatorStyle == STYLE_BLOCK ? 7 : 0)); + mIndicatorGravity = ta.getInt(R.styleable.SlidingScaleTabLayout_tl_indicator_gravity, Gravity.BOTTOM); + mIndicatorWidthEqualTitle = ta.getBoolean(R.styleable.SlidingScaleTabLayout_tl_indicator_width_equal_title, false); + + mUnderlineColor = ta.getColor(R.styleable.SlidingScaleTabLayout_tl_underline_color, Color.parseColor("#ffffff")); + mUnderlineHeight = ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_underline_height, dp2px(0)); + mUnderlineGravity = ta.getInt(R.styleable.SlidingScaleTabLayout_tl_underline_gravity, Gravity.BOTTOM); + + mDividerColor = ta.getColor(R.styleable.SlidingScaleTabLayout_tl_divider_color, Color.parseColor("#ffffff")); + mDividerWidth = ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_divider_width, dp2px(0)); + mDividerPadding = ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_divider_padding, dp2px(12)); + + mTextUnSelectSize = ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_textUnSelectSize, sp2px(14)); + // 被选中的文字大小,默认额未选中的大小一样 + mTextSelectSize = ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_textSelectSize, mTextUnSelectSize); + + mTextSelectColor = ta.getColor(R.styleable.SlidingScaleTabLayout_tl_textSelectColor, Color.parseColor("#ffffff")); + mTextUnSelectColor = ta.getColor(R.styleable.SlidingScaleTabLayout_tl_textUnSelectColor, Color.parseColor("#AAffffff")); + mTextBold = ta.getInt(R.styleable.SlidingScaleTabLayout_tl_textBold, TEXT_BOLD_NONE); + mTextAllCaps = ta.getBoolean(R.styleable.SlidingScaleTabLayout_tl_textAllCaps, false); + + mTabSpaceEqual = ta.getBoolean(R.styleable.SlidingScaleTabLayout_tl_tab_space_equal, false); + mTabWidth = ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_tab_width, dp2px(-1)); + mTabPadding = ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_tab_padding, mTabSpaceEqual || mTabWidth > 0 ? dp2px(0) : dp2px(20)); + // 得到设置的上下间距和gravity + mTabMarginTop = ta.getDimensionPixelSize(R.styleable.SlidingScaleTabLayout_tl_tab_marginTop, 0); + mTabMarginBottom = ta.getDimensionPixelSize(R.styleable.SlidingScaleTabLayout_tl_tab_marginBottom, 0); + + mTabHorizontalGravity = ta.getInt(R.styleable.SlidingScaleTabLayout_tl_tab_horizontal_gravity, CENTER); + mTabVerticalGravity = ta.getInt(R.styleable.SlidingScaleTabLayout_tl_tab_vertical_gravity, CENTER); + + mTabMsgMarginTop = (int) ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_tab_msg_marginTop, 0f); + mTabMsgMarginRight = (int) ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_tab_msg_marginRight, 0f); + + mTabDotMarginTop = (int) ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_tab_dot_marginTop, 0f); + mTabDotMarginRight = (int) ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_tab_dot_marginRight, 0f); + mTabBackgroundId = ta.getResourceId(R.styleable.SlidingScaleTabLayout_tl_tab_background, 0); + openDmg = ta.getBoolean(R.styleable.SlidingScaleTabLayout_tl_openTextDmg, false); + ta.recycle(); + + iTabScaleTransformer = new TabScaleTransformer(this, mTextSelectSize, mTextUnSelectSize, openDmg); + } + + /** + * 关联ViewPager + */ + public void setViewPager(ViewPager vp) { + if (vp == null || vp.getAdapter() == null) { + throw new IllegalStateException("ViewPager or ViewPager adapter can not be NULL !"); + } + + this.mViewPager = vp; + + initViewPagerListener(); + } + + /** + * 设置标题,不关联ViewPager + */ + public void setTitle(String[] titles) { + mTitles = new ArrayList<>(); + Collections.addAll(mTitles, titles); + initViewPagerListener(); + } + + + /** + * 关联ViewPager,用于不想在ViewPager适配器中设置titles数据的情况 + */ + public void setViewPager(ViewPager vp, String[] titles) { + if (vp == null || vp.getAdapter() == null) { + throw new IllegalStateException("ViewPager or ViewPager adapter can not be NULL !"); + } + + if (titles == null || titles.length == 0) { + throw new IllegalStateException("Titles can not be EMPTY !"); + } + + if (titles.length != vp.getAdapter().getCount()) { + throw new IllegalStateException("Titles length must be the same as the page count !"); + } + + this.mViewPager = vp; + mTitles = new ArrayList<>(); + Collections.addAll(mTitles, titles); + + initViewPagerListener(); + } + + /** + * 关联ViewPager,用于连适配器都不想自己实例化的情况 + */ + public void setViewPager(ViewPager vp, String[] titles, FragmentActivity fa, ArrayList fragments) { + if (vp == null) { + throw new IllegalStateException("ViewPager can not be NULL !"); + } + + if (titles == null || titles.length == 0) { + throw new IllegalStateException("Titles can not be EMPTY !"); + } + + this.mViewPager = vp; + this.mViewPager.setAdapter(new InnerPagerAdapter(fa.getSupportFragmentManager(), fragments, titles)); + initViewPagerListener(); + } + + private void initViewPagerListener() { + if (this.mViewPager != null) { + this.mViewPager.removeOnPageChangeListener(this); + this.mViewPager.addOnPageChangeListener(this); + initTransformer(); + } + notifyDataSetChanged(); + } + + private void initTransformer() { + // 如果选中状态的文字大小和未选中状态的文字大小是不同的,开启缩放 + if (mTextUnSelectSize != mTextSelectSize) { + extendTransformer = new ExtendTransformer(); + this.mViewPager.setPageTransformer(true, extendTransformer); + } + } + + /** + * 更新数据 + */ + public void notifyDataSetChanged() { + mTabsContainer.removeAllViews(); + this.mTabCount = mTitles == null ? mViewPager.getAdapter().getCount() : mTitles.size(); + View tabView; + for (int i = 0; i < mTabCount; i++) { + tabView = LayoutInflater.from(mContext).inflate(R.layout.layout_scale_tab, mTabsContainer, false); + TextView title = tabView.findViewById(R.id.tv_tab_title); + // 设置tab的位置信息 + setTabLayoutParams(title); + CharSequence pageTitle = mTitles == null ? mViewPager.getAdapter().getPageTitle(i) : mTitles.get(i); + addTab(i, pageTitle.toString(), tabView); + } + + updateTabStyles(); + } + + private void setTabLayoutParams(TextView title) { + RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) title.getLayoutParams(); + params.topMargin = mTabMarginTop; + params.bottomMargin = mTabMarginBottom; + + if (mTabVerticalGravity == TOP) { + params.addRule(RelativeLayout.ALIGN_PARENT_TOP); + } else if (mTabVerticalGravity == BOTTOM) { + params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + } else { + params.addRule(RelativeLayout.CENTER_VERTICAL); + } + + if (mTabHorizontalGravity == LEFT) { + params.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + } else if (mTabHorizontalGravity == RIGHT) { + params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + } else { + params.addRule(RelativeLayout.CENTER_HORIZONTAL); + } + + + title.setLayoutParams(params); + + if (isDmgOpen()) { + ImageView imageView = (ImageView) ViewUtils.findBrotherView(title, R.id.tv_tab_title_dmg, 3); + if (imageView == null) return; + params = (RelativeLayout.LayoutParams) imageView.getLayoutParams(); + params.topMargin = mTabMarginTop; + params.bottomMargin = mTabMarginBottom; + // 调整镜像的问题 + if (mTabVerticalGravity == TOP) { + params.addRule(RelativeLayout.ALIGN_PARENT_TOP); + imageView.setScaleType(ImageView.ScaleType.FIT_START); + } else if (mTabVerticalGravity == BOTTOM) { + params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + imageView.setScaleType(ImageView.ScaleType.FIT_END); + } else { + params.addRule(RelativeLayout.CENTER_VERTICAL); + imageView.setScaleType(ImageView.ScaleType.FIT_CENTER); + } + + if (mTabHorizontalGravity == LEFT) { + params.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + } else if (mTabHorizontalGravity == RIGHT) { + params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + } else { + params.addRule(RelativeLayout.CENTER_HORIZONTAL); + } + + imageView.setLayoutParams(params); + } + + } + + /** + * 如果文字的大小没有变化,不需要开启镜像,请注意 + */ + private boolean isDmgOpen() { + return openDmg && mTextSelectSize != mTextUnSelectSize; + } + + /** + * 创建并添加tab + */ + private void addTab(final int position, String title, View tabView) { + TextView tv_tab_title = tabView.findViewById(R.id.tv_tab_title); + if (tv_tab_title != null) { +// tv_tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, position == mCurrentTab ? mTextSelectSize : mTextUnSelectSize); + tv_tab_title.setText(title); + // 设置tab背景 + if (mTabBackgroundId != 0) { + tv_tab_title.setBackgroundResource(mTabBackgroundId); + } +// if (TextUtils.isEmpty(title)) { +// tabView.setVisibility(View.GONE); +// } else { +// tabView.setVisibility(View.VISIBLE); +// } + } + + tabView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + int position = mTabsContainer.indexOfChild(v); + if (position != -1) { + setCurrentTab(position); + } + } + }); + + /** 每一个Tab的布局参数 */ + LinearLayout.LayoutParams lp_tab = mTabSpaceEqual ? + new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f) : + new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); + if (mTabWidth > 0) { + lp_tab = new LinearLayout.LayoutParams((int) mTabWidth, LayoutParams.MATCH_PARENT); + } + mTabsContainer.addView(tabView, position, lp_tab); + } + + private void updateTabStyles() { + for (int i = 0; i < mTabCount; i++) { + View v = mTabsContainer.getChildAt(i); +// v.setPadding((int) mTabPadding, v.getPaddingTop(), (int) mTabPadding, v.getPaddingBottom()); + TextView tv_tab_title = v.findViewById(R.id.tv_tab_title); + if (tv_tab_title != null) { + v.setPadding((int) mTabPadding, 0, (int) mTabPadding, 0); + tv_tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, i == mCurrentTab ? mTextSelectSize : mTextUnSelectSize); + tv_tab_title.setTextColor(i == mCurrentTab ? mTextSelectColor : mTextUnSelectColor); + // 设置选中状态 + tv_tab_title.setSelected(i == mCurrentTab); + + if (mTextAllCaps) { + tv_tab_title.setText(tv_tab_title.getText().toString().toUpperCase()); + } + + if (mTextBold == TEXT_BOLD_BOTH) { + tv_tab_title.getPaint().setFakeBoldText(true); + } + // 被选中设置为粗体 + else if (mTextBold == TEXT_BOLD_WHEN_SELECT) { + tv_tab_title.getPaint().setFakeBoldText(i == mCurrentTab); + } else if (mTextBold == TEXT_BOLD_NONE) { + tv_tab_title.getPaint().setFakeBoldText(false); + } + + if (isDmgOpen()) { + generateTitleDmg(v, tv_tab_title, i); + } + } + } + + } + + private void generateTitleDmg(View tabView, TextView textView, int position) { + // 空字符串不能做镜像,否则会引发空指针 + if (TextUtils.isEmpty(textView.getText())) { + return; + } + + // 如果需要开启镜像,需要把所有的字设置为选中的字体 +// textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextUnSelectSize); +// ImageView imageView = tabView.findViewById(R.id.tv_tab_title_dmg); +// imageView.setImageBitmap(ViewUtils.generateViewCacheBitmap(textView)); +// imageView.setMaxWidth(imageView.getDrawable().getIntrinsicWidth()); + + + ImageView imageView = tabView.findViewById(R.id.tv_tab_title_dmg); + // 如果需要开启镜像,需要把所有的字设置为选中的字体 + if (mTextSelectSize >= mTextUnSelectSize) { + textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSelectSize); + imageView.setImageBitmap(ViewUtils.generateViewCacheBitmap(textView)); + int drawableWidth = imageView.getDrawable().getIntrinsicWidth(); + imageView.setMinimumWidth((int) (drawableWidth * mTextUnSelectSize / mTextSelectSize)); + imageView.setMaxWidth(drawableWidth); + } else { + textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextUnSelectSize); + imageView.setImageBitmap(ViewUtils.generateViewCacheBitmap(textView)); + int drawableWidth = imageView.getDrawable().getIntrinsicWidth(); + imageView.setMinimumWidth((int) (drawableWidth * mTextSelectSize / mTextUnSelectSize)); + imageView.setMaxWidth(drawableWidth); + } + +// iTabScaleTransformer.setNormalWidth(position, imageView.getDrawable().getIntrinsicWidth(), position == mViewPager.getCurrentItem()); + textView.setVisibility(View.GONE); + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + /** + * position:当前View的位置 + * mCurrentPositionOffset:当前View的偏移量比例.[0,1) + */ + this.mCurrentTab = position; + this.mCurrentPositionOffset = positionOffset; + iTabScaleTransformer.onPageScrolled(position, positionOffset, positionOffsetPixels); + scrollToCurrentTab(); + invalidate(); +// Log.i("onPageScrolled", "mCurrentTab:" + mCurrentTab + " positionOffset:" + positionOffset); + if (this.mCurrentPositionOffset == 0) { + updateTabSelection(mCurrentTab); + } + } + + @Override + public void onPageSelected(int position) { + + } + + @Override + public void onPageScrollStateChanged(int state) { +// if (state == ViewPager.SCROLL_STATE_IDLE) { +// updateTabSelection(mCurrentTab); +// } + } + + /** + * HorizontalScrollView滚到当前tab,并且居中显示 + */ + private void scrollToCurrentTab() { + if (mTabCount <= 0) { + return; + } + + int offset = (int) (mCurrentPositionOffset * mTabsContainer.getChildAt(mCurrentTab).getWidth()); + /**当前Tab的left+当前Tab的Width乘以positionOffset*/ + int newScrollX = mTabsContainer.getChildAt(mCurrentTab).getLeft() + offset; + + if (mCurrentTab > 0 || offset > 0) { + /**HorizontalScrollView移动到当前tab,并居中*/ + newScrollX -= getWidth() / 2 - getPaddingLeft(); + calcIndicatorRect(); + newScrollX += ((mTabRect.right - mTabRect.left) / 2); + } + + if (newScrollX != mLastScrollX) { + mLastScrollX = newScrollX; + /** scrollTo(int x,int y):x,y代表的不是坐标点,而是偏移量 + * x:表示离起始位置的x水平方向的偏移量 + * y:表示离起始位置的y垂直方向的偏移量 + */ + scrollTo(newScrollX, 0); + } + } + + private void updateTabSelection(int position) { + for (int i = 0; i < mTabCount; ++i) { + View tabView = mTabsContainer.getChildAt(i); + final boolean isSelect = i == position; + final TextView tab_title = tabView.findViewById(R.id.tv_tab_title); + + if (tab_title != null) { + tab_title.setTextColor(isSelect ? mTextSelectColor : mTextUnSelectColor); + // 设置选中状态 + tab_title.setSelected(isSelect); + + if (mTextBold == TEXT_BOLD_BOTH) { + tab_title.getPaint().setFakeBoldText(true); + } + // 被选中设置为粗体 + else if (mTextBold == TEXT_BOLD_WHEN_SELECT && i == position) { + tab_title.getPaint().setFakeBoldText(true); + } else { + tab_title.getPaint().setFakeBoldText(false); + } + if (isDmgOpen() && (mTextSelectColor != mTextUnSelectColor || mTextBold == TEXT_BOLD_WHEN_SELECT)) { + tab_title.setVisibility(View.VISIBLE); + generateTitleDmg(tabView, tab_title, i); + } else { + final int finalI = i; + tab_title.post(new Runnable() { + @Override + public void run() { + tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, finalI == mCurrentTab ? mTextSelectSize : mTextUnSelectSize); + tab_title.requestLayout(); + } + }); + } + } + } + } + + private float margin; + + private void calcIndicatorRect() { + View currentTabView = mTabsContainer.getChildAt(this.mCurrentTab); + float left = currentTabView.getLeft(); + float right = currentTabView.getRight(); + + //for mIndicatorWidthEqualTitle + if (mIndicatorStyle == STYLE_NORMAL && mIndicatorWidthEqualTitle) { + TextView tab_title = currentTabView.findViewById(R.id.tv_tab_title); + float textWidth = mTextPaint.measureText(tab_title.getText().toString()); + margin = (right - left - textWidth) / 2; + } + + if (this.mCurrentTab < mTabCount - 1) { + View nextTabView = mTabsContainer.getChildAt(this.mCurrentTab + 1); + float nextTabLeft = nextTabView.getLeft(); + float nextTabRight = nextTabView.getRight(); + + left = left + mCurrentPositionOffset * (nextTabLeft - left); + right = right + mCurrentPositionOffset * (nextTabRight - right); + + //for mIndicatorWidthEqualTitle + if (mIndicatorStyle == STYLE_NORMAL && mIndicatorWidthEqualTitle) { + TextView next_tab_title = nextTabView.findViewById(R.id.tv_tab_title); + float nextTextWidth = mTextPaint.measureText(next_tab_title.getText().toString()); + float nextMargin = (nextTabRight - nextTabLeft - nextTextWidth) / 2; + margin = margin + mCurrentPositionOffset * (nextMargin - margin); + } + } + + mIndicatorRect.left = (int) left; + mIndicatorRect.right = (int) right; + //for mIndicatorWidthEqualTitle + if (mIndicatorStyle == STYLE_NORMAL && mIndicatorWidthEqualTitle) { + mIndicatorRect.left = (int) (left + margin - 1); + mIndicatorRect.right = (int) (right - margin - 1); + } + + mTabRect.left = (int) left; + mTabRect.right = (int) right; + + if (mIndicatorWidth < 0) { //indicatorWidth小于0时,原jpardogo's PagerSlidingTabStrip + + } else {//indicatorWidth大于0时,圆角矩形以及三角形 + float indicatorLeft = currentTabView.getLeft() + (currentTabView.getWidth() - mIndicatorWidth) / 2; + + if (this.mCurrentTab < mTabCount - 1) { + View nextTab = mTabsContainer.getChildAt(this.mCurrentTab + 1); + indicatorLeft = indicatorLeft + mCurrentPositionOffset * (currentTabView.getWidth() / 2 + nextTab.getWidth() / 2); + } + + mIndicatorRect.left = (int) indicatorLeft; + mIndicatorRect.right = (int) (mIndicatorRect.left + mIndicatorWidth); + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (isInEditMode() || mTabCount <= 0) { + return; + } + + int height = getHeight(); + int paddingLeft = getPaddingLeft(); + // draw divider + if (mDividerWidth > 0) { + mDividerPaint.setStrokeWidth(mDividerWidth); + mDividerPaint.setColor(mDividerColor); + for (int i = 0; i < mTabCount - 1; i++) { + View tab = mTabsContainer.getChildAt(i); + canvas.drawLine(paddingLeft + tab.getRight(), mDividerPadding, paddingLeft + tab.getRight(), height - mDividerPadding, mDividerPaint); + } + } + + // draw underline + if (mUnderlineHeight > 0) { + mRectPaint.setColor(mUnderlineColor); + if (mUnderlineGravity == Gravity.BOTTOM) { + canvas.drawRect(paddingLeft, height - mUnderlineHeight, mTabsContainer.getWidth() + paddingLeft, height, mRectPaint); + } else { + canvas.drawRect(paddingLeft, 0, mTabsContainer.getWidth() + paddingLeft, mUnderlineHeight, mRectPaint); + } + } + + //draw indicator line + + calcIndicatorRect(); + if (mIndicatorStyle == STYLE_TRIANGLE) { + if (mIndicatorHeight > 0) { + mTrianglePaint.setColor(mIndicatorColor); + mTrianglePath.reset(); + mTrianglePath.moveTo(paddingLeft + mIndicatorRect.left, height); + mTrianglePath.lineTo(paddingLeft + mIndicatorRect.left / 2 + mIndicatorRect.right / 2, height - mIndicatorHeight); + mTrianglePath.lineTo(paddingLeft + mIndicatorRect.right, height); + mTrianglePath.close(); + canvas.drawPath(mTrianglePath, mTrianglePaint); + } + } else if (mIndicatorStyle == STYLE_BLOCK) { + if (mIndicatorHeight < 0) { + mIndicatorHeight = height - mIndicatorMarginTop - mIndicatorMarginBottom; + } else { + + } + + if (mIndicatorHeight > 0) { + if (mIndicatorCornerRadius < 0 || mIndicatorCornerRadius > mIndicatorHeight / 2) { + mIndicatorCornerRadius = mIndicatorHeight / 2; + } + + mIndicatorDrawable.setColor(mIndicatorColor); + mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left, + (int) mIndicatorMarginTop, (int) (paddingLeft + mIndicatorRect.right - mIndicatorMarginRight), + (int) (mIndicatorMarginTop + mIndicatorHeight)); + mIndicatorDrawable.setCornerRadius(mIndicatorCornerRadius); + mIndicatorDrawable.draw(canvas); + } + } else { + /* mRectPaint.setColor(mIndicatorColor); + calcIndicatorRect(); + canvas.drawRect(getPaddingLeft() + mIndicatorRect.left, getHeight() - mIndicatorHeight, + mIndicatorRect.right + getPaddingLeft(), getHeight(), mRectPaint);*/ + + if (mIndicatorHeight > 0) { + mIndicatorDrawable.setColor(mIndicatorColor); + + if (mIndicatorGravity == Gravity.BOTTOM) { + mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left, + height - (int) mIndicatorHeight - (int) mIndicatorMarginBottom, + paddingLeft + mIndicatorRect.right - (int) mIndicatorMarginRight, + height - (int) mIndicatorMarginBottom); + } else { + mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left, + (int) mIndicatorMarginTop, + paddingLeft + mIndicatorRect.right - (int) mIndicatorMarginRight, + (int) mIndicatorHeight + (int) mIndicatorMarginTop); + } + mIndicatorDrawable.setCornerRadius(mIndicatorCornerRadius); + mIndicatorDrawable.draw(canvas); + } + } + } + + //setter and getter + public void setCurrentTab(int currentTab) { + setCurrentTab(currentTab, !mSnapOnTabClick); + } + + public void setCurrentTab(int currentTab, boolean smoothScroll) { + if (mCurrentTab != currentTab) { + this.mCurrentTab = currentTab; + if (mViewPager != null) { + mViewPager.setCurrentItem(currentTab, smoothScroll); + } + + if (mListener != null) { + mListener.onTabSelect(currentTab); + } + } else { + if (mListener != null) { + mListener.onTabReselect(currentTab); + } + } + } + + public void setIndicatorStyle(int indicatorStyle) { + this.mIndicatorStyle = indicatorStyle; + invalidate(); + } + + public void setTabPadding(float tabPadding) { + this.mTabPadding = dp2px(tabPadding); + updateTabStyles(); + } + + public void setTabSpaceEqual(boolean tabSpaceEqual) { + this.mTabSpaceEqual = tabSpaceEqual; + updateTabStyles(); + } + + public void setTabWidth(float tabWidth) { + this.mTabWidth = dp2px(tabWidth); + updateTabStyles(); + } + + public void setIndicatorColor(int indicatorColor) { + this.mIndicatorColor = indicatorColor; + invalidate(); + } + + public void setIndicatorHeight(float indicatorHeight) { + this.mIndicatorHeight = dp2px(indicatorHeight); + invalidate(); + } + + public void setIndicatorWidth(float indicatorWidth) { + this.mIndicatorWidth = dp2px(indicatorWidth); + invalidate(); + } + + public void setIndicatorCornerRadius(float indicatorCornerRadius) { + this.mIndicatorCornerRadius = dp2px(indicatorCornerRadius); + invalidate(); + } + + public void setIndicatorGravity(int indicatorGravity) { + this.mIndicatorGravity = indicatorGravity; + invalidate(); + } + + public void setIndicatorMargin(float indicatorMarginLeft, float indicatorMarginTop, + float indicatorMarginRight, float indicatorMarginBottom) { + this.mIndicatorMarginLeft = dp2px(indicatorMarginLeft); + this.mIndicatorMarginTop = dp2px(indicatorMarginTop); + this.mIndicatorMarginRight = dp2px(indicatorMarginRight); + this.mIndicatorMarginBottom = dp2px(indicatorMarginBottom); + invalidate(); + } + + public void setIndicatorWidthEqualTitle(boolean indicatorWidthEqualTitle) { + this.mIndicatorWidthEqualTitle = indicatorWidthEqualTitle; + invalidate(); + } + + public void setUnderlineColor(int underlineColor) { + this.mUnderlineColor = underlineColor; + invalidate(); + } + + public void setUnderlineHeight(float underlineHeight) { + this.mUnderlineHeight = dp2px(underlineHeight); + invalidate(); + } + + public void setUnderlineGravity(int underlineGravity) { + this.mUnderlineGravity = underlineGravity; + invalidate(); + } + + public void setDividerColor(int dividerColor) { + this.mDividerColor = dividerColor; + invalidate(); + } + + public void setDividerWidth(float dividerWidth) { + this.mDividerWidth = dp2px(dividerWidth); + invalidate(); + } + + public void setDividerPadding(float dividerPadding) { + this.mDividerPadding = dp2px(dividerPadding); + invalidate(); + } + + public void setTextSelectsize(float textsize) { + this.mTextSelectSize = sp2px(textsize); + initTransformer(); + updateTabStyles(); + } + + public void setTextUnselectSize(int textSize) { + this.mTextUnSelectSize = textSize; + initTransformer(); + updateTabStyles(); + } + + public void setTextSelectColor(int textSelectColor) { + this.mTextSelectColor = textSelectColor; + updateTabStyles(); + } + + public void setTextUnselectColor(int textUnselectColor) { + this.mTextUnSelectColor = textUnselectColor; + updateTabStyles(); + } + + public void setTextBold(int textBold) { + this.mTextBold = textBold; + updateTabStyles(); + } + + public void setTextAllCaps(boolean textAllCaps) { + this.mTextAllCaps = textAllCaps; + updateTabStyles(); + } + + public void setSnapOnTabClick(boolean snapOnTabClick) { + mSnapOnTabClick = snapOnTabClick; + } + + + public int getTabCount() { + return mTabCount; + } + + public int getCurrentTab() { + return mCurrentTab; + } + + public int getIndicatorStyle() { + return mIndicatorStyle; + } + + public float getTabPadding() { + return mTabPadding; + } + + public boolean isTabSpaceEqual() { + return mTabSpaceEqual; + } + + public float getTabWidth() { + return mTabWidth; + } + + public int getIndicatorColor() { + return mIndicatorColor; + } + + public float getIndicatorHeight() { + return mIndicatorHeight; + } + + public float getIndicatorWidth() { + return mIndicatorWidth; + } + + public float getIndicatorCornerRadius() { + return mIndicatorCornerRadius; + } + + public float getIndicatorMarginLeft() { + return mIndicatorMarginLeft; + } + + public float getIndicatorMarginTop() { + return mIndicatorMarginTop; + } + + public float getIndicatorMarginRight() { + return mIndicatorMarginRight; + } + + public float getIndicatorMarginBottom() { + return mIndicatorMarginBottom; + } + + public int getUnderlineColor() { + return mUnderlineColor; + } + + public float getUnderlineHeight() { + return mUnderlineHeight; + } + + public int getDividerColor() { + return mDividerColor; + } + + public float getDividerWidth() { + return mDividerWidth; + } + + public float getDividerPadding() { + return mDividerPadding; + } + + public float getTextSelectSize() { + return mTextSelectSize; + } + + public float getTextUnselectSize() { + return mTextUnSelectSize; + } + + public int getTextSelectColor() { + return mTextSelectColor; + } + + public int getTextUnselectColor() { + return mTextUnSelectColor; + } + + public int getTextBold() { + return mTextBold; + } + + public boolean isTextAllCaps() { + return mTextAllCaps; + } + + public void addViewPagerTransformer(IViewPagerTransformer transformer) { + this.extendTransformer.addViewPagerTransformer(transformer); + } + + public void removeViewPagerTransformer(IViewPagerTransformer transformer) { + this.extendTransformer.removeViewPagerTransformer(transformer); + } + + public List getTransformers() { + return extendTransformer.getTransformers(); + } + + public void setTransformers(List transformers) { + this.extendTransformer.setTransformers(transformers); + } + + public TextView getTitleView(int tab) { + View tabView = mTabsContainer.getChildAt(tab); + return (TextView) tabView.findViewById(R.id.tv_tab_title); + } + + //setter and getter + + // show MsgTipView + private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private SparseBooleanArray mInitSetMap = new SparseBooleanArray(); + + /** + * 显示未读消息 + * + * @param position 显示tab位置 + * @param num num小于等于0显示红点,num大于0显示数字 + */ + public void showMsg(int position, int num) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + + View tabView = mTabsContainer.getChildAt(position); + MsgView tipView = tabView.findViewById(R.id.rtv_msg_tip); + if (tipView != null) { + UnreadMsgUtils.show(tipView, num); + + RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) tipView.getLayoutParams(); + if (openDmg) { + params.addRule(RelativeLayout.ALIGN_END, R.id.tv_tab_title_dmg); + params.addRule(RelativeLayout.ALIGN_TOP, R.id.tv_tab_title_dmg); + } else { + params.addRule(RelativeLayout.ALIGN_END, R.id.tv_tab_title); + params.addRule(RelativeLayout.ALIGN_TOP, R.id.tv_tab_title); + } + + // 红点的位置 + if (num <= 0) { + params.topMargin = mTabDotMarginTop; + params.rightMargin = mTabDotMarginRight; + } + // 未读数的位置 + else { + params.topMargin = mTabMsgMarginTop; + params.rightMargin = mTabMsgMarginRight; + } + + tipView.setLayoutParams(params); + if (mInitSetMap.get(position)) { + return; + } + mInitSetMap.put(position, true); + } + } + + /** + * 显示未读红点 + * + * @param position 显示tab位置 + */ + public void showDot(int position) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + showMsg(position, 0); + } + + /** + * 隐藏未读消息 + */ + public void hideMsg(int position) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + + View tabView = mTabsContainer.getChildAt(position); + MsgView tipView = tabView.findViewById(R.id.rtv_msg_tip); + if (tipView != null) { + tipView.setVisibility(View.GONE); + } + } + + /** + * 当前类只提供了少许设置未读消息属性的方法,可以通过该方法获取MsgView对象从而各种设置 + */ + public MsgView getMsgView(int position) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + View tabView = mTabsContainer.getChildAt(position); + return (MsgView) tabView.findViewById(R.id.rtv_msg_tip); + } + + /** + * 当前类只提供了少许设置未读消息属性的方法,可以通过该方法获取MsgView对象从而各种设置 + */ + public TextView getTitle(int position) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + View tabView = mTabsContainer.getChildAt(position); + if (tabView == null) { + return null; + } + return (TextView) tabView.findViewById(R.id.tv_tab_title); + } + + public ImageView getDmgView(int position) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + View tabView = mTabsContainer.getChildAt(position); + if (tabView == null) { + return null; + } + +// if (tabView.getVisibility() != View.GONE) { +// return null; +// } + + return (ImageView) tabView.findViewById(R.id.tv_tab_title_dmg); + } + + private OnTabSelectListener mListener; + + public void setOnTabSelectListener(OnTabSelectListener listener) { + this.mListener = listener; + } + + class InnerPagerAdapter extends FragmentPagerAdapter { + private ArrayList fragments; + private String[] titles; + + public InnerPagerAdapter(FragmentManager fm, ArrayList fragments, String[] titles) { + super(fm); + this.fragments = fragments; + this.titles = titles; + } + + @Override + public int getCount() { + return fragments.size(); + } + + @Override + public CharSequence getPageTitle(int position) { + return titles[position]; + } + + @Override + public Fragment getItem(int position) { + return fragments.get(position); + } + + @Override + public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { + // 覆写destroyItem并且空实现,这样每个Fragment中的视图就不会被销毁 + // super.destroyItem(container, position, object); + } + + @Override + public int getItemPosition(@NonNull Object object) { + return PagerAdapter.POSITION_NONE; + } + } + + @Override + protected Parcelable onSaveInstanceState() { + Bundle bundle = new Bundle(); + bundle.putParcelable("instanceState", super.onSaveInstanceState()); + bundle.putInt("mCurrentTab", mCurrentTab); + return bundle; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (state instanceof Bundle) { + Bundle bundle = (Bundle) state; + mCurrentTab = bundle.getInt("mCurrentTab"); + state = bundle.getParcelable("instanceState"); + if (mCurrentTab != 0 && mTabsContainer.getChildCount() > 0) { + updateTabSelection(mCurrentTab); + scrollToCurrentTab(); + } + } + super.onRestoreInstanceState(state); + } + + protected int dp2px(float dp) { + final float scale = mContext.getResources().getDisplayMetrics().density; + return (int) (dp * scale + 0.5f); + } + + protected int sp2px(float sp) { + final float scale = this.mContext.getResources().getDisplayMetrics().scaledDensity; + return (int) (sp * scale + 0.5f); + } +} diff --git a/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/SlidingTabLayout.java b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/SlidingTabLayout.java new file mode 100644 index 0000000..42752a1 --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/SlidingTabLayout.java @@ -0,0 +1,1012 @@ +package com.flyco.tablayout; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.drawable.GradientDrawable; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.util.SparseBooleanArray; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.HorizontalScrollView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; + +import com.flyco.tablayout.listener.OnTabSelectListener; +import com.flyco.tablayout.utils.UnreadMsgUtils; +import com.flyco.tablayout.widget.MsgView; + +import java.util.ArrayList; +import java.util.Collections; + +/** + * 滑动TabLayout,对于ViewPager的依赖性强 + */ +public class SlidingTabLayout extends HorizontalScrollView implements ViewPager.OnPageChangeListener { + + private static final int TOP = 0; + private static final int BOTTOM = 1; + private static final int CENTER = 2; + + private Context mContext; + private ViewPager mViewPager; + private ArrayList mTitles; + private LinearLayout mTabsContainer; + private int mCurrentTab; + private float mCurrentPositionOffset; + private int mTabCount; + /** + * 用于绘制显示器 + */ + private Rect mIndicatorRect = new Rect(); + /** + * 用于实现滚动居中 + */ + private Rect mTabRect = new Rect(); + private GradientDrawable mIndicatorDrawable = new GradientDrawable(); + + private Paint mRectPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Paint mDividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Paint mTrianglePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Path mTrianglePath = new Path(); + private static final int STYLE_NORMAL = 0; + private static final int STYLE_TRIANGLE = 1; + private static final int STYLE_BLOCK = 2; + private int mIndicatorStyle = STYLE_NORMAL; + + private float mTabPadding; + private boolean mTabSpaceEqual; + private float mTabWidth; + + /** + * indicator + */ + private int mIndicatorColor; + private float mIndicatorHeight; + private float mIndicatorWidth; + private float mIndicatorCornerRadius; + private float mIndicatorMarginLeft; + private float mIndicatorMarginTop; + private float mIndicatorMarginRight; + private float mIndicatorMarginBottom; + private int mIndicatorGravity; + private boolean mIndicatorWidthEqualTitle; + + /** + * underline + */ + private int mUnderlineColor; + private float mUnderlineHeight; + private int mUnderlineGravity; + + /** + * divider + */ + private int mDividerColor; + private float mDividerWidth; + private float mDividerPadding; + + /** + * title + */ + private static final int TEXT_BOLD_NONE = 0; + private static final int TEXT_BOLD_WHEN_SELECT = 1; + private static final int TEXT_BOLD_BOTH = 2; + private float mTextSelectSize; + private float mTextUnSelectSize; + private int mTextSelectColor; + private int mTextUnSelectColor; + private int mTextBold; + private boolean mTextAllCaps; + + private int mLastScrollX; + private int mHeight; + private boolean mSnapOnTabClick; + + /** + * tab的上下间距 + */ + private int mTabMarginTop; + private int mTabMarginBottom; + + /** + * tab摆放的位置,目前只支持top和bottom + */ + private int mTabGravity; + + public SlidingTabLayout(Context context) { + this(context, null, 0); + } + + public SlidingTabLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SlidingTabLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setFillViewport(true);//设置滚动视图是否可以伸缩其内容以填充视口 + setWillNotDraw(false);//重写onDraw方法,需要调用这个方法来清除flag + setClipChildren(false); + setClipToPadding(false); + + this.mContext = context; + mTabsContainer = new LinearLayout(context); + addView(mTabsContainer); + + obtainAttributes(context, attrs); + + //get layout_height + String height = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "layout_height"); + + if (height.equals(ViewGroup.LayoutParams.MATCH_PARENT + "")) { + } else if (height.equals(ViewGroup.LayoutParams.WRAP_CONTENT + "")) { + } else { + int[] systemAttrs = {android.R.attr.layout_height}; + TypedArray a = context.obtainStyledAttributes(attrs, systemAttrs); + mHeight = a.getDimensionPixelSize(0, ViewGroup.LayoutParams.WRAP_CONTENT); + a.recycle(); + } + } + + private void obtainAttributes(Context context, AttributeSet attrs) { + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlidingTabLayout); + + mIndicatorStyle = ta.getInt(R.styleable.SlidingTabLayout_tl_indicator_style, STYLE_NORMAL); + mIndicatorColor = ta.getColor(R.styleable.SlidingTabLayout_tl_indicator_color, Color.parseColor(mIndicatorStyle == STYLE_BLOCK ? "#4B6A87" : "#ffffff")); + mIndicatorHeight = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_height, + dp2px(mIndicatorStyle == STYLE_TRIANGLE ? 4 : (mIndicatorStyle == STYLE_BLOCK ? -1 : 2))); + mIndicatorWidth = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_width, dp2px(mIndicatorStyle == STYLE_TRIANGLE ? 10 : -1)); + mIndicatorCornerRadius = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_corner_radius, dp2px(mIndicatorStyle == STYLE_BLOCK ? -1 : 0)); + mIndicatorMarginLeft = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_margin_left, dp2px(0)); + mIndicatorMarginTop = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_margin_top, dp2px(mIndicatorStyle == STYLE_BLOCK ? 7 : 0)); + mIndicatorMarginRight = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_margin_right, dp2px(0)); + mIndicatorMarginBottom = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_margin_bottom, dp2px(mIndicatorStyle == STYLE_BLOCK ? 7 : 0)); + mIndicatorGravity = ta.getInt(R.styleable.SlidingTabLayout_tl_indicator_gravity, Gravity.BOTTOM); + mIndicatorWidthEqualTitle = ta.getBoolean(R.styleable.SlidingTabLayout_tl_indicator_width_equal_title, false); + + mUnderlineColor = ta.getColor(R.styleable.SlidingTabLayout_tl_underline_color, Color.parseColor("#ffffff")); + mUnderlineHeight = ta.getDimension(R.styleable.SlidingTabLayout_tl_underline_height, dp2px(0)); + mUnderlineGravity = ta.getInt(R.styleable.SlidingTabLayout_tl_underline_gravity, Gravity.BOTTOM); + + mDividerColor = ta.getColor(R.styleable.SlidingTabLayout_tl_divider_color, Color.parseColor("#ffffff")); + mDividerWidth = ta.getDimension(R.styleable.SlidingTabLayout_tl_divider_width, dp2px(0)); + mDividerPadding = ta.getDimension(R.styleable.SlidingTabLayout_tl_divider_padding, dp2px(12)); + + mTextSelectSize = ta.getDimension(R.styleable.SlidingTabLayout_tl_textSelectSize, sp2px(14)); + mTextUnSelectSize = ta.getDimension(R.styleable.SlidingTabLayout_tl_textUnSelectSize, sp2px(14)); + mTextSelectColor = ta.getColor(R.styleable.SlidingTabLayout_tl_textSelectColor, Color.parseColor("#ffffff")); + mTextUnSelectColor = ta.getColor(R.styleable.SlidingTabLayout_tl_textUnSelectColor, Color.parseColor("#AAffffff")); + mTextBold = ta.getInt(R.styleable.SlidingTabLayout_tl_textBold, TEXT_BOLD_NONE); + mTextAllCaps = ta.getBoolean(R.styleable.SlidingTabLayout_tl_textAllCaps, false); + + mTabSpaceEqual = ta.getBoolean(R.styleable.SlidingTabLayout_tl_tab_space_equal, false); + mTabWidth = ta.getDimension(R.styleable.SlidingTabLayout_tl_tab_width, dp2px(-1)); + mTabPadding = ta.getDimension(R.styleable.SlidingTabLayout_tl_tab_padding, mTabSpaceEqual || mTabWidth > 0 ? dp2px(0) : dp2px(20)); + // 得到设置的上下间距和gravity + mTabMarginTop = ta.getDimensionPixelSize(R.styleable.SlidingTabLayout_tl_tab_marginTop, 0); + mTabMarginBottom = ta.getDimensionPixelSize(R.styleable.SlidingTabLayout_tl_tab_marginBottom, 0); + mTabGravity = ta.getInt(R.styleable.SlidingTabLayout_tl_tab_gravity, CENTER); + + ta.recycle(); + } + + /** + * 关联ViewPager + */ + public void setViewPager(ViewPager vp) { + if (vp == null || vp.getAdapter() == null) { + throw new IllegalStateException("ViewPager or ViewPager adapter can not be NULL !"); + } + + this.mViewPager = vp; + + this.mViewPager.removeOnPageChangeListener(this); + this.mViewPager.addOnPageChangeListener(this); + notifyDataSetChanged(); + } + + /** + * 关联ViewPager,用于不想在ViewPager适配器中设置titles数据的情况 + */ + public void setTitle(String[] titles) { + if (titles == null || titles.length == 0) { + throw new IllegalStateException("Titles can not be EMPTY !"); + } + + mTitles = new ArrayList<>(); + Collections.addAll(mTitles, titles); + notifyDataSetChanged(); + } + + /** + * 关联ViewPager,用于不想在ViewPager适配器中设置titles数据的情况 + */ + public void setViewPager(ViewPager vp, String[] titles) { + if (vp == null || vp.getAdapter() == null) { + throw new IllegalStateException("ViewPager or ViewPager adapter can not be NULL !"); + } + + if (titles == null || titles.length == 0) { + throw new IllegalStateException("Titles can not be EMPTY !"); + } + + if (titles.length != vp.getAdapter().getCount()) { + throw new IllegalStateException("Titles length must be the same as the page count !"); + } + + this.mViewPager = vp; + mTitles = new ArrayList<>(); + Collections.addAll(mTitles, titles); + + this.mViewPager.removeOnPageChangeListener(this); + this.mViewPager.addOnPageChangeListener(this); + notifyDataSetChanged(); + } + + /** + * 关联ViewPager,用于连适配器都不想自己实例化的情况 + */ + public void setViewPager(ViewPager vp, String[] titles, FragmentActivity fa, ArrayList fragments) { + if (vp == null) { + throw new IllegalStateException("ViewPager can not be NULL !"); + } + + if (titles == null || titles.length == 0) { + throw new IllegalStateException("Titles can not be EMPTY !"); + } + + this.mViewPager = vp; + this.mViewPager.setAdapter(new InnerPagerAdapter(fa.getSupportFragmentManager(), fragments, titles)); + + this.mViewPager.removeOnPageChangeListener(this); + this.mViewPager.addOnPageChangeListener(this); + notifyDataSetChanged(); + } + + /** + * 更新数据 + */ + public void notifyDataSetChanged() { + mTabsContainer.removeAllViews(); + this.mTabCount = mTitles == null ? mViewPager.getAdapter().getCount() : mTitles.size(); + View tabView; + for (int i = 0; i < mTabCount; i++) { + tabView = View.inflate(mContext, R.layout.layout_tab, null); + TextView title = tabView.findViewById(R.id.tv_tab_title); + // 设置tab的位置信息 + setTabLayoutParams(title); + CharSequence pageTitle = mTitles == null ? mViewPager.getAdapter().getPageTitle(i) : mTitles.get(i); + addTab(i, pageTitle.toString(), tabView); + } + + updateTabStyles(); + } + + private void setTabLayoutParams(TextView title) { + RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) title.getLayoutParams(); + params.topMargin = mTabMarginTop; + params.bottomMargin = mTabMarginBottom; + if (mTabGravity == TOP) { + params.addRule(RelativeLayout.ALIGN_PARENT_TOP); + } else if (mTabGravity == BOTTOM) { + params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + } else { + params.addRule(RelativeLayout.CENTER_VERTICAL); + } + title.setLayoutParams(params); + } + + public void addNewTab(String title) { + View tabView = View.inflate(mContext, R.layout.layout_tab, null); + if (mTitles != null) { + mTitles.add(title); + } + + CharSequence pageTitle = mTitles == null ? mViewPager.getAdapter().getPageTitle(mTabCount) : mTitles.get(mTabCount); + addTab(mTabCount, pageTitle.toString(), tabView); + this.mTabCount = mTitles == null ? mViewPager.getAdapter().getCount() : mTitles.size(); + + updateTabStyles(); + } + + /** + * 创建并添加tab + */ + private void addTab(final int position, String title, View tabView) { + TextView tv_tab_title = tabView.findViewById(R.id.tv_tab_title); + if (tv_tab_title != null) { + if (title != null) tv_tab_title.setText(title); + } + + tabView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + int position = mTabsContainer.indexOfChild(v); + if (position != -1) { + setCurrentTab(position); + } + } + }); + + /** 每一个Tab的布局参数 */ + LinearLayout.LayoutParams lp_tab = mTabSpaceEqual ? + new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f) : + new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); + if (mTabWidth > 0) { + lp_tab = new LinearLayout.LayoutParams((int) mTabWidth, LayoutParams.MATCH_PARENT); + } + mTabsContainer.addView(tabView, position, lp_tab); + } + + private void updateTabStyles() { + for (int i = 0; i < mTabCount; i++) { + View v = mTabsContainer.getChildAt(i); +// v.setPadding((int) mTabPadding, v.getPaddingTop(), (int) mTabPadding, v.getPaddingBottom()); + TextView tv_tab_title = v.findViewById(R.id.tv_tab_title); + if (tv_tab_title != null) { + tv_tab_title.setTextColor(i == mCurrentTab ? mTextSelectColor : mTextUnSelectColor); + tv_tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, i == mCurrentTab ? mTextSelectSize : mTextUnSelectSize); + tv_tab_title.setPadding((int) mTabPadding, 0, (int) mTabPadding, 0); + if (mTextAllCaps) { + tv_tab_title.setText(tv_tab_title.getText().toString().toUpperCase()); + } + + if (mTextBold == TEXT_BOLD_BOTH) { + tv_tab_title.getPaint().setFakeBoldText(true); + } + // 被选中设置为粗体 + else if (mTextBold == TEXT_BOLD_WHEN_SELECT && i == mCurrentTab) { + tv_tab_title.getPaint().setFakeBoldText(true); + } else if (mTextBold == TEXT_BOLD_NONE) { + tv_tab_title.getPaint().setFakeBoldText(false); + } + } + } + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + /** + * position:当前View的位置 + * mCurrentPositionOffset:当前View的偏移量比例.[0,1) + */ + this.mCurrentTab = position; + this.mCurrentPositionOffset = positionOffset; + scrollToCurrentTab(); + invalidate(); + } + + @Override + public void onPageSelected(int position) { + updateTabSelection(position); + } + + @Override + public void onPageScrollStateChanged(int state) { + } + + /** + * HorizontalScrollView滚到当前tab,并且居中显示 + */ + private void scrollToCurrentTab() { + if (mTabCount <= 0) { + return; + } + + int offset = (int) (mCurrentPositionOffset * mTabsContainer.getChildAt(mCurrentTab).getWidth()); + /**当前Tab的left+当前Tab的Width乘以positionOffset*/ + int newScrollX = mTabsContainer.getChildAt(mCurrentTab).getLeft() + offset; + + if (mCurrentTab > 0 || offset > 0) { + /**HorizontalScrollView移动到当前tab,并居中*/ + newScrollX -= getWidth() / 2 - getPaddingLeft(); + calcIndicatorRect(); + newScrollX += ((mTabRect.right - mTabRect.left) / 2); + } + + if (newScrollX != mLastScrollX) { + mLastScrollX = newScrollX; + /** scrollTo(int x,int y):x,y代表的不是坐标点,而是偏移量 + * x:表示离起始位置的x水平方向的偏移量 + * y:表示离起始位置的y垂直方向的偏移量 + */ + scrollTo(newScrollX, 0); + } + } + + private void updateTabSelection(int position) { + for (int i = 0; i < mTabCount; ++i) { + View tabView = mTabsContainer.getChildAt(i); + final boolean isSelect = i == position; + TextView tab_title = tabView.findViewById(R.id.tv_tab_title); + + if (tab_title != null) { + tab_title.setTextColor(isSelect ? mTextSelectColor : mTextUnSelectColor); + tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, isSelect ? mTextSelectSize : mTextUnSelectSize); + if (mTextBold == TEXT_BOLD_WHEN_SELECT) { + tab_title.getPaint().setFakeBoldText(isSelect); + } + } + } + } + + private float margin; + + private void calcIndicatorRect() { + View currentTabView = mTabsContainer.getChildAt(this.mCurrentTab); + float left = currentTabView.getLeft(); + float right = currentTabView.getRight(); + + //for mIndicatorWidthEqualTitle + if (mIndicatorStyle == STYLE_NORMAL && mIndicatorWidthEqualTitle) { + TextView tab_title = currentTabView.findViewById(R.id.tv_tab_title); + mTextPaint.setTextSize(mTextSelectSize); + float textWidth = mTextPaint.measureText(tab_title.getText().toString()); + margin = (right - left - textWidth) / 2; + } + + if (this.mCurrentTab < mTabCount - 1) { + View nextTabView = mTabsContainer.getChildAt(this.mCurrentTab + 1); + float nextTabLeft = nextTabView.getLeft(); + float nextTabRight = nextTabView.getRight(); + + left = left + mCurrentPositionOffset * (nextTabLeft - left); + right = right + mCurrentPositionOffset * (nextTabRight - right); + + //for mIndicatorWidthEqualTitle + if (mIndicatorStyle == STYLE_NORMAL && mIndicatorWidthEqualTitle) { + TextView next_tab_title = nextTabView.findViewById(R.id.tv_tab_title); + mTextPaint.setTextSize(mTextUnSelectSize); + float nextTextWidth = mTextPaint.measureText(next_tab_title.getText().toString()); + float nextMargin = (nextTabRight - nextTabLeft - nextTextWidth) / 2; + margin = margin + mCurrentPositionOffset * (nextMargin - margin); + } + } + + mIndicatorRect.left = (int) left; + mIndicatorRect.right = (int) right; + //for mIndicatorWidthEqualTitle + if (mIndicatorStyle == STYLE_NORMAL && mIndicatorWidthEqualTitle) { + mIndicatorRect.left = (int) (left + margin - 1); + mIndicatorRect.right = (int) (right - margin - 1); + } + + mTabRect.left = (int) left; + mTabRect.right = (int) right; + + if (mIndicatorWidth < 0) { //indicatorWidth小于0时,原jpardogo's PagerSlidingTabStrip + + } else {//indicatorWidth大于0时,圆角矩形以及三角形 + float indicatorLeft = currentTabView.getLeft() + (currentTabView.getWidth() - mIndicatorWidth) / 2; + + if (this.mCurrentTab < mTabCount - 1) { + View nextTab = mTabsContainer.getChildAt(this.mCurrentTab + 1); + indicatorLeft = indicatorLeft + mCurrentPositionOffset * (currentTabView.getWidth() / 2 + nextTab.getWidth() / 2); + } + + mIndicatorRect.left = (int) indicatorLeft; + mIndicatorRect.right = (int) (mIndicatorRect.left + mIndicatorWidth); + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (isInEditMode() || mTabCount <= 0) { + return; + } + + int height = getHeight(); + int paddingLeft = getPaddingLeft(); + // draw divider + if (mDividerWidth > 0) { + mDividerPaint.setStrokeWidth(mDividerWidth); + mDividerPaint.setColor(mDividerColor); + for (int i = 0; i < mTabCount - 1; i++) { + View tab = mTabsContainer.getChildAt(i); + canvas.drawLine(paddingLeft + tab.getRight(), mDividerPadding, paddingLeft + tab.getRight(), height - mDividerPadding, mDividerPaint); + } + } + + // draw underline + if (mUnderlineHeight > 0) { + mRectPaint.setColor(mUnderlineColor); + if (mUnderlineGravity == Gravity.BOTTOM) { + canvas.drawRect(paddingLeft, height - mUnderlineHeight, mTabsContainer.getWidth() + paddingLeft, height, mRectPaint); + } else { + canvas.drawRect(paddingLeft, 0, mTabsContainer.getWidth() + paddingLeft, mUnderlineHeight, mRectPaint); + } + } + + //draw indicator line + + calcIndicatorRect(); + if (mIndicatorStyle == STYLE_TRIANGLE) { + if (mIndicatorHeight > 0) { + mTrianglePaint.setColor(mIndicatorColor); + mTrianglePath.reset(); + mTrianglePath.moveTo(paddingLeft + mIndicatorRect.left, height); + mTrianglePath.lineTo(paddingLeft + mIndicatorRect.left / 2 + mIndicatorRect.right / 2, height - mIndicatorHeight); + mTrianglePath.lineTo(paddingLeft + mIndicatorRect.right, height); + mTrianglePath.close(); + canvas.drawPath(mTrianglePath, mTrianglePaint); + } + } else if (mIndicatorStyle == STYLE_BLOCK) { + if (mIndicatorHeight < 0) { + mIndicatorHeight = height - mIndicatorMarginTop - mIndicatorMarginBottom; + } else { + + } + + if (mIndicatorHeight > 0) { + if (mIndicatorCornerRadius < 0 || mIndicatorCornerRadius > mIndicatorHeight / 2) { + mIndicatorCornerRadius = mIndicatorHeight / 2; + } + + mIndicatorDrawable.setColor(mIndicatorColor); + mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left, + (int) mIndicatorMarginTop, (int) (paddingLeft + mIndicatorRect.right - mIndicatorMarginRight), + (int) (mIndicatorMarginTop + mIndicatorHeight)); + mIndicatorDrawable.setCornerRadius(mIndicatorCornerRadius); + mIndicatorDrawable.draw(canvas); + } + } else { + /* mRectPaint.setColor(mIndicatorColor); + calcIndicatorRect(); + canvas.drawRect(getPaddingLeft() + mIndicatorRect.left, getHeight() - mIndicatorHeight, + mIndicatorRect.right + getPaddingLeft(), getHeight(), mRectPaint);*/ + + if (mIndicatorHeight > 0) { + mIndicatorDrawable.setColor(mIndicatorColor); + + if (mIndicatorGravity == Gravity.BOTTOM) { + mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left, + height - (int) mIndicatorHeight - (int) mIndicatorMarginBottom, + paddingLeft + mIndicatorRect.right - (int) mIndicatorMarginRight, + height - (int) mIndicatorMarginBottom); + } else { + mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left, + (int) mIndicatorMarginTop, + paddingLeft + mIndicatorRect.right - (int) mIndicatorMarginRight, + (int) mIndicatorHeight + (int) mIndicatorMarginTop); + } + mIndicatorDrawable.setCornerRadius(mIndicatorCornerRadius); + mIndicatorDrawable.draw(canvas); + } + } + } + + //setter and getter + public void setCurrentTab(int currentTab) { + setCurrentTab(currentTab, !mSnapOnTabClick); + } + + public void setCurrentTab(int currentTab, boolean smoothScroll) { + if (mCurrentTab != currentTab) { + this.mCurrentTab = currentTab; + if (mViewPager != null) { + mViewPager.setCurrentItem(currentTab, smoothScroll); + } + + if (mListener != null) { + mListener.onTabSelect(currentTab); + } + } else { + if (mListener != null) { + mListener.onTabReselect(currentTab); + } + } + } + + public void setIndicatorStyle(int indicatorStyle) { + this.mIndicatorStyle = indicatorStyle; + invalidate(); + } + + public void setTabPadding(float tabPadding) { + this.mTabPadding = dp2px(tabPadding); + updateTabStyles(); + } + + public void setTabSpaceEqual(boolean tabSpaceEqual) { + this.mTabSpaceEqual = tabSpaceEqual; + updateTabStyles(); + } + + public void setTabWidth(float tabWidth) { + this.mTabWidth = dp2px(tabWidth); + updateTabStyles(); + } + + public void setIndicatorColor(int indicatorColor) { + this.mIndicatorColor = indicatorColor; + invalidate(); + } + + public void setIndicatorHeight(float indicatorHeight) { + this.mIndicatorHeight = dp2px(indicatorHeight); + invalidate(); + } + + public void setIndicatorWidth(float indicatorWidth) { + this.mIndicatorWidth = dp2px(indicatorWidth); + invalidate(); + } + + public void setIndicatorCornerRadius(float indicatorCornerRadius) { + this.mIndicatorCornerRadius = dp2px(indicatorCornerRadius); + invalidate(); + } + + public void setIndicatorGravity(int indicatorGravity) { + this.mIndicatorGravity = indicatorGravity; + invalidate(); + } + + public void setIndicatorMargin(float indicatorMarginLeft, float indicatorMarginTop, + float indicatorMarginRight, float indicatorMarginBottom) { + this.mIndicatorMarginLeft = dp2px(indicatorMarginLeft); + this.mIndicatorMarginTop = dp2px(indicatorMarginTop); + this.mIndicatorMarginRight = dp2px(indicatorMarginRight); + this.mIndicatorMarginBottom = dp2px(indicatorMarginBottom); + invalidate(); + } + + public void setIndicatorWidthEqualTitle(boolean indicatorWidthEqualTitle) { + this.mIndicatorWidthEqualTitle = indicatorWidthEqualTitle; + invalidate(); + } + + public void setUnderlineColor(int underlineColor) { + this.mUnderlineColor = underlineColor; + invalidate(); + } + + public void setUnderlineHeight(float underlineHeight) { + this.mUnderlineHeight = dp2px(underlineHeight); + invalidate(); + } + + public void setUnderlineGravity(int underlineGravity) { + this.mUnderlineGravity = underlineGravity; + invalidate(); + } + + public void setDividerColor(int dividerColor) { + this.mDividerColor = dividerColor; + invalidate(); + } + + public void setDividerWidth(float dividerWidth) { + this.mDividerWidth = dp2px(dividerWidth); + invalidate(); + } + + public void setDividerPadding(float dividerPadding) { + this.mDividerPadding = dp2px(dividerPadding); + invalidate(); + } + + public void setTextSelectsize(float textsize) { + this.mTextSelectSize = sp2px(textsize); + updateTabStyles(); + } + + public void setTextUnselectSize(int textSize) { + this.mTextUnSelectSize = textSize; + updateTabStyles(); + } + + public void setTextSelectColor(int textSelectColor) { + this.mTextSelectColor = textSelectColor; + updateTabStyles(); + } + + public void setTextUnselectColor(int textUnselectColor) { + this.mTextUnSelectColor = textUnselectColor; + updateTabStyles(); + } + + public void setTextBold(int textBold) { + this.mTextBold = textBold; + updateTabStyles(); + } + + public void setTextAllCaps(boolean textAllCaps) { + this.mTextAllCaps = textAllCaps; + updateTabStyles(); + } + + public void setSnapOnTabClick(boolean snapOnTabClick) { + mSnapOnTabClick = snapOnTabClick; + } + + + public int getTabCount() { + return mTabCount; + } + + public int getCurrentTab() { + return mCurrentTab; + } + + public int getIndicatorStyle() { + return mIndicatorStyle; + } + + public float getTabPadding() { + return mTabPadding; + } + + public boolean isTabSpaceEqual() { + return mTabSpaceEqual; + } + + public float getTabWidth() { + return mTabWidth; + } + + public int getIndicatorColor() { + return mIndicatorColor; + } + + public float getIndicatorHeight() { + return mIndicatorHeight; + } + + public float getIndicatorWidth() { + return mIndicatorWidth; + } + + public float getIndicatorCornerRadius() { + return mIndicatorCornerRadius; + } + + public float getIndicatorMarginLeft() { + return mIndicatorMarginLeft; + } + + public float getIndicatorMarginTop() { + return mIndicatorMarginTop; + } + + public float getIndicatorMarginRight() { + return mIndicatorMarginRight; + } + + public float getIndicatorMarginBottom() { + return mIndicatorMarginBottom; + } + + public int getUnderlineColor() { + return mUnderlineColor; + } + + public float getUnderlineHeight() { + return mUnderlineHeight; + } + + public int getDividerColor() { + return mDividerColor; + } + + public float getDividerWidth() { + return mDividerWidth; + } + + public float getDividerPadding() { + return mDividerPadding; + } + + public float getTextSelectSize() { + return mTextSelectSize; + } + + public float getTextUnselectSize() { + return mTextUnSelectSize; + } + + public int getTextSelectColor() { + return mTextSelectColor; + } + + public int getTextUnselectColor() { + return mTextUnSelectColor; + } + + public int getTextBold() { + return mTextBold; + } + + public boolean isTextAllCaps() { + return mTextAllCaps; + } + + public TextView getTitleView(int tab) { + View tabView = mTabsContainer.getChildAt(tab); + return (TextView) tabView.findViewById(R.id.tv_tab_title); + } + + //setter and getter + + // show MsgTipView + private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private SparseBooleanArray mInitSetMap = new SparseBooleanArray(); + + /** + * 显示未读消息 + * + * @param position 显示tab位置 + * @param num num小于等于0显示红点,num大于0显示数字 + */ + public void showMsg(int position, int num) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + + View tabView = mTabsContainer.getChildAt(position); + MsgView tipView = tabView.findViewById(R.id.rtv_msg_tip); + if (tipView != null) { + UnreadMsgUtils.show(tipView, num); + + if (mInitSetMap.get(position)) { + return; + } + + setMsgMargin(position, 4, 2); + mInitSetMap.put(position, true); + } + } + + /** + * 显示未读红点 + * + * @param position 显示tab位置 + */ + public void showDot(int position) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + showMsg(position, 0); + } + + /** + * 隐藏未读消息 + */ + public void hideMsg(int position) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + + View tabView = mTabsContainer.getChildAt(position); + MsgView tipView = tabView.findViewById(R.id.rtv_msg_tip); + if (tipView != null) { + tipView.setVisibility(View.GONE); + } + } + + /** + * 设置未读消息偏移,原点为文字的右上角.当控件高度固定,消息提示位置易控制,显示效果佳 + */ + public void setMsgMargin(int position, float leftPadding, float bottomPadding) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + View tabView = mTabsContainer.getChildAt(position); + MsgView tipView = tabView.findViewById(R.id.rtv_msg_tip); + if (tipView != null) { + TextView tv_tab_title = tabView.findViewById(R.id.tv_tab_title); + mTextPaint.setTextSize(position == mCurrentTab ? mTextSelectSize : mTextUnSelectSize); + float textWidth = mTextPaint.measureText(tv_tab_title.getText().toString()); + float textHeight = mTextPaint.descent() - mTextPaint.ascent(); + MarginLayoutParams lp = (MarginLayoutParams) tipView.getLayoutParams(); + lp.leftMargin = mTabWidth >= 0 ? (int) (mTabWidth / 2 + textWidth / 2 + dp2px(leftPadding)) : (int) (mTabPadding + textWidth + dp2px(leftPadding)); + lp.topMargin = mHeight > 0 ? (int) (mHeight - textHeight) / 2 - dp2px(bottomPadding) : 0; + tipView.setLayoutParams(lp); + } + } + + /** + * 当前类只提供了少许设置未读消息属性的方法,可以通过该方法获取MsgView对象从而各种设置 + */ + public MsgView getMsgView(int position) { + if (position >= mTabCount) { + position = mTabCount - 1; + } + View tabView = mTabsContainer.getChildAt(position); + MsgView tipView = tabView.findViewById(R.id.rtv_msg_tip); + return tipView; + } + + private OnTabSelectListener mListener; + + public void setOnTabSelectListener(OnTabSelectListener listener) { + this.mListener = listener; + } + + class InnerPagerAdapter extends FragmentPagerAdapter { + private ArrayList fragments; + private String[] titles; + + public InnerPagerAdapter(FragmentManager fm, ArrayList fragments, String[] titles) { + super(fm); + this.fragments = fragments; + this.titles = titles; + } + + @Override + public int getCount() { + return fragments.size(); + } + + @Override + public CharSequence getPageTitle(int position) { + return titles[position]; + } + + @Override + public Fragment getItem(int position) { + return fragments.get(position); + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + // 覆写destroyItem并且空实现,这样每个Fragment中的视图就不会被销毁 + // super.destroyItem(container, position, object); + } + + @Override + public int getItemPosition(Object object) { + return PagerAdapter.POSITION_NONE; + } + } + + @Override + protected Parcelable onSaveInstanceState() { + Bundle bundle = new Bundle(); + bundle.putParcelable("instanceState", super.onSaveInstanceState()); + bundle.putInt("mCurrentTab", mCurrentTab); + return bundle; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (state instanceof Bundle) { + Bundle bundle = (Bundle) state; + mCurrentTab = bundle.getInt("mCurrentTab"); + state = bundle.getParcelable("instanceState"); + if (mCurrentTab != 0 && mTabsContainer.getChildCount() > 0) { + updateTabSelection(mCurrentTab); + scrollToCurrentTab(); + } + } + super.onRestoreInstanceState(state); + } + + protected int dp2px(float dp) { + final float scale = mContext.getResources().getDisplayMetrics().density; + return (int) (dp * scale + 0.5f); + } + + protected int sp2px(float sp) { + final float scale = this.mContext.getResources().getDisplayMetrics().scaledDensity; + return (int) (sp * scale + 0.5f); + } +} diff --git a/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/listener/CustomTabEntity.java b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/listener/CustomTabEntity.java new file mode 100644 index 0000000..ee39fe7 --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/listener/CustomTabEntity.java @@ -0,0 +1,13 @@ +package com.flyco.tablayout.listener; + +import androidx.annotation.DrawableRes; + +public interface CustomTabEntity { + String getTabTitle(); + + @DrawableRes + int getTabSelectedIcon(); + + @DrawableRes + int getTabUnselectedIcon(); +} \ No newline at end of file diff --git a/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/listener/OnTabSelectListener.java b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/listener/OnTabSelectListener.java new file mode 100644 index 0000000..903a5fd --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/listener/OnTabSelectListener.java @@ -0,0 +1,7 @@ +package com.flyco.tablayout.listener; + +public interface OnTabSelectListener { + void onTabSelect(int position); + + void onTabReselect(int position); +} \ No newline at end of file diff --git a/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/transformer/ExtendTransformer.java b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/transformer/ExtendTransformer.java new file mode 100644 index 0000000..2c508aa --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/transformer/ExtendTransformer.java @@ -0,0 +1,53 @@ +package com.flyco.tablayout.transformer; + +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.viewpager.widget.ViewPager; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by li.zhipeng on 2019/1/3. + *

+ * tab切换的 + */ +public class ExtendTransformer implements ViewPager.PageTransformer { + + private ArrayList transformers = new ArrayList<>(); + + + public ExtendTransformer() { + } + + public void addViewPagerTransformer(IViewPagerTransformer transformer) { + if (!transformers.contains(transformer)) { + transformers.add(transformer); + } + } + + public void removeViewPagerTransformer(IViewPagerTransformer transformer) { + transformers.remove(transformer); + } + + public List getTransformers() { + return transformers; + } + + public void setTransformers(List transformers) { + this.transformers.addAll(transformers); + } + + @Override + public void transformPage(@NonNull View view, final float position) { + // 回调设置的页面切换效果设置 + if (transformers != null && transformers.size() > 0) { + for (IViewPagerTransformer transformer : transformers) { + transformer.transformPage(view, position); + } + } + } + + +} diff --git a/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/transformer/ITabScaleTransformer.java b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/transformer/ITabScaleTransformer.java new file mode 100644 index 0000000..145495c --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/transformer/ITabScaleTransformer.java @@ -0,0 +1,7 @@ +package com.flyco.tablayout.transformer; + +public interface ITabScaleTransformer { + void setNormalWidth(int position, int width, boolean isSelect); + + void onPageScrolled(int position, float positionOffset, int positionOffsetPixels); +} diff --git a/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/transformer/IViewPagerTransformer.java b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/transformer/IViewPagerTransformer.java new file mode 100644 index 0000000..e76544b --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/transformer/IViewPagerTransformer.java @@ -0,0 +1,12 @@ +package com.flyco.tablayout.transformer; + +import androidx.viewpager.widget.ViewPager; + +/** + * Created by li.zhipeng on 2019/1/3. + *

+ * ViewPager的扩展Transformer,配合SlidingScaleTabLayout使用 + * 因为字体的切换效果设置了默认的Transformer,所以扩展此接口 + */ +public interface IViewPagerTransformer extends ViewPager.PageTransformer { +} diff --git a/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/transformer/TabScaleTransformer.java b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/transformer/TabScaleTransformer.java new file mode 100644 index 0000000..5757d64 --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/transformer/TabScaleTransformer.java @@ -0,0 +1,110 @@ +package com.flyco.tablayout.transformer; + +import android.util.Log; +import android.util.TypedValue; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.flyco.tablayout.SlidingScaleTabLayout; + +/** + * Created by li.zhipeng on 2019/1/3. + *

+ * tab切换的 + */ +public class TabScaleTransformer implements ITabScaleTransformer { + + private SlidingScaleTabLayout slidingScaleTabLayout; + + private float textSelectSize; + + private float textUnSelectSize; + +// private float maxScale; + + private boolean openDmg; + + public TabScaleTransformer(SlidingScaleTabLayout slidingScaleTabLayout, + float textSelectSize, float textUnSelectSize, boolean openDmg) { + this.slidingScaleTabLayout = slidingScaleTabLayout; + this.textSelectSize = textSelectSize; + this.textUnSelectSize = textUnSelectSize; +// this.maxScale = (textSelectSize / textUnSelectSize) - 1; + this.openDmg = openDmg; + } + + @Override + public void setNormalWidth(int position, int width, boolean isSelect) { + } + + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + Log.i("TabScaleTransformer", "position:" + position); + // 字体大小相同,不需要切换 + if (textSelectSize == textUnSelectSize) return; + if (openDmg) { + for (int i = 0; i < slidingScaleTabLayout.getTabCount(); i++) { + if (i != position && i != position + 1) { + changTabDmgWidth(i, 0); + } + } + changeDmgSize(position, positionOffset); + } else { + for (int i = 0; i < slidingScaleTabLayout.getTabCount(); i++) { + if (i != position && i != position + 1) { + updateTextSize(i, 1); + } + } + changeTextSize(position, positionOffset); + } + } + + private void changeTextSize(final int position, final float positionOffset) { + updateTextSize(position, positionOffset); + if (position + 1 < slidingScaleTabLayout.getTabCount()) { + updateTextSize(position + 1, 1 - positionOffset); + } + } + + private void updateTextSize(final int position, final float positionOffset) { + final TextView currentTab = slidingScaleTabLayout.getTitle(position); + // 必须要在View调用post更新样式,否则可能无效 + currentTab.post(new Runnable() { + @Override + public void run() { + int textSize = (int) (textSelectSize - Math.abs((textSelectSize - textUnSelectSize) * positionOffset)); + if (currentTab.getTextSize() != textSize) { + currentTab.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); + currentTab.requestLayout(); + } + } + }); + } + + private void changeDmgSize(final int position, final float positionOffset) { + slidingScaleTabLayout.post(new Runnable() { + @Override + public void run() { +// Log.i("lzp", "position:" + position + " positionOffset:" + positionOffset); + float scale = 1 - positionOffset; + changTabDmgWidth(position, scale); + if (position + 1 < slidingScaleTabLayout.getTabCount()) { + changTabDmgWidth(position + 1, positionOffset); + } + } + }); + } + + private void changTabDmgWidth(int position, float scale) { + final ImageView currentTabDmg = slidingScaleTabLayout.getDmgView(position); + if (currentTabDmg == null) return; + if (currentTabDmg.getDrawable() == null) return; + ViewGroup.LayoutParams params = currentTabDmg.getLayoutParams(); + int width = (int) (currentTabDmg.getMinimumWidth() + (currentTabDmg.getMaxWidth() - currentTabDmg.getMinimumWidth()) * scale); + if (params.width != width) { + params.width = width; + currentTabDmg.setLayoutParams(params); + } +// Log.i("lzp", "position:" + position + " scale:" + scale + " width:" + params.width); + } +} \ No newline at end of file diff --git a/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/utils/FragmentChangeManager.java b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/utils/FragmentChangeManager.java new file mode 100644 index 0000000..72e5cf5 --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/utils/FragmentChangeManager.java @@ -0,0 +1,63 @@ +package com.flyco.tablayout.utils; + +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; + +import java.util.ArrayList; + +public class FragmentChangeManager { + private FragmentManager mFragmentManager; + private int mContainerViewId; + /** + * Fragment切换数组 + */ + private ArrayList mFragments; + /** + * 当前选中的Tab + */ + private int mCurrentTab; + + public FragmentChangeManager(FragmentManager fm, int containerViewId, ArrayList fragments) { + this.mFragmentManager = fm; + this.mContainerViewId = containerViewId; + this.mFragments = fragments; + initFragments(); + } + + /** + * 初始化fragments + */ + private void initFragments() { + for (Fragment fragment : mFragments) { + mFragmentManager.beginTransaction().add(mContainerViewId, fragment).hide(fragment).commit(); + } + + setFragments(0); + } + + /** + * 界面切换控制 + */ + public void setFragments(int index) { + for (int i = 0; i < mFragments.size(); i++) { + FragmentTransaction ft = mFragmentManager.beginTransaction(); + Fragment fragment = mFragments.get(i); + if (i == index) { + ft.show(fragment); + } else { + ft.hide(fragment); + } + ft.commit(); + } + mCurrentTab = index; + } + + public int getCurrentTab() { + return mCurrentTab; + } + + public Fragment getCurrentFragment() { + return mFragments.get(mCurrentTab); + } +} \ No newline at end of file diff --git a/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/utils/UnreadMsgUtils.java b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/utils/UnreadMsgUtils.java new file mode 100644 index 0000000..0446bfa --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/utils/UnreadMsgUtils.java @@ -0,0 +1,58 @@ +package com.flyco.tablayout.utils; + + +import android.util.DisplayMetrics; +import android.view.View; +import android.widget.RelativeLayout; + +import com.flyco.tablayout.widget.MsgView; + +/** + * 未读消息提示View,显示小红点或者带有数字的红点: + * 数字一位,圆 + * 数字两位,圆角矩形,圆角是高度的一半 + * 数字超过两位,显示99+ + */ +public class UnreadMsgUtils { + public static void show(MsgView msgView, int num) { + if (msgView == null) { + return; + } + RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) msgView.getLayoutParams(); + DisplayMetrics dm = msgView.getResources().getDisplayMetrics(); + msgView.setVisibility(View.VISIBLE); + if (num <= 0) {//圆点,设置默认宽高 + msgView.setStrokeWidth(0); + msgView.setText(""); + + lp.width = (int) (5 * dm.density); + lp.height = (int) (5 * dm.density); + msgView.setLayoutParams(lp); + } else { + lp.height = (int) (18 * dm.density); + if (num < 10) {//圆 + lp.width = (int) (18 * dm.density); + msgView.setText(num + ""); + } else if (num < 100) {//圆角矩形,圆角是高度的一半,设置默认padding + lp.width = RelativeLayout.LayoutParams.WRAP_CONTENT; + msgView.setPadding((int) (6 * dm.density), 0, (int) (6 * dm.density), 0); + msgView.setText(num + ""); + } else {//数字超过两位,显示99+ + lp.width = RelativeLayout.LayoutParams.WRAP_CONTENT; + msgView.setPadding((int) (6 * dm.density), 0, (int) (6 * dm.density), 0); + msgView.setText("99+"); + } + msgView.setLayoutParams(lp); + } + } + + public static void setSize(MsgView rtv, int size) { + if (rtv == null) { + return; + } + RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) rtv.getLayoutParams(); + lp.width = size; + lp.height = size; + rtv.setLayoutParams(lp); + } +} diff --git a/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/utils/ViewUtils.java b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/utils/ViewUtils.java new file mode 100644 index 0000000..18ea77e --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/utils/ViewUtils.java @@ -0,0 +1,40 @@ +package com.flyco.tablayout.utils; + +import android.graphics.Bitmap; +import android.view.View; + +import androidx.annotation.IdRes; + +public class ViewUtils { + + public static Bitmap generateViewCacheBitmap(View view) { + view.destroyDrawingCache(); + int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + view.measure(widthMeasureSpec, heightMeasureSpec); + int width = view.getMeasuredWidth(); + int height = view.getMeasuredHeight(); + view.layout(0, 0, width, height); + view.setDrawingCacheEnabled(true); + view.buildDrawingCache(); + return Bitmap.createBitmap(view.getDrawingCache()); + } + + public static View findBrotherView(View view, @IdRes int id, int level) { + int count = 0; + View temp = view; + while (count < level) { + View target = temp.findViewById(id); + if (target != null) { + return target; + } + count += 1; + if (temp.getParent() instanceof View) { + temp = (View) temp.getParent(); + } else { + break; + } + } + return null; + } +} diff --git a/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/widget/MsgView.java b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/widget/MsgView.java new file mode 100644 index 0000000..d7c78f4 --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/java/com/flyco/tablayout/widget/MsgView.java @@ -0,0 +1,159 @@ +package com.flyco.tablayout.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.StateListDrawable; +import android.os.Build; +import android.util.AttributeSet; +import android.widget.TextView; + +import com.flyco.tablayout.R; + +/** + * 用于需要圆角矩形框背景的TextView的情况,减少直接使用TextView时引入的shape资源文件 + */ +public class MsgView extends TextView { + private Context context; + private GradientDrawable gd_background = new GradientDrawable(); + private int backgroundColor; + private int cornerRadius; + private int strokeWidth; + private int strokeColor; + private boolean isRadiusHalfHeight; + private boolean isWidthHeightEqual; + + public MsgView(Context context) { + this(context, null); + } + + public MsgView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public MsgView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + this.context = context; + obtainAttributes(context, attrs); + } + + private void obtainAttributes(Context context, AttributeSet attrs) { + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MsgView); + backgroundColor = ta.getColor(R.styleable.MsgView_mv_backgroundColor, Color.TRANSPARENT); + cornerRadius = ta.getDimensionPixelSize(R.styleable.MsgView_mv_cornerRadius, 0); + strokeWidth = ta.getDimensionPixelSize(R.styleable.MsgView_mv_strokeWidth, 0); + strokeColor = ta.getColor(R.styleable.MsgView_mv_strokeColor, Color.TRANSPARENT); + isRadiusHalfHeight = ta.getBoolean(R.styleable.MsgView_mv_isRadiusHalfHeight, false); + isWidthHeightEqual = ta.getBoolean(R.styleable.MsgView_mv_isWidthHeightEqual, false); + + ta.recycle(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (isWidthHeightEqual() && getWidth() > 0 && getHeight() > 0) { + int max = Math.max(getWidth(), getHeight()); + int measureSpec = MeasureSpec.makeMeasureSpec(max, MeasureSpec.EXACTLY); + super.onMeasure(measureSpec, measureSpec); + return; + } + + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (isRadiusHalfHeight()) { + setCornerRadius(getHeight() / 2); + } else { + setBgSelector(); + } + } + + + public void setBackgroundColor(int backgroundColor) { + this.backgroundColor = backgroundColor; + setBgSelector(); + } + + public void setCornerRadius(int cornerRadius) { + this.cornerRadius = dp2px(cornerRadius); + setBgSelector(); + } + + public void setStrokeWidth(int strokeWidth) { + this.strokeWidth = dp2px(strokeWidth); + setBgSelector(); + } + + public void setStrokeColor(int strokeColor) { + this.strokeColor = strokeColor; + setBgSelector(); + } + + public void setIsRadiusHalfHeight(boolean isRadiusHalfHeight) { + this.isRadiusHalfHeight = isRadiusHalfHeight; + setBgSelector(); + } + + public void setIsWidthHeightEqual(boolean isWidthHeightEqual) { + this.isWidthHeightEqual = isWidthHeightEqual; + setBgSelector(); + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public int getCornerRadius() { + return cornerRadius; + } + + public int getStrokeWidth() { + return strokeWidth; + } + + public int getStrokeColor() { + return strokeColor; + } + + public boolean isRadiusHalfHeight() { + return isRadiusHalfHeight; + } + + public boolean isWidthHeightEqual() { + return isWidthHeightEqual; + } + + protected int dp2px(float dp) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (dp * scale + 0.5f); + } + + protected int sp2px(float sp) { + final float scale = this.context.getResources().getDisplayMetrics().scaledDensity; + return (int) (sp * scale + 0.5f); + } + + private void setDrawable(GradientDrawable gd, int color, int strokeColor) { + gd.setColor(color); + gd.setCornerRadius(cornerRadius); + gd.setStroke(strokeWidth, strokeColor); + } + + public void setBgSelector() { + StateListDrawable bg = new StateListDrawable(); + + setDrawable(gd_background, backgroundColor, strokeColor); + bg.addState(new int[]{-android.R.attr.state_pressed}, gd_background); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {//16 + setBackground(bg); + } else { + //noinspection deprecation + setBackgroundDrawable(bg); + } + } +} diff --git a/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_scale_tab.xml b/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_scale_tab.xml new file mode 100644 index 0000000..1533d51 --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_scale_tab.xml @@ -0,0 +1,38 @@ + + + + + + + + + + \ No newline at end of file diff --git a/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab.xml b/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab.xml new file mode 100644 index 0000000..05d3232 --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_bottom.xml b/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_bottom.xml new file mode 100644 index 0000000..17ba69a --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_bottom.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_left.xml b/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_left.xml new file mode 100644 index 0000000..b5db8fb --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_left.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_right.xml b/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_right.xml new file mode 100644 index 0000000..572f9a5 --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_right.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_segment.xml b/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_segment.xml new file mode 100644 index 0000000..965cdcd --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_segment.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_top.xml b/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_top.xml new file mode 100644 index 0000000..210ee30 --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/res/layout/layout_tab_top.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/FlycoTabLayoutZ_Lib/src/main/res/values/attrs.xml b/FlycoTabLayoutZ_Lib/src/main/res/values/attrs.xml new file mode 100644 index 0000000..324c0ca --- /dev/null +++ b/FlycoTabLayoutZ_Lib/src/main/res/values/attrs.xml @@ -0,0 +1,319 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index b236b8b..b19725c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,4 @@ apply plugin: 'com.android.application' -apply plugin: "com.tencent.android.tpns" static def appName() { return "FLYSN" @@ -78,8 +77,8 @@ android { official { flavorDimensions "default" - versionCode 87 - versionName "3.4.7" + versionCode 88 + versionName "3.4.8" } } @@ -208,7 +207,7 @@ android { MT6789Debug.initWith(debug) MT6789Debug { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "63e2c211ad3bf161969582a58cf3c598" ] buildConfigField "String", "platform", '"G12NL"' versionNameSuffix "-debug" @@ -219,7 +218,7 @@ android { MT6789Release.initWith(release) MT6789Release { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "63e2c211ad3bf161969582a58cf3c598" ] buildConfigField "String", "platform", '"G12NL"' signingConfig signingConfigs.MT6789 @@ -228,7 +227,7 @@ android { Huaruian8768Debug.initWith(debug) Huaruian8768Debug { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "a4d58ed320000359544e99e1189438c7" ] buildConfigField "String", "platform", '"G10J"' versionNameSuffix "-debug" @@ -239,7 +238,7 @@ android { Huaruian8768Release.initWith(release) Huaruian8768Release { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "a4d58ed320000359544e99e1189438c7" ] buildConfigField "String", "platform", '"G10J"' signingConfig signingConfigs.Huaruian8768 @@ -248,7 +247,7 @@ android { iPlay50SEDebug.initWith(debug) iPlay50SEDebug { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "adfc5099b8305d05bff03dcfd6579640" ] buildConfigField "String", "platform", '"ipaly50"' versionNameSuffix "-debug" @@ -259,7 +258,7 @@ android { iPlay50SERelease.initWith(release) iPlay50SERelease { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "adfc5099b8305d05bff03dcfd6579640" ] buildConfigField "String", "platform", '"ipaly50"' signingConfig signingConfigs.iPlay50SE @@ -268,7 +267,7 @@ android { unisocDebug.initWith(debug) unisocDebug { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "a45f52890415873d7fe97731b9ff27a7" ] buildConfigField "String", "platform", '"ZhanRui"' versionNameSuffix "-debug" @@ -279,7 +278,7 @@ android { unisocRelease.initWith(debug) unisocRelease { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "a45f52890415873d7fe97731b9ff27a7" ] buildConfigField "String", "platform", '"ZhanRui"' debuggable true @@ -289,7 +288,7 @@ android { mtk11Debug.initWith(debug) mtk11Debug { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "2faeb0552117ccbbeb2fd63afd5d71f3" ] buildConfigField "String", "platform", '"MTK"' versionNameSuffix "-debug" @@ -300,7 +299,7 @@ android { mtk11Release.initWith(release) mtk11Release { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "2faeb0552117ccbbeb2fd63afd5d71f3" ] buildConfigField "String", "platform", '"MTK"' signingConfig signingConfigs.mtkAndroid11 @@ -309,7 +308,7 @@ android { teclastMTKDebug.initWith(debug) teclastMTKDebug { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "4ba0125745b17d0c73a6638dcda43d2f" ] buildConfigField "String", "platform", '"T30PRO"' versionNameSuffix "-debug" @@ -320,7 +319,7 @@ android { teclastMTKRelease.initWith(release) teclastMTKRelease { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "4ba0125745b17d0c73a6638dcda43d2f" ] buildConfigField "String", "platform", '"T30PRO"' signingConfig signingConfigs.teclastMTK @@ -329,7 +328,7 @@ android { G6Debug.initWith(debug) G6Debug { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "418069ea951c42cdaa39a4d2015f23b9" ] buildConfigField "String", "platform", '"G6"' versionNameSuffix "-debug" @@ -340,7 +339,7 @@ android { G6Release.initWith(release) G6Release { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "418069ea951c42cdaa39a4d2015f23b9" ] buildConfigField "String", "platform", '"G6"' signingConfig signingConfigs.G10P @@ -349,7 +348,7 @@ android { G13Debug.initWith(debug) G13Debug { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "418069ea951c42cdaa39a4d2015f23b9" ] buildConfigField "String", "platform", '"G13"' versionNameSuffix "-debug" @@ -360,7 +359,7 @@ android { G13Release.initWith(release) G13Release { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "418069ea951c42cdaa39a4d2015f23b9" ] buildConfigField "String", "platform", '"G13"' signingConfig signingConfigs.G10P @@ -369,7 +368,7 @@ android { Teclast8515Debug.initWith(debug) Teclast8515Debug { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "29db21d44be7ae2cadb6ea7189ebec72" ] buildConfigField "String", "platform", '"8515"' versionNameSuffix "-debug" @@ -380,7 +379,7 @@ android { Teclast8515Release.initWith(release) Teclast8515Release { manifestPlaceholders = [ - AK: "7IubK1Ugeuxga4KKC5VQyjTeQlExsYZq" + AMAP_KEY: "29db21d44be7ae2cadb6ea7189ebec72" ] buildConfigField "String", "platform", '"8515"' signingConfig signingConfigs.Teclast8515 @@ -390,7 +389,7 @@ android { teclastUnisocdebug.initWith(zhanRuiDebug) teclastUnisocdebug { manifestPlaceholders = [ - AK: "u5qGsILQ7qlXI8GDv6T6cGl8IWMtsPSu" + AMAP_KEY: "ef180363f53f3ea07af84be26b797f5d" ] buildConfigField "String", "platform", '"M40SE"' versionNameSuffix "-debug" @@ -401,7 +400,7 @@ android { teclastUnisocrelease.initWith(zhanRuiDebug) teclastUnisocrelease { manifestPlaceholders = [ - AK: "u5qGsILQ7qlXI8GDv6T6cGl8IWMtsPSu" + AMAP_KEY: "ef180363f53f3ea07af84be26b797f5d" ] buildConfigField "String", "platform", '"M40SE"' signingConfig signingConfigs.teclastUnisoc @@ -410,7 +409,7 @@ android { teclastUnisocUserdebug.initWith(zhanRuiDebug) teclastUnisocUserdebug { manifestPlaceholders = [ - AK: "u5qGsILQ7qlXI8GDv6T6cGl8IWMtsPSu" + AMAP_KEY: "a89c464a7e619791d65bf683602b9e30" ] buildConfigField "String", "platform", '"M40SE"' versionNameSuffix "-debug" @@ -421,7 +420,7 @@ android { zhanRuiDebug.initWith(debug) zhanRuiDebug { manifestPlaceholders = [ - AK: "EiqcGW9LWDvfMKl7mZxUtt1UkNKpdQI5" + AMAP_KEY: "63d3bf78053d530926309bb25e007e1a" ] buildConfigField "String", "platform", '"ZhanRui"' versionNameSuffix "-debug" @@ -432,7 +431,7 @@ android { zhanRuiRelease.initWith(release) zhanRuiRelease { manifestPlaceholders = [ - AK: "EiqcGW9LWDvfMKl7mZxUtt1UkNKpdQI5" + AMAP_KEY: "63d3bf78053d530926309bb25e007e1a" ] buildConfigField "String", "platform", '"ZhanRui"' signingConfig signingConfigs.zhanRui @@ -441,7 +440,7 @@ android { zhanRuiUserdebugReleas.initWith(debug) zhanRuiUserdebugReleas { manifestPlaceholders = [ - AK: "EiqcGW9LWDvfMKl7mZxUtt1UkNKpdQI5" + AMAP_KEY: "4ebbc3a36c028f6749fed5257f17c77e" ] buildConfigField "String", "platform", '"ZhanRui"' signingConfig signingConfigs.zhanRuiUserdebug @@ -450,6 +449,9 @@ android { debug { buildConfigField "String", "platform", '"MTK"' versionNameSuffix "-debug" + manifestPlaceholders = [ + AMAP_KEY: "9b4bdfa60aaacaf490356fc2e4e3ad0a" + ] //Zipalign优化 zipAlignEnabled true minifyEnabled false @@ -457,7 +459,7 @@ android { applicationVariants.all { variant -> variant.outputs.each { output -> if (outputFile != null) { - def fileName = "${appName()}-${variant.versionCode}-V${variant.versionName}-${releaseTime()}-${buildType.name}.apk" + def fileName = "${appName()}_${variant.versionCode}_V${variant.versionName}_${releaseTime()}_${buildType.name}.apk" output.outputFileName = fileName } } @@ -466,6 +468,9 @@ android { release { buildConfigField "String", "platform", '"MTK"' + manifestPlaceholders = [ + AMAP_KEY: "9b4bdfa60aaacaf490356fc2e4e3ad0a" + ] //Zipalign优化 zipAlignEnabled true //混淆 @@ -479,7 +484,7 @@ android { variant.outputs.each { output -> def outputFile = "" if (outputFile != null) { - def fileName = "${appName()}-${variant.versionCode}-V${variant.versionName}-${releaseTime()}-${buildType.name}.apk" + def fileName = "${appName()}_${variant.versionCode}_V${variant.versionName}_${releaseTime()}_${buildType.name}.apk" output.outputFileName = new File(outputFile, fileName) } } @@ -499,6 +504,8 @@ dependencies { // implementation fileTree(dir: 'libs', include: ['*.jar']) compileOnly files('libs/framework.jar') + implementation project(path: ':FlycoTabLayoutZ_Lib') + //implementation 'com.android.support:multidex:1.0.3' implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' @@ -510,7 +517,7 @@ dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' //okhttp - implementation 'com.squareup.okhttp3:okhttp:4.10.0' + implementation 'com.squareup.okhttp3:okhttp:4.8.1' //Retrofit implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' @@ -524,6 +531,10 @@ dependencies { implementation 'com.trello.rxlifecycle4:rxlifecycle-components:4.0.2' implementation 'com.trello.rxlifecycle4:rxlifecycle-components-preference:4.0.2' implementation 'com.trello.rxlifecycle4:rxlifecycle-android-lifecycle:4.0.2' + + implementation 'com.jakewharton.rxbinding4:rxbinding:4.0.0' + implementation 'com.jeremyliao:live-event-bus-x:1.7.3' + //Google implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.google.zxing:core:3.5.0' @@ -541,18 +552,19 @@ dependencies { implementation 'com.tencent:mmkv-static:1.2.13' //bugly implementation 'com.tencent.bugly:crashreport:4.1.9.2' - //腾讯移动推送 TPNS - implementation 'com.tencent.tpns:tpns:1.4.4.2-release' +// //腾讯移动推送 TPNS +// implementation 'com.tencent.tpns:tpns:1.4.4.2-release' //阿里云推送 implementation 'com.aliyun.ams:alicloud-android-push:3.8.0' - //百度地图 - implementation 'com.baidu.lbsyun:BaiduMapSDK_Location:9.1.8' +// //百度地图 +// implementation 'com.baidu.lbsyun:BaiduMapSDK_Location:9.1.8' + //高德地图定位 + implementation 'com.amap.api:location:5.1.0' //工具类 implementation 'com.blankj:utilcodex:1.31.0' //autosize会改变第三方view的大小 //https://github.com/JessYanCoding/AndroidAutoSize //implementation 'me.jessyan:autosize:1.2.1' - implementation 'com.flyco.tablayout:FlycoTabLayout_Lib:2.1.2@aar' implementation 'com.github.chrisbanes:PhotoView:2.0.0' //沉浸状态栏 implementation 'com.gitee.zackratos:UltimateBarX:0.8.0' @@ -561,6 +573,8 @@ dependencies { // implementation 'com.king.view:splitedittext:1.0.0' //动态权限框架 // implementation 'com.hjq:xxpermissions:6.0' + // 吐司框架:https://github.com/getActivity/Toaster + implementation 'com.github.getActivity:Toaster:12.6' } preBuild { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a6aabb9..a066575 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -50,7 +50,7 @@ - + @@ -73,13 +73,6 @@ - - - - - @@ -104,20 +97,33 @@ android:theme="@style/ImmerseTheme" android:usesCleartextTraffic="true" tools:replace="android:allowBackup"> + + + + + + + + + + + android:name=".activity.home.HomeActivity" + android:launchMode="singleTask"> + + + android:theme="@style/DialogCloseOnTouchOutside" /> - - - - - - - - - - - - - - - - @@ -266,19 +253,16 @@ - - - - + + + + android:name="com.amap.api.v2.apikey" + android:value="${AMAP_KEY}" /> implements NetworkUtils.OnNetworkStatusChangedListener { + private static final String TAG = "MainActivity"; + + private String[] mAllTitle = new String[]{ +// "使用统计", + "设备信息",}; + + private List mFragments; + private FragmentManager mFragmentManager; + private BaseFragmentPagerAdapter mBaseFragmentPagerAdapter; + private ViewPager.OnPageChangeListener mListener; + private int defaultCurrent = 0; + + @Override + public boolean setNightMode() { + return true; + } + + @Override + public void onDisconnected() { + Log.e(TAG, "onDisconnected: "); + } + + @Override + public void onConnected(NetworkUtils.NetworkType networkType) { + Log.e(TAG, "onConnected: "); + } + + @Override + public int getLayoutId() { + return R.layout.activity_home; + } + + @Override + protected void initDataBinding() { + mViewModel.setCtx(this); + mViewModel.setLifecycle(getLifecycleSubject()); + mViewModel.setVDBinding(mViewDataBinding); + mViewDataBinding.setClick(new BtnClick()); + } + + @Override + public void initView() { + mFragments = new ArrayList<>(); +// mFragments.add(new UsageFragment()); + mFragments.add(new DeviceFragment()); + mFragmentManager = getSupportFragmentManager(); + mBaseFragmentPagerAdapter = new BaseFragmentPagerAdapter(mFragmentManager, mFragments); + mViewDataBinding.viewPager.setAdapter(mBaseFragmentPagerAdapter); + mViewDataBinding.viewPager.setOnPageChangeListener(mListener); + mViewDataBinding.viewPager.setOffscreenPageLimit(4); + mViewDataBinding.mainSlidingTabLayout.setViewPager(mViewDataBinding.viewPager, mAllTitle); + } + + @Override + public void initData() { + mViewModel.mSnInfoData.observe(this, new androidx.lifecycle.Observer>() { + @Override + public void onChanged(BaseResponse snInfoBaseResponse) { + if (snInfoBaseResponse != null) { + //设备已经绑定 + if (snInfoBaseResponse.code == 200) { + SnInfo snInfo = snInfoBaseResponse.data; + NetInterfaceManager.getInstance().checkDeviceType(snInfo.getType_id()); +// mViewDataBinding.tvBindStatu.setText("设备已绑定"); + } + //设备没有绑定 + else if (snInfoBaseResponse.code == 300) { + ControlManager.getInstance().setDisableSetting(); +// mViewDataBinding.tvBindStatu.setText("设备未绑定"); + } + //没有授权的设备 + else if (snInfoBaseResponse.code == 400) { + ToastUtil.show(getString(R.string.device_unauthorized)); + Log.e(TAG, "setSnInfo: " + getString(R.string.device_unauthorized)); + ControlManager.getInstance().setDisableSetting(); +// mViewDataBinding.tvBindStatu.setText("设备未绑定"); + } + } + mViewModel.getAllApp(); + mViewModel.checkUpdate(); + mViewModel.checkFXYUpdate(); + mViewModel.getOverallApp(); + } + }); + } + + @Override + protected void onResume() { + super.onResume(); + Log.e(TAG, "onResume: "); + } + + @Override + protected void onPause() { + super.onPause(); + Log.e(TAG, "onPause: "); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + Log.e(TAG, "onDestroy: "); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + Log.e(TAG, "onNewIntent: "); + + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == KeyEvent.ACTION_DOWN) { + lazyExit(); + return true; + } + return super.onKeyDown(keyCode, event); + } + + private boolean isDebugMode() { + if (DeviceManager.isDebugMode()) { + return true; + } else { + return (boolean) SPUtils.get(this, CommonConfig.userDebugMode, false); + } + } + + final static int DEBUGCOUNTS = 8;//点击次数 + final static long DEBUGDURATION = 4 * 1000;//规定有效时间 + long[] mDEBUGHits = new long[DEBUGCOUNTS]; + + private void enterUserDebug() { + //每次点击时,数组向前移动一位 + System.arraycopy(mDEBUGHits, 1, mDEBUGHits, 0, mDEBUGHits.length - 1); + //为数组最后一位赋值 + mDEBUGHits[mDEBUGHits.length - 1] = SystemClock.uptimeMillis(); + if (mDEBUGHits[0] >= SystemClock.uptimeMillis() - DEBUGDURATION) { + mDEBUGHits = new long[DEBUGCOUNTS]; //重新初始化数组 + if ((boolean) SPUtils.get(HomeActivity.this, CommonConfig.EnableDebug, false)) { + ToastUtil.show("已经是Debug模式"); + } else { + showDebugDialog(); + } + } + } + + private void showDebugDialog() { + UserDebugDialog dialog = new UserDebugDialog(this); + dialog.setOnClickBottomListener(new UserDebugDialog.OnClickBottomListener() { + @Override + public void onPositiveClick() { + if ("53680320".equals(dialog.getEdittext())) { + SPUtils.put(HomeActivity.this, CommonConfig.EnableDebug, true); + ToastUtil.show("进入用户Debug模式"); + } else { + ToastUtil.show("密码错误"); + } + dialog.dismiss(); + } + + @Override + public void onNegtiveClick() { + dialog.dismiss(); + } + }); + dialog.show(); + dialog.getWindow().setGravity(Gravity.CENTER); + dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); + dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + } + + + private long mPreClickTime; + + private void lazyExit() { + if (System.currentTimeMillis() - mPreClickTime > 3000) { + ToastUtil.show("再按一次,退出"); + mPreClickTime = System.currentTimeMillis(); + } else { + finish(); + } + } + + private final int COUNTS = 4; // 点击次数 + private final long DURATION = 2 * 1000; // 规定有效时间 + private long[] mHits = new long[COUNTS]; + + private void continuousClick(int count, long time) { + //每次点击时,数组向前移动一位 + System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1); + //为数组最后一位赋值 + mHits[mHits.length - 1] = SystemClock.uptimeMillis(); + if (mHits[0] >= SystemClock.uptimeMillis() - DURATION) { + mHits = new long[COUNTS]; //重新初始化数组 + ToastUtil.show("正在检查更新"); + } + } + + + public class BtnClick { + + public void exit(View view) { + lazyExit(); + } + + public void debug(View view) { + if (isDebugMode()) { + enterUserDebug(); + } + } + + + } + +} diff --git a/app/src/main/java/com/fuying/sn/activity/home/HomeViewModel.java b/app/src/main/java/com/fuying/sn/activity/home/HomeViewModel.java new file mode 100644 index 0000000..b73ed00 --- /dev/null +++ b/app/src/main/java/com/fuying/sn/activity/home/HomeViewModel.java @@ -0,0 +1,109 @@ +package com.fuying.sn.activity.home; + +import android.graphics.Bitmap; +import android.util.Log; + +import androidx.lifecycle.MutableLiveData; + +import com.fuying.sn.base.mvvm.BaseViewModel; +import com.fuying.sn.bean.BaseResponse; +import com.fuying.sn.bean.SnInfo; +import com.fuying.sn.config.CommonConfig; +import com.fuying.sn.databinding.ActivityHomeBinding; +import com.fuying.sn.network.NetInterfaceManager; +import com.fuying.sn.utils.CXAESUtil; +import com.fuying.sn.utils.Utils; +import com.trello.rxlifecycle4.android.ActivityEvent; + +import io.reactivex.rxjava3.disposables.Disposable; + +public class HomeViewModel extends BaseViewModel { + + @Override + public ActivityHomeBinding getVDBinding() { + return binding; + } + + @Override + public void onDestroy() { + + } + + + public MutableLiveData> mSnInfoData = new MutableLiveData<>(); + + public void getSnInfo() { + NetInterfaceManager.getInstance() + .getSnInfo(getLifecycle(), new NetInterfaceManager.ObserverCallback() { + @Override + public void onSubscribe(Disposable d) { + Log.e("getSnInfo", "onSubscribe: "); + } + + @Override + public void onNext(BaseResponse response) { + Log.e("getSnInfo", "onNext: " + response); + mSnInfoData.setValue(response); + } + + @Override + public void onError(Throwable e) { + Log.e("getSnInfo", "onError: " + e.getMessage()); +// String jsonString = mCacheHelper.getAsString(UrlAddress.SN_INFO); +// Gson gson = new Gson(); +// Type type = new TypeToken>() { +// }.getType(); +// BaseResponse userInfoBaseResponse = gson.fromJson(jsonString, type); +// mSnInfoData.setValue(userInfoBaseResponse); + } + + @Override + public void onComplete() { + Log.e("getSnInfo", "onComplete: "); + } + }); + } + + public MutableLiveData mQRCodeData = new MutableLiveData<>(); + + public void getQRCode() { + String encryptString = CXAESUtil.encrypt(CommonConfig.AES_KEY, Utils.getIMEI(getCtx())); + Log.e("getQRCode", "setImageAndText: " + encryptString); + Bitmap bitmap = Utils.createQRImage(encryptString, 400, 400); + mQRCodeData.setValue(bitmap); + } + + public void getAllApp() { + NetInterfaceManager.getInstance().getAppWhiteList(new NetInterfaceManager.WhiteListCallback() { + @Override + public void onComplet() { + } + }); + } + + public void checkUpdate() { + NetInterfaceManager.getInstance().checkAllAppUpdate(true, getLifecycle(), new NetInterfaceManager.CompleteCallback() { + @Override + public void onComplete() { + } + }); + } + + public void checkFXYUpdate() { +// NetInterfaceManager.getInstance().checkFXYAppUpdate(true, getLifecycle(), new NetInterfaceManager.CompleteCallback() { +// @Override +// public void onComplete() { +// mView.checkFXYUpdateFinish(); +// } +// }); + } + + public void getOverallApp() { + NetInterfaceManager.getInstance().getOverallApp(true, getLifecycle(), new NetInterfaceManager.CompleteCallback() { + @Override + public void onComplete() { + + } + }); + } +} diff --git a/app/src/main/java/com/fuying/sn/activity/main/MainActivity.java b/app/src/main/java/com/fuying/sn/activity/main/MainActivity.java index 6b5ec8c..d1fb33d 100644 --- a/app/src/main/java/com/fuying/sn/activity/main/MainActivity.java +++ b/app/src/main/java/com/fuying/sn/activity/main/MainActivity.java @@ -46,7 +46,7 @@ import com.fuying.sn.service.GuardService; import com.fuying.sn.service.ManagerService; import com.fuying.sn.service.StepService; import com.fuying.sn.service.main.MainService; -import com.fuying.sn.utils.JGYUtils; +import com.fuying.sn.utils.JgyUtils; import com.fuying.sn.utils.SPUtils; import com.fuying.sn.utils.TimeUtils; import com.fuying.sn.utils.ToastUtil; @@ -110,9 +110,9 @@ public class MainActivity extends BaseMvvmActivity>() { }.getType(); @@ -109,6 +109,7 @@ public class MainViewModel extends BaseViewModel { + + private AppInfo mAppInfoData; + + + @Override + protected int getLayoutId() { + return R.layout.activity_update; + } + + @Override + protected void initDataBinding() { + mViewModel.setCtx(this); + mViewModel.setLifecycle(getLifecycleSubject()); + mViewModel.setVDBinding(mViewDataBinding); + mViewDataBinding.setClick(new BtnClick()); + } + + @Override + protected void initView() { + + } + + @Override + protected void initData() { + Intent intent = getIntent(); + mAppInfoData = (AppInfo) intent.getSerializableExtra("appInfo"); + mViewDataBinding.setAppUpdateInfo(mAppInfoData); + mViewDataBinding.setMsg("检测到新版本,是否更新"); + } + + + public class BtnClick { + public void empty(View view) { + + } + + public void exit(View view) { + finish(); + } + + public void upgrade(View view) { + Intent intent = new Intent(UpdateActivity.this, DownloadService.class); +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { +// startForegroundService(intent); +// } else { + startService(intent); +// } + if (mAppInfoData != null) { + DownloadEntity entity = Aria.download(this).getFirstDownloadEntity(mAppInfoData.getApp_url()); + if (null != entity) { + if (entity.isComplete()) { + ApkUtils.installApp(UpdateActivity.this, entity.getFilePath()); + } else { + if (entity.getState() == STATE_RUNNING) { + Toaster.show("文件正在下载中"); + finish(); + } else { + Aria.download(this).resumeAllTask(); + Toaster.show("正在下载"); + finish(); + } + } + } else { + ApkUtils.checkAppUpdate(UpdateActivity.this, mAppInfoData); + Toaster.show("正在下载更新"); + finish(); + } + } + + } + } +} diff --git a/app/src/main/java/com/fuying/sn/activity/update/UpdateViewModel.java b/app/src/main/java/com/fuying/sn/activity/update/UpdateViewModel.java new file mode 100644 index 0000000..9d693fa --- /dev/null +++ b/app/src/main/java/com/fuying/sn/activity/update/UpdateViewModel.java @@ -0,0 +1,18 @@ +package com.fuying.sn.activity.update; + +import com.fuying.sn.base.mvvm.BaseViewModel; +import com.fuying.sn.databinding.ActivityUpdateBinding; +import com.trello.rxlifecycle4.android.ActivityEvent; + +public class UpdateViewModel extends BaseViewModel { + + @Override + public ActivityUpdateBinding getVDBinding() { + return binding; + } + + @Override + public void onDestroy() { + + } +} diff --git a/app/src/main/java/com/fuying/sn/base/BaseApplication.java b/app/src/main/java/com/fuying/sn/base/BaseApplication.java index e2bf372..6cc0cae 100644 --- a/app/src/main/java/com/fuying/sn/base/BaseApplication.java +++ b/app/src/main/java/com/fuying/sn/base/BaseApplication.java @@ -31,18 +31,13 @@ import com.fuying.sn.service.ManagerService; import com.fuying.sn.service.StepService; import com.fuying.sn.service.main.MainService; import com.fuying.sn.utils.AppUsedTimeUtils; -import com.fuying.sn.utils.JGYUtils; +import com.fuying.sn.utils.JgyUtils; import com.fuying.sn.utils.SystemUtils; import com.fuying.sn.utils.Utils; -import com.tencent.android.tpush.XGIOperateCallback; -import com.tencent.android.tpush.XGPushConfig; -import com.tencent.android.tpush.XGPushManager; +import com.hjq.toast.Toaster; import com.tencent.bugly.crashreport.CrashReport; import com.tencent.mmkv.MMKV; -import java.util.ArrayList; -import java.util.List; - public class BaseApplication extends Application { private static final String TAG = "BaseApplication"; @@ -79,9 +74,9 @@ public class BaseApplication extends Application { PushManager.init(this); aliyunPushInit(); - tPushInit(); - JGYUtils.init(this); + Toaster.init(this); + JgyUtils.init(this); CrashReport.initCrashReport(getApplicationContext(), "e5f026c8d5", false); CrashReport.setDeviceId(this, Utils.getSerial()); @@ -97,7 +92,7 @@ public class BaseApplication extends Application { AmapManager.init(this); NetInterfaceManager.init(this); AppUsedTimeUtils.init(this); - JGYUtils.hookWebView(); + JgyUtils.hookWebView(); startService(); } @@ -196,36 +191,4 @@ public class BaseApplication extends Application { }); } - private void tPushInit() { - XGPushConfig.enableDebug(this, true); - XGPushConfig.enablePullUpOtherApp(this, false); - XGPushManager.registerPush(this, new XGIOperateCallback() { - @Override - public void onSuccess(Object data, int flag) { - //token在设备卸载重装的时候有可能会变 - Log.e("TPush", "注册成功,设备token为:" + data); - List accountInfoList = new ArrayList<>(); - Log.e("TPush", "onSuccess: " + Utils.getSerial()); - accountInfoList.add(new XGPushManager.AccountInfo(XGPushManager.AccountType.CUSTOM.getValue(), Utils.getSerial())); -// accountInfoList.add(new XGPushManager.AccountInfo(XGPushManager.AccountType.CUSTOM.getValue(), Utils.getIMEI(getApplicationContext()))); - XGPushManager.upsertAccounts(getApplicationContext(), accountInfoList, new XGIOperateCallback() { - @Override - public void onSuccess(Object data, int flag) { - Log.e("TPush", "upsertAccounts onSuccess, data:" + data + ", flag:" + flag); - NetInterfaceManager.getInstance().setPushTags(); - } - - @Override - public void onFail(Object data, int errCode, String msg) { - Log.e("TPush", "upsertAccounts onFail, data:" + data + ", code:" + errCode + ", msg:" + msg); - } - }); - } - - @Override - public void onFail(Object data, int errCode, String msg) { - Log.e("TPush", "注册失败,错误码:" + errCode + ",错误信息:" + msg); - } - }); - } } diff --git a/app/src/main/java/com/fuying/sn/base/BaseFragment.java b/app/src/main/java/com/fuying/sn/base/BaseFragment.java new file mode 100644 index 0000000..a065368 --- /dev/null +++ b/app/src/main/java/com/fuying/sn/base/BaseFragment.java @@ -0,0 +1,44 @@ +package com.fuying.sn.base; + +import android.os.Bundle; + +import com.fuying.sn.base.rx.BaseRxFragment; + +public abstract class BaseFragment extends BaseRxFragment { + + protected boolean isViewInitiated; + protected boolean isVisibleToUser; + protected boolean isDataInitiated; + + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + isViewInitiated = true; + prepareFetchData(); + } + + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + this.isVisibleToUser = isVisibleToUser; + prepareFetchData(); + } + + public abstract void fetchData(); + + public boolean prepareFetchData() { + return prepareFetchData(false); + } + + public boolean prepareFetchData(boolean forceUpdate) { + if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) { + fetchData(); + //注释掉保证每次都更新数据 + isDataInitiated = true; + return true; + } + return false; + } + +} diff --git a/app/src/main/java/com/fuying/sn/base/mvvm/fragment/BaseMvvmDialogFragment.java b/app/src/main/java/com/fuying/sn/base/mvvm/fragment/BaseMvvmDialogFragment.java new file mode 100644 index 0000000..0c2efb0 --- /dev/null +++ b/app/src/main/java/com/fuying/sn/base/mvvm/fragment/BaseMvvmDialogFragment.java @@ -0,0 +1,184 @@ +package com.fuying.sn.base.mvvm.fragment; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Configuration; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.databinding.DataBindingUtil; +import androidx.databinding.ViewDataBinding; +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; + +import com.fuying.sn.base.BaseDialogFragment; + +import java.lang.ref.WeakReference; +import java.lang.reflect.ParameterizedType; + +/** + * @author: lml + * @date: 2021/12/15 + */ +public abstract class BaseMvvmDialogFragment extends BaseDialogFragment { + protected String mTag = this.getClass().getSimpleName(); + /** + * 是否顯示了 + */ + protected boolean mIsVisible; + /** + * 是否準備好了-Created + */ + protected boolean mHasPrepare; + + + protected VM mViewModel; + protected VDB mViewDataBinding; + protected Class vmClass; + // +// protected Toolbar toolbar; +// protected View statusBarView; + // + protected Bundle bundle;//来自getArguments() + protected Bundle savedInstanceState; + +// protected Context context; + + /** + * 上下文 + */ + private WeakReference ctx; + + public Context getCtx() { + return ctx == null ? null : ctx.get(); + } + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); +// this.context = context; + ctx = new WeakReference<>(context); + } + + /** + * onCreate、onResume里不能调用 + * + * @return + */ + public boolean isAttached() { + boolean flag = getCtx() != null && isAdded(); + Log.e(" >> isAttached >>", "flag = " + flag); + return flag; + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + //ViewDataBinding + mViewDataBinding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false); + mViewDataBinding.setLifecycleOwner(this); + + //ViewModel + vmClass = (Class) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; + mViewModel = new ViewModelProvider(this).get(vmClass); + + return mViewDataBinding.getRoot(); + } + + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + initDataBinding(); + initView(bundle = getArguments()); + + initData(this.savedInstanceState = savedInstanceState); + + if (mIsVisible) { + onEnter(); + } + mHasPrepare = true; + + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + mHasPrepare = false; + mViewDataBinding = null; + } + + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + if (mIsVisible == getUserVisibleHint()) + return; + mIsVisible = getUserVisibleHint(); + if (mIsVisible) { + if (!mHasPrepare) + return; + onEnter(); + } else { + onExit(); + } + } + + @LayoutRes + protected abstract int getLayoutId(); + + protected abstract void initDataBinding(); + + protected abstract void initView(Bundle bundle); + + protected abstract void initData(Bundle savedInstanceState); + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + } + + protected void hideInputMethod(Activity activity) { + InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE); + View view = activity.getCurrentFocus(); + if (view != null) { + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + } + + protected void hideInputMethod(Activity activity, EditText editText) { + InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Activity.INPUT_METHOD_SERVICE); + View view = activity.getCurrentFocus(); + if (view != null) { + imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } + } + + protected void showInputMethod(EditText editText) { + InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(editText, InputMethodManager.SHOW_FORCED); + } + + /** + * 進入界面 + */ + protected void onEnter() { + + } + + /** + * 離開界面 + */ + protected void onExit() { + + } + +} diff --git a/app/src/main/java/com/fuying/sn/base/mvvm/fragment/BaseMvvmFragment.java b/app/src/main/java/com/fuying/sn/base/mvvm/fragment/BaseMvvmFragment.java new file mode 100644 index 0000000..824afe7 --- /dev/null +++ b/app/src/main/java/com/fuying/sn/base/mvvm/fragment/BaseMvvmFragment.java @@ -0,0 +1,279 @@ +package com.fuying.sn.base.mvvm.fragment; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Configuration; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.databinding.DataBindingUtil; +import androidx.databinding.ViewDataBinding; +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; + +import com.fuying.sn.base.BaseFragment; + +import java.lang.ref.WeakReference; +import java.lang.reflect.ParameterizedType; + +/** + * @author: lml + * @date: 2021/12/15 + */ +public abstract class BaseMvvmFragment extends BaseFragment { + protected String mTag = this.getClass().getSimpleName(); + /** + * 是否顯示了 + */ + protected boolean mIsVisible; + /** + * 是否準備好了-Created + */ + protected boolean mHasPrepare; + + + protected VM mViewModel; + protected VDB mViewDataBinding; + protected Class vmClass; + // +// protected Toolbar toolbar; +// protected View statusBarView; + // + protected Bundle bundle;//来自getArguments() + protected Bundle savedInstanceState; + +// protected Context context; + + /** + * 上下文 + */ + private WeakReference ctx; + + public Context getCtx() { + return ctx == null ? null : ctx.get(); + } + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); +// this.context = context; + ctx = new WeakReference<>(context); + } + + /** + * onCreate、onResume里不能调用 + * + * @return + */ + public boolean isAttached() { + boolean flag = getCtx() != null && isAdded(); + Log.e(" >> isAttached >>", "flag = " + flag); + return flag; + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + //ViewDataBinding + mViewDataBinding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false); + mViewDataBinding.setLifecycleOwner(this); + + //ViewModel + vmClass = (Class) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; + mViewModel = new ViewModelProvider(this).get(vmClass); + // + return mViewDataBinding.getRoot(); + } + + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + +// if (initStatusBarToolBar()) { +// toolbar = getToolbar(); +// } + //注册eventbus +// if (getClass().isAnnotationPresent(BindEventBus.class)) +// EventBusManager.register(this); + // + +// fitsLayoutOverlap(); + initDataBinding(); + long time = System.currentTimeMillis(); + initView(bundle = getArguments()); + Log.e(this.getClass().getSimpleName(), "onViewCreated: initView " + (System.currentTimeMillis() - time) + "ms"); + // + long time2 = System.currentTimeMillis(); + initData(this.savedInstanceState = savedInstanceState); + Log.e(this.getClass().getSimpleName(), "onViewCreated: initData " + (System.currentTimeMillis() - time2) + "ms"); + // + if (mIsVisible) { + onEnter(); + } + mHasPrepare = true; + // +// LiveDataBus.get().with(ConstantUtils.DATA_BUS_LOADING_FRAGMENT, Boolean.class).observe(getActivity(), bool -> { +// L.e(" >> LiveDataBus >> DATA_BUS_LOADING_FRAGMENT: %s", bool); +// if(bool) { +// showLoading(R.string.str_please_wait); +// } else { +// hideLoading(); +// } +// }); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + mHasPrepare = false; + mViewDataBinding = null; + //移除eventbus +// if (getClass().isAnnotationPresent(BindEventBus.class)) +// EventBusManager.unregister(this); + // + } + + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + if (mIsVisible == getUserVisibleHint()) + return; + mIsVisible = getUserVisibleHint(); + if (mIsVisible) { + if (!mHasPrepare) + return; + onEnter(); + } else { + onExit(); + } + } + + @LayoutRes + protected abstract int getLayoutId(); + +// protected abstract Toolbar getToolbar(); + +// protected View getStatusView() { +// return null; +// } + + protected abstract void initDataBinding(); + + protected abstract void initView(Bundle bundle); + + protected abstract void initData(Bundle savedInstanceState); + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); +// fitsLayoutOverlap(); + } + +// protected boolean isImmersionBarEnabled() { +// return true; +// } + +// protected boolean initStatusBarToolBar() { +// return true; +// } + + +// private void fitsLayoutOverlap() { +// if (!isImmersionBarEnabled()) return; +// if (statusBarView != null) { +// ImmersionBar.setStatusBarView(getActivity(), statusBarView); +// } +// if (toolbar != null) { +// ImmersionBar.setTitleBar(getActivity(), toolbar); +// } +// } + + protected void hideInputMethod(Activity activity) { + InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE); + View view = activity.getCurrentFocus(); + if (view != null) { + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + } + + protected void hideInputMethod(Activity activity, EditText editText) { + InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Activity.INPUT_METHOD_SERVICE); + View view = activity.getCurrentFocus(); + if (view != null) { + imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } + } + + protected void showInputMethod(EditText editText) { + InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(editText, InputMethodManager.SHOW_FORCED); + } + + +// private CustomDialog mWaitDialog; +// +// public void showLoading(@StringRes int contentID) { +// showLoading(contentID, R.color.white); +// } +// +// public void showLoading(@StringRes int contentID, @ColorRes int color) { +// hideLoading(); +// DialogX.init(getActivity()); +// if (color == R.color.white) { +// mWaitDialog = DialogXUtil.getInstance().showLoading(getActivity(), getString(contentID), getResources().getColor(color)); +// } else { +// mWaitDialog = DialogXUtil.getInstance().showLoading_black(getActivity(), getString(contentID), getResources().getColor(color)); +// } +// } +// +// public void updateLoadingTip(@StringRes int messageID, int percent) { +// try { +// if (mWaitDialog != null && mWaitDialog.isShow()) { +// TextView tvTip = mWaitDialog.getCustomView().findViewById(R.id.tv_load_tip); +// if (tvTip != null) +// tvTip.setText(getResources().getString(messageID) + (percent == -1 ? "" : percent + "%")); +// } +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +// +// public boolean isShowLoading() { +// return mWaitDialog != null && mWaitDialog.isShow(); +// } +// +// public void hideLoading() { +// try { +// boolean isShow = isShowLoading(); +// L.d(" >> hideLoading :: isShow: %s", isShow); +// if (isShow) +// mWaitDialog.dismiss(); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } + + /** + * 進入界面 + */ + protected void onEnter() { + + } + + /** + * 離開界面 + */ + protected void onExit() { + + } + +} diff --git a/app/src/main/java/com/fuying/sn/bean/MapBean.java b/app/src/main/java/com/fuying/sn/bean/MapBean.java new file mode 100644 index 0000000..bfdc196 --- /dev/null +++ b/app/src/main/java/com/fuying/sn/bean/MapBean.java @@ -0,0 +1,134 @@ +package com.fuying.sn.bean; + +import java.io.Serializable; + +public class MapBean implements Serializable { + private static final long serialVersionUID = -4356064111098876676L; + + double longitude; + double latitude; + String adcode; + String address; + String city; + String cityCode; + String country; + String countryCode; + String district; + String province; + String street; + String streetNumber; + String town; + String locationDescribe; + + public double getLongitude() { + return longitude; + } + + public void setLongitude(double longitude) { + this.longitude = longitude; + } + + public double getLatitude() { + return latitude; + } + + public void setLatitude(double latitude) { + this.latitude = latitude; + } + + public String getAdcode() { + return adcode; + } + + public void setAdcode(String adcode) { + this.adcode = adcode; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getCityCode() { + return cityCode; + } + + public void setCityCode(String cityCode) { + this.cityCode = cityCode; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getCountryCode() { + return countryCode; + } + + public void setCountryCode(String countryCode) { + this.countryCode = countryCode; + } + + public String getDistrict() { + return district; + } + + public void setDistrict(String district) { + this.district = district; + } + + public String getProvince() { + return province; + } + + public void setProvince(String province) { + this.province = province; + } + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } + + public String getStreetNumber() { + return streetNumber; + } + + public void setStreetNumber(String streetNumber) { + this.streetNumber = streetNumber; + } + + public String getTown() { + return town; + } + + public void setTown(String town) { + this.town = town; + } + + public String getLocationDescribe() { + return locationDescribe; + } + + public void setLocationDescribe(String locationDescribe) { + this.locationDescribe = locationDescribe; + } +} diff --git a/app/src/main/java/com/fuying/sn/config/CommonConfig.java b/app/src/main/java/com/fuying/sn/config/CommonConfig.java index 9af76a0..f1d75cd 100644 --- a/app/src/main/java/com/fuying/sn/config/CommonConfig.java +++ b/app/src/main/java/com/fuying/sn/config/CommonConfig.java @@ -3,6 +3,20 @@ package com.fuying.sn.config; public class CommonConfig { public static final String MMKV_ID = "InterProcessKV"; + public static final String MAP_LOCATION_JSON_KEY = "MAPLOCATION_JSON_STRING"; + public static final String MAP_LONGITUDE_KEY = "map_longitude_key"; + public static final String MAP_LATITUDE_KEY = "map_latitude_key"; + public static final String MAP_ADDRESS_KEY = "map_address_key"; + public static final String MAP_PROVINCE_KEY = "map_province_key"; + public static final String MAP_CITY_KEY = "map_city_key"; + public static final String MAP_DISTRICT_KEY = "map_district_key"; + public static final String MAP_STREET_KEY = "map_street_key"; + public static final String MAP_LOCATION_DESCRIBE_KEY = "map_locationDescribe_key"; + public static final String MAP_ERROR_KEY = "map_error_key"; + public static final String MAP_onLocationChanged_time_KEY = "map_onLocationChanged_time_key"; + public static final String MAP_BEAN = "MapBean"; + + /*首次开机*/ public static final String JGY_FIRST_BOOT = "first_boot"; /*保存的应用版本号*/ diff --git a/app/src/main/java/com/fuying/sn/desktop/RunningAppManager.java b/app/src/main/java/com/fuying/sn/desktop/RunningAppManager.java index 5a40ad2..4186794 100644 --- a/app/src/main/java/com/fuying/sn/desktop/RunningAppManager.java +++ b/app/src/main/java/com/fuying/sn/desktop/RunningAppManager.java @@ -38,7 +38,7 @@ import com.fuying.sn.gson.GsonUtils; import com.fuying.sn.network.NetInterfaceManager; import com.fuying.sn.network.UrlAddress; import com.fuying.sn.utils.AppUsedTimeUtils; -import com.fuying.sn.utils.JGYUtils; +import com.fuying.sn.utils.JgyUtils; import com.fuying.sn.utils.ToastUtil; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -188,13 +188,13 @@ public class RunningAppManager { private void checkForegroundAppName() { String topPackage = getTopActivityInfo(); if ("org.chromium.browser".equals(topPackage)) { - JGYUtils.getInstance().killPackage("org.chromium.browser"); + JgyUtils.getInstance().killPackage("org.chromium.browser"); } String appPackageName = getAppPackageName(); Log.d(TAG, "checkForegroundAppName: topPackage = " + topPackage); Log.d(TAG, "checkForegroundAppName: appPackageName = " + appPackageName); - if (!JGYUtils.getInstance().isScreenOn()) { + if (!JgyUtils.getInstance().isScreenOn()) { Log.d(TAG, "checkForegroundAppName: isScreenOn = false"); if (!TextUtils.isEmpty(appPackageName)) { Log.d(TAG, "checkForegroundAppName: isEmpty = false"); @@ -255,7 +255,7 @@ public class RunningAppManager { // removeTask(topPackage); NetInterfaceManager.getInstance().getAppTimeControl(); NetInterfaceManager.getInstance().getSnTimeControl(); - JGYUtils.getInstance().killPackage(appPackageName); + JgyUtils.getInstance().killPackage(appPackageName); killApp(); gotoLauncher(); } else { @@ -282,7 +282,7 @@ public class RunningAppManager { Log.i(TAG, "checkForegroundAppName: 没有剩余时间2"); // removeTask(topPackage); killApp(); - JGYUtils.getInstance().killPackage(appPackageName); + JgyUtils.getInstance().killPackage(appPackageName); gotoLauncher(); NetInterfaceManager.getInstance().getAppTimeControl(); NetInterfaceManager.getInstance().getSnTimeControl(); @@ -302,13 +302,13 @@ public class RunningAppManager { List appList = launcherApp.stream().map(resolveInfo -> resolveInfo.activityInfo.packageName).collect(Collectors.toList()); Log.e(TAG, "killApp: " + appList); for (String pkg : appList) { - if (JGYUtils.fuxiaoying.equals(pkg) + if (JgyUtils.fuxiaoying.equals(pkg) || BuildConfig.APPLICATION_ID.equals(pkg) || "com.android.settings".equals(pkg) ) { continue; } - JGYUtils.getInstance().killPackage(pkg); + JgyUtils.getInstance().killPackage(pkg); } } @@ -759,7 +759,7 @@ public class RunningAppManager { this.add("com.uiui.store"); this.add("com.uiui.info"); this.add("com.tt.ttutils"); - this.add(JGYUtils.fuxiaoying); + this.add(JgyUtils.fuxiaoying); this.add("com.sprd.engineermode"); this.add("com.teclast.update"); this.add("com.incar.update"); diff --git a/app/src/main/java/com/fuying/sn/fragment/device/DeviceFragment.java b/app/src/main/java/com/fuying/sn/fragment/device/DeviceFragment.java new file mode 100644 index 0000000..3969339 --- /dev/null +++ b/app/src/main/java/com/fuying/sn/fragment/device/DeviceFragment.java @@ -0,0 +1,156 @@ +package com.fuying.sn.fragment.device; + +import android.app.Activity; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.text.TextUtils; +import android.text.format.Formatter; +import android.util.Log; +import android.view.View; + +import androidx.lifecycle.Observer; + +import com.blankj.utilcode.constant.MemoryConstants; +import com.blankj.utilcode.util.ConvertUtils; +import com.fuying.sn.BuildConfig; +import com.fuying.sn.R; +import com.fuying.sn.activity.update.UpdateActivity; +import com.fuying.sn.base.mvvm.fragment.BaseMvvmFragment; +import com.fuying.sn.bean.AppInfo; +import com.fuying.sn.bean.SnInfo; +import com.fuying.sn.config.CommonConfig; +import com.fuying.sn.databinding.FragmentDeviceBinding; +import com.fuying.sn.utils.ApkUtils; +import com.fuying.sn.utils.JgyUtils; +import com.fuying.sn.utils.TimeUtils; +import com.fuying.sn.utils.Utils; +import com.hjq.toast.Toaster; +import com.jakewharton.rxbinding4.view.RxView; +import com.tencent.mmkv.MMKV; + +import io.reactivex.rxjava3.disposables.Disposable; + + +public class DeviceFragment extends BaseMvvmFragment { + private static final String TAG = "DeviceFragment"; + private Activity mContext; + + private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); + + private Disposable mClickDisposable; + // 2秒防抖时间 + private long windowDurationMs = 60000; + // 记录上次成功触发的时间 + private final long[] lastClickTime = {0}; + + @Override + protected int getLayoutId() { + return R.layout.fragment_device; + } + + @Override + protected void initDataBinding() { + mContext = getActivity(); + mViewModel.setCtx(getCtx()); + mViewModel.setLifecycle(getLifecycleSubject()); + mViewModel.setVDBinding(mViewDataBinding); + mViewDataBinding.setClick(new BtnClick()); + } + + @Override + protected void initView(Bundle bundle) { + Log.e(TAG, "initView: byte2MemorySize = " + ConvertUtils.byte2MemorySize(Utils.getTotalMem(mContext), MemoryConstants.GB)); + Log.e(TAG, "initView: getTotalMem = " + ConvertUtils.byte2FitMemorySize(Utils.getTotalMem(mContext))); + Log.e(TAG, "initView: getTotalMemory = " + Formatter.formatFileSize(mContext, Utils.getTotalMemory(mContext))); + + setDeviceInfo(); + + + mClickDisposable = RxView.clicks(mViewDataBinding.clUpdate) + .subscribe(o -> { + long currentTime = System.currentTimeMillis(); + if (currentTime - lastClickTime[0] >= windowDurationMs) { + // 【情况 A】满足时间间隔:执行核心逻辑 + lastClickTime[0] = currentTime; + mViewModel.checkUpdate(true); + } else { + // 【情况 B】在防抖时间内:仅提示 + Toaster.show("请稍后再试"); + } + }); + } + + @Override + protected void initData(Bundle savedInstanceState) { + mViewModel.mSnInfoMutableLiveData.observe(this, new Observer() { + @Override + public void onChanged(SnInfo snInfo) { + if (snInfo != null) { + if (!TextUtils.isEmpty(snInfo.getMobile())) { + mViewDataBinding.tvPhoneNumber.setText(snInfo.getMobile()); + } + mViewDataBinding.tvBindTime.setText(TimeUtils.getDate(0)); + } else { + mViewDataBinding.tvPhoneNumber.setText("未绑定"); + mViewDataBinding.tvBindTime.setText("未绑定"); + + } + } + }); + mViewModel.getSnInfo(); + + mViewModel.mAppInfoMutableLiveData.observe(this, new Observer() { + @Override + public void onChanged(AppInfo appInfo) { + if (appInfo != null) { + mViewDataBinding.tvUpdateVersion.setText(appInfo.getApp_version_name()); + if (ApkUtils.isUpdate(mContext, appInfo)) { + Intent intent = new Intent(mContext, UpdateActivity.class); + intent.putExtra("appInfo", appInfo); + startActivity(intent); + Toaster.show("有新的版本需要更新"); + } else { +// Toaster.show("已是最新版本"); + } + } else { + Toaster.show("已是最新版本"); + } + } + }); + mViewModel.checkUpdate(false); + } + + @Override + public void fetchData() { + Log.e(TAG, "fetchData: "); + setDeviceInfo(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + if (mClickDisposable != null && !mClickDisposable.isDisposed()) { + mClickDisposable.dispose(); + mClickDisposable = null; + } + } + + private void setDeviceInfo() { + mViewDataBinding.tvSn.setText(Utils.getSn()); + mViewDataBinding.tvImei.setText(Utils.getIMEI(mContext)); + mViewDataBinding.tvMac.setText(Utils.getMAC(mContext)); + mViewDataBinding.tvRam.setText(Math.ceil(ConvertUtils.byte2MemorySize(Utils.getTotalMem(mContext), MemoryConstants.GB)) + " GB"); + mViewDataBinding.tvAndroidVersion.setText("Android " + Build.VERSION.RELEASE); + mViewDataBinding.tvBuildVersion.setText("V" + JgyUtils.getRomVersion()); + + mViewDataBinding.tvLocalVersion.setText("V" + BuildConfig.VERSION_NAME); + + } + + public class BtnClick { + public void checkUpdate(View view) { + mViewModel.checkUpdate(true); + } + } +} diff --git a/app/src/main/java/com/fuying/sn/fragment/device/DeviceViewModel.java b/app/src/main/java/com/fuying/sn/fragment/device/DeviceViewModel.java new file mode 100644 index 0000000..f55fc89 --- /dev/null +++ b/app/src/main/java/com/fuying/sn/fragment/device/DeviceViewModel.java @@ -0,0 +1,102 @@ +package com.fuying.sn.fragment.device; + +import android.util.Log; + +import androidx.lifecycle.MutableLiveData; + +import com.fuying.sn.BuildConfig; +import com.fuying.sn.base.mvvm.BaseViewModel; +import com.fuying.sn.bean.AppInfo; +import com.fuying.sn.bean.BaseResponse; +import com.fuying.sn.bean.SnInfo; +import com.fuying.sn.databinding.FragmentDeviceBinding; +import com.fuying.sn.network.NetInterfaceManager; +import com.trello.rxlifecycle4.RxLifecycle; +import com.trello.rxlifecycle4.android.FragmentEvent; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; + +public class DeviceViewModel extends BaseViewModel { + + @Override + public FragmentDeviceBinding getVDBinding() { + return binding; + } + + @Override + public void onDestroy() { + + } + + public MutableLiveData mSnInfoMutableLiveData = new MutableLiveData<>(); + + public void getSnInfo() { + NetInterfaceManager.getInstance().getSnInfoControl() + .compose(RxLifecycle.bindUntilEvent(getLifecycle(), FragmentEvent.DESTROY)) + .subscribe(new Observer>() { + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.e("getSnInfo", "onSubscribe: "); + } + + @Override + public void onNext(@NonNull BaseResponse snInfoBaseResponse) { + Log.e("getSnInfo", "onNext: " + snInfoBaseResponse); + if (snInfoBaseResponse.code == 200) { + SnInfo snInfo = snInfoBaseResponse.data; + mSnInfoMutableLiveData.setValue(snInfo); + } else { + mSnInfoMutableLiveData.setValue(null); + } + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("getSnInfo", "onError: " + e.getMessage()); + } + + @Override + public void onComplete() { + Log.e("getSnInfo", "onComplete: "); + } + }); + } + + public MutableLiveData mAppInfoMutableLiveData = new MutableLiveData<>(); + + public void checkUpdate(boolean response) { + NetInterfaceManager.getInstance().getUpdateObservable(BuildConfig.APPLICATION_ID) + .compose(RxLifecycle.bindUntilEvent(getLifecycle(), FragmentEvent.DESTROY)) + .subscribe(new Observer>() { + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.e("checkUpdate", "onSubscribe: "); + } + + @Override + public void onNext(@NonNull BaseResponse appInfoBaseResponse) { + Log.e("checkUpdate", "onNext: " + appInfoBaseResponse); + if (appInfoBaseResponse.code == 200) { + AppInfo appInfo = appInfoBaseResponse.data; + mAppInfoMutableLiveData.setValue(appInfo); + } else { + if (response) { + mAppInfoMutableLiveData.setValue(null); + } + } + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("checkUpdate", "onError: " + e.getMessage()); + } + + @Override + public void onComplete() { + Log.e("checkUpdate", "onComplete: "); + } + }); + } +} diff --git a/app/src/main/java/com/fuying/sn/fragment/usage/UsageFragment.java b/app/src/main/java/com/fuying/sn/fragment/usage/UsageFragment.java new file mode 100644 index 0000000..7a5fe2c --- /dev/null +++ b/app/src/main/java/com/fuying/sn/fragment/usage/UsageFragment.java @@ -0,0 +1,51 @@ +package com.fuying.sn.fragment.usage; + +import android.app.Activity; +import android.os.Bundle; + +import com.fuying.sn.R; +import com.fuying.sn.base.mvvm.fragment.BaseMvvmFragment; +import com.fuying.sn.config.CommonConfig; +import com.fuying.sn.databinding.FragmentUsageBinding; +import com.tencent.mmkv.MMKV; + +public class UsageFragment extends BaseMvvmFragment { + private static final String TAG = "UsageFragment"; + private Activity mContext; + + private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); + + + @Override + protected int getLayoutId() { + return R.layout.fragment_usage; + } + + @Override + protected void initDataBinding() { + mContext = getActivity(); + mViewModel.setCtx(getCtx()); + mViewModel.setLifecycle(getLifecycleSubject()); + mViewModel.setVDBinding(mViewDataBinding); + mViewDataBinding.setClick(new BtnClick()); + } + + @Override + protected void initView(Bundle bundle) { + + } + + @Override + protected void initData(Bundle savedInstanceState) { + + } + + @Override + public void fetchData() { + + } + + public class BtnClick { + + } +} diff --git a/app/src/main/java/com/fuying/sn/fragment/usage/UsageViewModel.java b/app/src/main/java/com/fuying/sn/fragment/usage/UsageViewModel.java new file mode 100644 index 0000000..5c8a28a --- /dev/null +++ b/app/src/main/java/com/fuying/sn/fragment/usage/UsageViewModel.java @@ -0,0 +1,18 @@ +package com.fuying.sn.fragment.usage; + +import com.fuying.sn.base.mvvm.BaseViewModel; +import com.fuying.sn.databinding.FragmentUsageBinding; +import com.trello.rxlifecycle4.android.FragmentEvent; + +public class UsageViewModel extends BaseViewModel { + + @Override + public FragmentUsageBinding getVDBinding() { + return binding; + } + + @Override + public void onDestroy() { + + } +} diff --git a/app/src/main/java/com/fuying/sn/manager/AmapManager.java b/app/src/main/java/com/fuying/sn/manager/AmapManager.java index 9fb5e30..b4b6ea5 100644 --- a/app/src/main/java/com/fuying/sn/manager/AmapManager.java +++ b/app/src/main/java/com/fuying/sn/manager/AmapManager.java @@ -2,29 +2,39 @@ package com.fuying.sn.manager; import android.annotation.SuppressLint; import android.content.Context; -import android.provider.Settings; +import android.text.TextUtils; import android.util.Log; -import com.baidu.location.BDAbstractLocationListener; -import com.baidu.location.BDLocation; -import com.baidu.location.LocationClient; -import com.baidu.location.LocationClientOption; -import com.fuying.sn.utils.SPUtils; +import com.amap.api.location.AMapLocation; +import com.amap.api.location.AMapLocationClient; +import com.amap.api.location.AMapLocationClientOption; +import com.amap.api.location.AMapLocationListener; +import com.fuying.sn.bean.MapBean; +import com.fuying.sn.config.CommonConfig; +import com.fuying.sn.gson.GsonUtils; +import com.fuying.sn.network.NetInterfaceManager; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.jeremyliao.liveeventbus.LiveEventBus; +import com.tencent.mmkv.MMKV; + +import java.lang.reflect.Type; public class AmapManager { private static final String TAG = "AmapManager"; + + MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); + @SuppressLint("StaticFieldLeak") private static AmapManager sInstance; private Context mContext; - @SuppressLint("StaticFieldLeak") - private LocationClient mLocationClient = null; - private LocationClientOption mOption; + + private AMapLocationClient mAMapLocationClient; + private AMapLocationClientOption mAMapLocationClientOption; + private MapBean mMapBean; private AmapManager(Context context) { - this.mContext = context; -// Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+gps"); - Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+network"); - Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.ASSISTED_GPS_ENABLED, 1); + this.mContext = context.getApplicationContext(); initAmap(); } @@ -41,161 +51,124 @@ public class AmapManager { if (sInstance == null) { throw new IllegalStateException("You must be init AmapManager first"); } + return sInstance; } + private AMapLocationClientOption getDefaultOption() { + if (mAMapLocationClientOption == null) { + mAMapLocationClientOption = new AMapLocationClientOption(); + } + mAMapLocationClientOption.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.SignIn); + //设置定位模式为AMapLocationMode.Hight_Accuracy,高精度模式。 + mAMapLocationClientOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy); + mAMapLocationClientOption.setNeedAddress(true); + //获取一次定位结果: + //该方法默认为false。 + mAMapLocationClientOption.setOnceLocation(true); + //获取最近3s内精度最高的一次定位结果: + //设置setOnceLocationLatest(boolean b)接口为true,启动定位时SDK会返回最近3s内精度最高的一次定位结果。 + // 如果设置其为true,setOnceLocation(boolean b)接口也会被设置为true,反之不会,默认为false。 + mAMapLocationClientOption.setOnceLocationLatest(true); + return mAMapLocationClientOption; + } + public void initAmap() { - if (mLocationClient == null) { - mLocationClient = new LocationClient(mContext); + if (mAMapLocationClient == null) { + mAMapLocationClient = new AMapLocationClient(mContext); } -// Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+gps"); - Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+network"); - Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.ASSISTED_GPS_ENABLED, 1); - mLocationClient.setLocOption(getDefaultLocationClientOption()); - mLocationClient.registerLocationListener(mListener); - mLocationClient.stop(); - mLocationClient.start(); + mAMapLocationClient.setLocationOption(getDefaultOption()); + + //设置定位监听 + mAMapLocationClient.setLocationListener(mAMapLocationListener); + //设置场景模式后最好调用一次stop,再调用start以保证场景模式生效 + + startLocation(); + + String jsonString = mMMKV.decodeString(CommonConfig.MAP_LOCATION_JSON_KEY, ""); + if (!TextUtils.isEmpty(jsonString)) { + Gson gson = new Gson(); + Type type = new TypeToken() { + }.getType(); + mMapBean = gson.fromJson(jsonString, type); + } else { + Log.e(TAG, "initAmap: jsonString is empty"); + } + } - public LocationClient getLocationClient() { - if (mLocationClient == null) { - initAmap(); - } - return mLocationClient; + public void startLocation() { + mAMapLocationClient.stopLocation(); + mAMapLocationClient.startLocation(); + Log.e(TAG, "initAmap: " + "startLocation"); } - /*** - * - * @return DefaultLocationClientOption 默认O设置 - */ - public LocationClientOption getDefaultLocationClientOption() { - if (mOption == null) { - mOption = new LocationClientOption(); - mOption.setCoorType("bd09ll"); // 可选,默认gcj02,设置返回的定位结果坐标系,如果配合百度地图使用,建议设置为bd09ll; - mOption.setScanSpan(0); // 可选,默认0,即仅定位一次,设置发起连续定位请求的间隔需要大于等于1000ms才是有效的 - mOption.setIsNeedAddress(true); // 可选,设置是否需要地址信息,默认不需要 - mOption.setIsNeedLocationDescribe(true); // 可选,设置是否需要地址描述 - mOption.setNeedDeviceDirect(false); // 可选,设置是否需要设备方向结果 - mOption.setLocationNotify(false); // 可选,默认false,设置是否当gps有效时按照1S1次频率输出GPS结果 - mOption.setIgnoreKillProcess(true); // 可选,默认true,定位SDK内部是一个SERVICE,并放到了独立进程,设置是否在stop - mOption.setIsNeedLocationDescribe(true); // 可选,默认false,设置是否需要位置语义化结果,可以在BDLocation - mOption.setIsNeedLocationPoiList(true); // 可选,默认false,设置是否需要POI结果,可以在BDLocation - mOption.SetIgnoreCacheException(false); // 可选,默认false,设置是否收集CRASH信息,默认收集 - mOption.setLocationMode(LocationClientOption.LocationMode.Battery_Saving); // 可选,默认高精度,设置定位模式,高精度,低功耗,仅设备,模糊 - mOption.setIsNeedAltitude(false); // 可选,默认false,设置定位时是否需要海拔信息,默认不需要,除基础定位版本都可用 - // 可选,设置首次定位时选择定位速度优先还是定位准确性优先,默认为速度优先 -// mOption.setFirstLocType(LocationClientOption.FirstLocType.SPEED_IN_FIRST_LOC); - } - return mOption; - } - - - /***** - * - * 定位结果回调,重写onReceiveLocation方法,可以直接拷贝如下代码到自己工程中修改 - * - */ - private BDAbstractLocationListener mListener = new BDAbstractLocationListener() { - - /** - * 定位请求回调函数 - * @param location 定位结果 - */ + private AMapLocationListener mAMapLocationListener = new AMapLocationListener() { @Override - public void onReceiveLocation(BDLocation location) { - if (null != location) { - switch (location.getLocType()) { - case BDLocation.TypeGpsLocation:// GPS定位结果 - case BDLocation.TypeNetWorkLocation:// 网络定位结果 - case BDLocation.TypeOffLineLocation:// 离线定位结果 - Log.e(TAG, "onLocationChanged: " + "定位成功"); - Log.e(TAG, "onLocationChanged: " + location.getAddrStr() + location.getLocationDescribe()); - SPUtils.put(mContext, "AmapAddress", location.getAddrStr() + location.getLocationDescribe()); - SPUtils.put(mContext, "longitude", location.getLongitude()); - SPUtils.put(mContext, "latitude", location.getLatitude()); - SPUtils.put(mContext, "AmapError", "-"); - break; - case BDLocation.TypeServerError: - Log.e(TAG, "onReceiveLocation: " + "服务端网络定位失败"); - SPUtils.put(mContext, "AmapError", "服务端网络定位失败,可以反馈IMEI号和大体定位时间到loc-bugs@baidu.com,会有人追查原因"); - break; - case BDLocation.TypeNetWorkException: - Log.e(TAG, "onReceiveLocation: " + "网络不同导致定位失败,请检查网络是否通畅"); - SPUtils.put(mContext, "AmapError", "网络不同导致定位失败,请检查网络是否通畅"); - break; - case BDLocation.TypeCriteriaException: - Log.e(TAG, "onReceiveLocation: " + "无法获取有效定位依据导致定位失败"); - SPUtils.put(mContext, "AmapError", "无法获取有效定位依据导致定位失败,一般是由于手机的原因,处于飞行模式下一般会造成这种结果,可以试着重启手机"); - break; - default: - } + public void onLocationChanged(AMapLocation aMapLocation) { + StringBuilder sb = new StringBuilder(); + //errCode等于0代表定位成功,其他的为定位失败,具体的可以参照官网定位错误码说明 + if (aMapLocation.getErrorCode() == 0) { + Log.e(TAG, "onLocationChanged: " + "定位成功"); + updateAddress(aMapLocation); + + mMapBean = getMapBean(aMapLocation); + saveMapResult(mMapBean); + LiveEventBus.get(CommonConfig.MAP_BEAN) + .post(mMapBean); + LiveEventBus.get(CommonConfig.MAP_ADDRESS_KEY) + .post(mMapBean.getAddress()); + + Log.e(TAG, "onLocationChanged: " + aMapLocation.getAddress()); + sb.append(aMapLocation.getAddress()).append("\n"); + mMMKV.encode(CommonConfig.MAP_onLocationChanged_time_KEY, System.currentTimeMillis()); + } else { + //定位失败 + sb.append("定位失败" + "\n"); + sb.append(aMapLocation.getErrorInfo()); + mMMKV.encode(CommonConfig.MAP_ERROR_KEY, aMapLocation.getErrorInfo()); + Log.e(TAG, "onLocationChanged: " + "定位失败"); } - Log.e(TAG, "AmapAddress: " + SPUtils.get(mContext, "AmapAddress", "-")); - Log.e(TAG, "AmapError: " + SPUtils.get(mContext, "AmapError", "-")); - Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-gps"); - Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-network"); - Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, ""); - Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.ASSISTED_GPS_ENABLED, 0); - mLocationClient.stop(); - } - - @Override - public void onConnectHotSpotMessage(String s, int i) { - super.onConnectHotSpotMessage(s, i); - } - - /** - * 回调定位诊断信息,开发者可以根据相关信息解决定位遇到的一些问题 - * @param locType 当前定位类型 - * @param diagnosticType 诊断类型(1~9) - * @param diagnosticMessage 具体的诊断信息释义 - */ - @Override - public void onLocDiagnosticMessage(int locType, int diagnosticType, String diagnosticMessage) { - super.onLocDiagnosticMessage(locType, diagnosticType, diagnosticMessage); -// int tag = 2; - StringBuffer sb = new StringBuffer(256); - sb.append("诊断结果: "); - if (locType == BDLocation.TypeNetWorkLocation) { - if (diagnosticType == 1) { - sb.append("网络定位成功,没有开启GPS,建议打开GPS会更好"); - sb.append("\n" + diagnosticMessage); - } else if (diagnosticType == 2) { - sb.append("网络定位成功,没有开启Wi-Fi,建议打开Wi-Fi会更好"); - sb.append("\n" + diagnosticMessage); - } - } else if (locType == BDLocation.TypeOffLineLocationFail) { - if (diagnosticType == 3) { - sb.append("定位失败,请您检查您的网络状态"); - sb.append("\n" + diagnosticMessage); - } - } else if (locType == BDLocation.TypeCriteriaException) { - if (diagnosticType == 4) { - sb.append("定位失败,无法获取任何有效定位依据"); - sb.append("\n" + diagnosticMessage); - } else if (diagnosticType == 5) { - sb.append("定位失败,无法获取有效定位依据,请检查运营商网络或者Wi-Fi网络是否正常开启,尝试重新请求定位"); - sb.append(diagnosticMessage); - } else if (diagnosticType == 6) { - sb.append("定位失败,无法获取有效定位依据,请尝试插入一张sim卡或打开Wi-Fi重试"); - sb.append("\n" + diagnosticMessage); - } else if (diagnosticType == 7) { - sb.append("定位失败,飞行模式下无法获取有效定位依据,请关闭飞行模式重试"); - sb.append("\n" + diagnosticMessage); - } else if (diagnosticType == 9) { - sb.append("定位失败,无法获取任何有效定位依据"); - sb.append("\n" + diagnosticMessage); - } - } else if (locType == BDLocation.TypeServerError) { - if (diagnosticType == 8) { - sb.append("定位失败,请确认您定位的开关打开状态,是否赋予APP定位权限"); - sb.append("\n" + diagnosticMessage); - } - } - Log.e(TAG, "onLocationChanged: " + "定位失败"); - SPUtils.put(mContext, "AmapError", sb); - Log.e(TAG, "onLocDiagnosticMessage: " + sb); - mLocationClient.stop(); + Log.e(TAG, "amap: " + sb.toString()); } }; + + private void updateAddress(AMapLocation aMapLocation) { + NetInterfaceManager.getInstance().updateAdminInfo(); + } + + private MapBean getMapBean(AMapLocation location) { + MapBean mapBean = new MapBean(); + mapBean.setLongitude(location.getLongitude()); + mapBean.setLatitude(location.getLatitude()); + mapBean.setAdcode(location.getAdCode()); + mapBean.setAddress(location.getAddress()); + mapBean.setCity(location.getCity()); + mapBean.setCityCode(location.getCityCode()); + mapBean.setCountry(location.getCountry()); + mapBean.setCountryCode(location.getAdCode()); + mapBean.setDistrict(location.getDistrict()); + mapBean.setProvince(location.getProvince()); + mapBean.setStreet(location.getStreet()); + mapBean.setStreetNumber(location.getStreetNum()); + mapBean.setTown(location.getStreet()); + mapBean.setLocationDescribe(location.getLocationDetail()); + Log.e(TAG, "getMapBean: " + GsonUtils.toJSONString(mapBean)); + return mapBean; + + } + + private void saveMapResult(MapBean mapBean) { + Log.e(TAG, "saveMapResult: " + GsonUtils.toJSONString(mapBean)); + mMMKV.encode(CommonConfig.MAP_LOCATION_JSON_KEY, GsonUtils.toJSONString(mapBean)); + mMMKV.encode(CommonConfig.MAP_LONGITUDE_KEY, mapBean.getLongitude()); + mMMKV.encode(CommonConfig.MAP_LATITUDE_KEY, mapBean.getLatitude()); + mMMKV.encode(CommonConfig.MAP_ADDRESS_KEY, mapBean.getAddress()); + mMMKV.encode(CommonConfig.MAP_PROVINCE_KEY, mapBean.getProvince()); + mMMKV.encode(CommonConfig.MAP_CITY_KEY, mapBean.getCity()); + mMMKV.encode(CommonConfig.MAP_DISTRICT_KEY, mapBean.getDistrict()); + mMMKV.encode(CommonConfig.MAP_STREET_KEY, mapBean.getStreet()); + mMMKV.encode(CommonConfig.MAP_LOCATION_DESCRIBE_KEY, mapBean.getLocationDescribe()); + } + } diff --git a/app/src/main/java/com/fuying/sn/manager/ControlManager.java b/app/src/main/java/com/fuying/sn/manager/ControlManager.java index 8865476..5e9e158 100644 --- a/app/src/main/java/com/fuying/sn/manager/ControlManager.java +++ b/app/src/main/java/com/fuying/sn/manager/ControlManager.java @@ -14,7 +14,7 @@ import com.fuying.sn.bean.SystemSettings; import com.fuying.sn.config.CommonConfig; import com.fuying.sn.gson.GsonUtils; import com.fuying.sn.utils.ApkUtils; -import com.fuying.sn.utils.JGYUtils; +import com.fuying.sn.utils.JgyUtils; import com.fuying.sn.utils.SPUtils; import com.fuying.sn.utils.ToastUtil; import com.google.gson.JsonObject; @@ -87,7 +87,7 @@ public class ControlManager { setAppstoreAdmin(systemSettings); setDefaultApp(systemSettings); setIsControl(systemSettings); - JGYUtils.getInstance().updateForbidList(); + JgyUtils.getInstance().updateForbidList(); } } @@ -117,7 +117,7 @@ public class ControlManager { setAppstoreAdmin(systemSettings); setDefaultApp(systemSettings); setIsControl(systemSettings); - JGYUtils.getInstance().updateForbidList(); + JgyUtils.getInstance().updateForbidList(); } /** @@ -202,7 +202,7 @@ public class ControlManager { break; } Intent usbIntent = new Intent(usbStatus); - if (JGYUtils.getInstance().checkAppPlatform() != JGYUtils.G11JPlatform) { + if (JgyUtils.getInstance().checkAppPlatform() != JgyUtils.G11JPlatform) { usbIntent.setPackage("com.android.settings"); } // if (!BuildConfig.DEBUG) @@ -246,7 +246,7 @@ public class ControlManager { boolean aole_white_list_Array = Settings.System.putString(mResolver, CommonConfig.AOLE_ACTION_WHITE_LIST_ARRAY, setting_phones); Log.e("SystemSetting", "aole_white_list_Array: " + aole_white_list_Array + "=" + setting_phones); - if (JGYUtils.getInstance().checkAppPlatform() == JGYUtils.M40sePlatform) { + if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.M40sePlatform) { Settings.System.putInt(mResolver, CommonConfig.AOLE_ACTION_SDCARD_FORBID_ON, 0); } else { int setting_memory = changeNum(settings.getSetting_memory()); @@ -268,7 +268,7 @@ public class ControlManager { boolean aole_white_list_Array = Settings.System.putString(mResolver, CommonConfig.AOLE_ACTION_WHITE_LIST_ARRAY, ""); // ToastTool.show("qch_call_forbid::"+setting_call+"----setting_phones::"+setting_phones+"----"+aole_white_list_Array+"---"+qch_call_forbid); Log.e(TAG, "aole_white_list_Array:" + aole_white_list_Array + "---" + aole_white_list_Array); - if (JGYUtils.getInstance().checkAppPlatform() == JGYUtils.M40sePlatform) { + if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.M40sePlatform) { //在m40se上 开启管控会造成第三方app无法读取sd卡,暂时打开 Settings.System.putInt(mResolver, CommonConfig.AOLE_ACTION_SDCARD_FORBID_ON, 0); } else { @@ -395,7 +395,7 @@ public class ControlManager { break; default: } - if (JGYUtils.getInstance().checkAppPlatform() == JGYUtils.T30ProPlatform) { + if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.T30ProPlatform) { Intent navIntent = new Intent(navigationStatus); mContext.sendBroadcast(navIntent); } else { @@ -836,7 +836,7 @@ public class ControlManager { Log.e(TAG, "setDefaultApp: desktop_app = " + desktop_app); if (!TextUtils.isEmpty(desktop_app)) { mMMKV.encode(CommonConfig.DESKTOP_APP_KEY, desktop_app); - JGYUtils.getInstance().setDefaultDesktop(desktop_app); + JgyUtils.getInstance().setDefaultDesktop(desktop_app); } else { mMMKV.encode(CommonConfig.DESKTOP_APP_KEY, ""); } @@ -844,7 +844,7 @@ public class ControlManager { Log.e(TAG, "setDefaultApp: browser_app = " + browser_app); if (!TextUtils.isEmpty(browser_app)) { mMMKV.encode(CommonConfig.BROWSER_APP_KEY, browser_app); - JGYUtils.getInstance().setDefaultBrowser(browser_app); + JgyUtils.getInstance().setDefaultBrowser(browser_app); } else { mMMKV.encode(CommonConfig.BROWSER_APP_KEY, ""); } @@ -852,7 +852,7 @@ public class ControlManager { Log.e(TAG, "setDefaultApp: typewriting_app = " + typewriting_app); if (!TextUtils.isEmpty(typewriting_app)) { mMMKV.encode(CommonConfig.TYPEWRITING_APP_KEY, typewriting_app); - JGYUtils.getInstance().setDefaultInputMethod(typewriting_app); + JgyUtils.getInstance().setDefaultInputMethod(typewriting_app); } else { mMMKV.encode(CommonConfig.TYPEWRITING_APP_KEY, ""); } @@ -865,17 +865,17 @@ public class ControlManager { String desktop_app = mMMKV.decodeString(CommonConfig.DESKTOP_APP_KEY, ""); Log.e(TAG, "setDefaultApp: desktop_app = " + desktop_app); if (!TextUtils.isEmpty(desktop_app)) { - JGYUtils.getInstance().setDefaultDesktop(desktop_app); + JgyUtils.getInstance().setDefaultDesktop(desktop_app); } String browser_app = mMMKV.decodeString(CommonConfig.BROWSER_APP_KEY, ""); Log.e(TAG, "setDefaultApp: browser_app = " + browser_app); if (!TextUtils.isEmpty(browser_app)) { - JGYUtils.getInstance().setDefaultBrowser(browser_app); + JgyUtils.getInstance().setDefaultBrowser(browser_app); } String typewriting_app = mMMKV.decodeString(CommonConfig.TYPEWRITING_APP_KEY, ""); Log.e(TAG, "setDefaultApp: typewriting_app = " + typewriting_app); if (!TextUtils.isEmpty(typewriting_app)) { - JGYUtils.getInstance().setDefaultInputMethod(typewriting_app); + JgyUtils.getInstance().setDefaultInputMethod(typewriting_app); } } diff --git a/app/src/main/java/com/fuying/sn/network/NetInterfaceManager.java b/app/src/main/java/com/fuying/sn/network/NetInterfaceManager.java index 6ab1d44..c0d3f23 100644 --- a/app/src/main/java/com/fuying/sn/network/NetInterfaceManager.java +++ b/app/src/main/java/com/fuying/sn/network/NetInterfaceManager.java @@ -81,10 +81,10 @@ import com.fuying.sn.network.api.QueryAllApp; import com.fuying.sn.network.api.QueryAppInside; import com.fuying.sn.network.api.QuerySnAppStart; import com.fuying.sn.network.api.RunningApp; -import com.fuying.sn.network.api.SNInfoApi; import com.fuying.sn.network.api.ScreenLock; import com.fuying.sn.network.api.ScreenState; import com.fuying.sn.network.api.Setting; +import com.fuying.sn.network.api.SnApi; import com.fuying.sn.network.api.SnBindApi; import com.fuying.sn.network.api.SnTagApi; import com.fuying.sn.network.api.TimeControl; @@ -98,7 +98,7 @@ import com.fuying.sn.utils.ApkUtils; import com.fuying.sn.utils.AppUsedTimeUtils; import com.fuying.sn.utils.CmdUtil; import com.fuying.sn.utils.FileUtils; -import com.fuying.sn.utils.JGYUtils; +import com.fuying.sn.utils.JgyUtils; import com.fuying.sn.utils.SPUtils; import com.fuying.sn.utils.StringUtils; import com.fuying.sn.utils.URLUtils; @@ -107,8 +107,6 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.reflect.TypeToken; -import com.tencent.android.tpush.XGIOperateCallback; -import com.tencent.android.tpush.XGPushManager; import com.tencent.mmkv.MMKV; import com.trello.rxlifecycle4.RxLifecycle; import com.trello.rxlifecycle4.android.ActivityEvent; @@ -246,14 +244,14 @@ public class NetInterfaceManager { * * */ - public Observable> getsnInfoControl() { - return mRetrofit.create(SNInfoApi.class) + public Observable> getSnInfoControl() { + return mRetrofit.create(SnApi.class) .getsninfo(Utils.getSerial()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } - public Observable> getsettingControl() { + public Observable> getSettingControl() { return mRetrofit.create(Setting.class) .getSetting(Utils.getSerial()) .subscribeOn(Schedulers.io()) @@ -353,13 +351,13 @@ public class NetInterfaceManager { public Observable> getUpdateObservable(String packageName) { return getNewestAppUpdateControl() - .getAppUpdate(packageName, "0", JGYUtils.getInstance().checkAppPlatform()) + .getAppUpdate(packageName, "0", JgyUtils.getInstance().checkAppPlatform()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } public Observable>> getOverallAppObservable() { - int platformCode = JGYUtils.getInstance().checkAppPlatform(); + int platformCode = JgyUtils.getInstance().checkAppPlatform(); return getOverallAppByPlatformControl() .getOverallApp(platformCode) .subscribeOn(Schedulers.io()) @@ -604,10 +602,10 @@ public class NetInterfaceManager { if (refresh) { connectMode = ConnectMode.DEFAULT; } - if (ConnectManager.getInstance().isNeedConnect(UrlAddress.SNINFO, connectMode)) { + if (ConnectManager.getInstance().isNeedConnect(UrlAddress.SN_INFO, connectMode)) { getSnInfo(lifecycle, callback); } else { - String jsonString = mCacheHelper.getAsString(UrlAddress.SNINFO); + String jsonString = mCacheHelper.getAsString(UrlAddress.SN_INFO); //为 "" 是已经请求成功的 if (jsonString == null) { getSnInfo(lifecycle, callback); @@ -623,18 +621,18 @@ public class NetInterfaceManager { } public void getSnInfo(BehaviorSubject lifecycle, ObserverCallback callback) { - getsnInfoControl() + getSnInfoControl() .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY)) .subscribe(getSnInfoObserver(callback)); } public void getSnInfo(ObserverCallback callback) { - getsnInfoControl() + getSnInfoControl() .subscribe(getSnInfoObserver(callback)); } public void getSnInfo() { - getsnInfoControl() + getSnInfoControl() .subscribe(getSnInfoObserver(null)); } @@ -653,7 +651,7 @@ public class NetInterfaceManager { SPUtils.put(mContext, CommonConfig.JGY_FIRST_CONNECT, 1); if (callback != null) callback.onNext(userInfoBaseResponse); - mCacheHelper.put(UrlAddress.SNINFO, GsonUtils.toJSONString(userInfoBaseResponse)); + mCacheHelper.put(UrlAddress.SN_INFO, GsonUtils.toJSONString(userInfoBaseResponse)); int code = userInfoBaseResponse.code; if (userInfoBaseResponse.code == 200) { SPUtils.put(mContext, CommonConfig.isLogined, 1); @@ -671,11 +669,11 @@ public class NetInterfaceManager { SPUtils.put(mContext, "USERINFO_GRADE", snInfo.getGrade()); } } else if (code == 300) { - mCacheHelper.put(UrlAddress.SNINFO, ""); + mCacheHelper.put(UrlAddress.SN_INFO, ""); SPUtils.put(mContext, CommonConfig.isLogined, 0); Settings.System.putInt(mContext.getContentResolver(), CommonConfig.KEY_IS_CONTROL, 0); } else if (code == 401) { - mCacheHelper.put(UrlAddress.SNINFO, ""); + mCacheHelper.put(UrlAddress.SN_INFO, ""); SPUtils.put(mContext, CommonConfig.isLogined, 2); Settings.System.putInt(mContext.getContentResolver(), CommonConfig.KEY_IS_CONTROL, 0); } @@ -704,27 +702,27 @@ public class NetInterfaceManager { */ public void setPushTags() { Set set = new HashSet<>(); - JGYUtils.getInstance().getAppPlatform(platform -> { - if (platform == JGYUtils.MTKPlatform) { - set.add(JGYUtils.MTKTag); - } else if (platform == JGYUtils.ZhanruiPlatform) { - set.add(JGYUtils.ZhanruiTag); - } else if (platform == JGYUtils.M40sePlatform) { - set.add(JGYUtils.M40SETag); - } else if (platform == JGYUtils.T30ProPlatform) { - set.add(JGYUtils.T30PROTag); - } else if (platform == JGYUtils.MTK8515Platform) { - set.add(JGYUtils.MTK8515Tag); - } else if (platform == JGYUtils.G13Platform) { - set.add(JGYUtils.G13Tag); - } else if (platform == JGYUtils.iPlay50SEPlatform) { - set.add(JGYUtils.iPlay50SETag); - } else if (platform == JGYUtils.G6Platform) { - set.add(JGYUtils.G6Tag); - } else if (platform == JGYUtils.G11JPlatform) { - set.add(JGYUtils.G11JTag); - } else if (platform == JGYUtils.G12NLPlatform) { - set.add(JGYUtils.G12NLTag); + JgyUtils.getInstance().getAppPlatform(platform -> { + if (platform == JgyUtils.MTKPlatform) { + set.add(JgyUtils.MTKTag); + } else if (platform == JgyUtils.ZhanruiPlatform) { + set.add(JgyUtils.ZhanruiTag); + } else if (platform == JgyUtils.M40sePlatform) { + set.add(JgyUtils.M40SETag); + } else if (platform == JgyUtils.T30ProPlatform) { + set.add(JgyUtils.T30PROTag); + } else if (platform == JgyUtils.MTK8515Platform) { + set.add(JgyUtils.MTK8515Tag); + } else if (platform == JgyUtils.G13Platform) { + set.add(JgyUtils.G13Tag); + } else if (platform == JgyUtils.iPlay50SEPlatform) { + set.add(JgyUtils.iPlay50SETag); + } else if (platform == JgyUtils.G6Platform) { + set.add(JgyUtils.G6Tag); + } else if (platform == JgyUtils.G11JPlatform) { + set.add(JgyUtils.G11JTag); + } else if (platform == JgyUtils.G12NLPlatform) { + set.add(JgyUtils.G12NLTag); } }); clearAndAppendTags(set); @@ -733,38 +731,27 @@ public class NetInterfaceManager { public void setPushTags(String tag) { Set set = new HashSet<>(); set.add(tag); - JGYUtils.getInstance().getAppPlatform(platform -> { - if (platform == JGYUtils.MTKPlatform) { - set.add(JGYUtils.MTKTag); - } else if (platform == JGYUtils.ZhanruiPlatform) { - set.add(JGYUtils.ZhanruiTag); - } else if (platform == JGYUtils.M40sePlatform) { - set.add(JGYUtils.M40SETag); - } else if (platform == JGYUtils.T30ProPlatform) { - set.add(JGYUtils.T30PROTag); - } else if (platform == JGYUtils.MTK8515Platform) { - set.add(JGYUtils.MTK8515Tag); - } else if (platform == JGYUtils.G13Platform) { - set.add(JGYUtils.G13Tag); - } else if (platform == JGYUtils.iPlay50SEPlatform) { - set.add(JGYUtils.iPlay50SETag); - } else if (platform == JGYUtils.G6Platform) { - set.add(JGYUtils.G6Tag); - } else if (platform == JGYUtils.G11JPlatform) { - set.add(JGYUtils.G11JTag); - } else if (platform == JGYUtils.G12NLPlatform) { - set.add(JGYUtils.G12NLTag); - } - }); - XGPushManager.clearAndAppendTags(mContext, "clearAndAppendTags :" + System.currentTimeMillis(), set, new XGIOperateCallback() { - @Override - public void onSuccess(Object o, int i) { - Log.e("clearAndAppendTags", "onSuccess: " + o + " tag " + set); - } - - @Override - public void onFail(Object o, int i, String s) { - Log.e("clearAndAppendTags", "onFail: " + o); + JgyUtils.getInstance().getAppPlatform(platform -> { + if (platform == JgyUtils.MTKPlatform) { + set.add(JgyUtils.MTKTag); + } else if (platform == JgyUtils.ZhanruiPlatform) { + set.add(JgyUtils.ZhanruiTag); + } else if (platform == JgyUtils.M40sePlatform) { + set.add(JgyUtils.M40SETag); + } else if (platform == JgyUtils.T30ProPlatform) { + set.add(JgyUtils.T30PROTag); + } else if (platform == JgyUtils.MTK8515Platform) { + set.add(JgyUtils.MTK8515Tag); + } else if (platform == JgyUtils.G13Platform) { + set.add(JgyUtils.G13Tag); + } else if (platform == JgyUtils.iPlay50SEPlatform) { + set.add(JgyUtils.iPlay50SETag); + } else if (platform == JgyUtils.G6Platform) { + set.add(JgyUtils.G6Tag); + } else if (platform == JgyUtils.G11JPlatform) { + set.add(JgyUtils.G11JTag); + } else if (platform == JgyUtils.G12NLPlatform) { + set.add(JgyUtils.G12NLTag); } }); @@ -792,17 +779,6 @@ public class NetInterfaceManager { if (!TextUtils.isEmpty(tag)) { tagSets.add(tag); } - XGPushManager.clearAndAppendTags(mContext, "clearAndAppendTags :" + System.currentTimeMillis(), tagSets, new XGIOperateCallback() { - @Override - public void onSuccess(Object o, int i) { - Log.e("clearAndAppendTags", "onSuccess: " + o + " tag " + tagSets); - } - - @Override - public void onFail(Object o, int i, String s) { - Log.e("clearAndAppendTags", "onFail: " + o); - } - }); String[] set = new ArrayList<>(tagSets).toArray(new String[tagSets.size()]); CloudPushService pushService = PushServiceFactory.getCloudPushService(); @@ -836,8 +812,8 @@ public class NetInterfaceManager { private Set getShowPackages() { HashSet pkgSet = new HashSet<>(); pkgSet.addAll(showPackages); - pkgSet.addAll(JGYUtils.getInstance().getFXYApp()); - pkgSet.addAll(JGYUtils.getInstance().getAppAndWhite()); + pkgSet.addAll(JgyUtils.getInstance().getFXYApp()); + pkgSet.addAll(JgyUtils.getInstance().getAppAndWhite()); return pkgSet; } @@ -946,8 +922,8 @@ public class NetInterfaceManager { } public void getElderlyUsersApp() { - Observable.zip(getUpdateObservable(JGYUtils.AIHealth), - getUpdateObservable(JGYUtils.ElderlyDesktop), + Observable.zip(getUpdateObservable(JgyUtils.AIHealth), + getUpdateObservable(JgyUtils.ElderlyDesktop), new BiFunction, BaseResponse, List>() { @Override public List apply(BaseResponse appInfoBaseResponse, BaseResponse appInfoBaseResponse2) throws Throwable { @@ -992,7 +968,7 @@ public class NetInterfaceManager { public void checkUpdate(String packageName, String versionCode) { Log.e("checkUpdate", "packageName: " + packageName); getNewestAppUpdateControl() - .getAppUpdate(packageName, versionCode, JGYUtils.getInstance().checkAppPlatform()) + .getAppUpdate(packageName, versionCode, JgyUtils.getInstance().checkAppPlatform()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer>() { @@ -1035,10 +1011,10 @@ public class NetInterfaceManager { if (refresh) { connectMode = ConnectMode.DEFAULT; } - if (ConnectManager.getInstance().isNeedConnect(UrlAddress.GET_NEWESTAPPUPDATE, connectMode)) { + if (ConnectManager.getInstance().isNeedConnect(UrlAddress.GET_NEWEST_APPUPDATE, connectMode)) { checkAllAppUpdate(lifecycle, callback); } else { - String jsonString = mCacheHelper.getAsString(UrlAddress.GET_NEWESTAPPUPDATE); + String jsonString = mCacheHelper.getAsString(UrlAddress.GET_NEWEST_APPUPDATE); //为 "" 是已经请求成功的 if (jsonString == null) { checkAllAppUpdate(lifecycle, callback); @@ -1056,7 +1032,7 @@ public class NetInterfaceManager { public void checkAllAppUpdate(BehaviorSubject lifecycle, CompleteCallback callback) { Observable.zip( getUpdateObservable(BuildConfig.APPLICATION_ID), - getUpdateObservable(JGYUtils.PACKAGE_APPSTORE), + getUpdateObservable(JgyUtils.PACKAGE_APPSTORE), // getUpdateObservable(JGYUtils.PACKAGE_OS), // getUpdateObservable(JGYUtils.PACKAGE_BROWSER), // getUpdateObservable(JGYUtils.Notifications), @@ -1091,7 +1067,7 @@ public class NetInterfaceManager { @Override public void onNext(@NonNull List appInfos) { Log.e("checkAllAppUpdate", "onNext: " + appInfos); - mCacheHelper.put(UrlAddress.GET_NEWESTAPPUPDATE, GsonUtils.toJSONString(appInfos)); + mCacheHelper.put(UrlAddress.GET_NEWEST_APPUPDATE, GsonUtils.toJSONString(appInfos)); getAllAppUpdate(appInfos); } @@ -1114,11 +1090,11 @@ public class NetInterfaceManager { if (refresh) { connectMode = ConnectMode.DEFAULT; } - if (ConnectManager.getInstance().isNeedConnect(JGYUtils.FUXIAOYING_KEY, connectMode)) { + if (ConnectManager.getInstance().isNeedConnect(JgyUtils.FUXIAOYING_KEY, connectMode)) { checkFXYAppUpdate(lifecycle, callback); } else { //有乱码 - String jsonString = mMMKV.decodeString(JGYUtils.FUXIAOYING_KEY + "_mmkv"); + String jsonString = mMMKV.decodeString(JgyUtils.FUXIAOYING_KEY + "_mmkv"); Log.e(TAG, "checkFXYAppUpdate: jsonString = " + jsonString); //为 "" 是已经请求成功的 if (jsonString == null) { @@ -1136,15 +1112,15 @@ public class NetInterfaceManager { public void checkFXYAppUpdate(BehaviorSubject lifecycle, CompleteCallback callback) { Observable.zip( - getUpdateObservable(JGYUtils.gkwxhd), - getUpdateObservable(JGYUtils.fuxiaoying), - getUpdateObservable(JGYUtils.moshujia), - getUpdateObservable(JGYUtils.english), - getUpdateObservable(JGYUtils.zhiduoke), - getUpdateObservable(JGYUtils.aobama), - getUpdateObservable(JGYUtils.growthspace), - getUpdateObservable(JGYUtils.pandaabc), - getUpdateObservable(JGYUtils.qibenyi), + getUpdateObservable(JgyUtils.gkwxhd), + getUpdateObservable(JgyUtils.fuxiaoying), + getUpdateObservable(JgyUtils.moshujia), + getUpdateObservable(JgyUtils.english), + getUpdateObservable(JgyUtils.zhiduoke), + getUpdateObservable(JgyUtils.aobama), + getUpdateObservable(JgyUtils.growthspace), + getUpdateObservable(JgyUtils.pandaabc), + getUpdateObservable(JgyUtils.qibenyi), (appInfoBaseResponse, appInfoBaseResponse2, appInfoBaseResponse3, appInfoBaseResponse4, appInfoBaseResponse5, appInfoBaseResponse6, appInfoBaseResponse7, appInfoBaseResponse8, appInfoBaseResponse9) -> { List appInfoList = new ArrayList<>(); if (appInfoBaseResponse.code == 200) { @@ -1188,7 +1164,7 @@ public class NetInterfaceManager { @Override public void onNext(@NonNull List appInfos) { Log.e("checkFXYAppUpdate", "onNext: " + appInfos); - mCacheHelper.put(JGYUtils.FUXIAOYING_KEY, GsonUtils.toJSONString(appInfos)); + mCacheHelper.put(JgyUtils.FUXIAOYING_KEY, GsonUtils.toJSONString(appInfos)); getAllAppUpdate(appInfos); } @@ -1839,7 +1815,7 @@ public class NetInterfaceManager { } String jsonString = GsonUtils.toJSONString(hideAPPList); SPUtils.put(mContext, "Hide_APP_List", String.join(",", jsonString)); - JGYUtils.getInstance().updateHideList(); + JgyUtils.getInstance().updateHideList(); Log.e(TAG, "Hide_APP_List: " + jsonString); } } @@ -1890,16 +1866,17 @@ public class NetInterfaceManager { } public void updateAdminInfo() { - String address = String.valueOf(SPUtils.get(mContext, "AmapAddress", "-")); - String longitude = String.valueOf(SPUtils.get(mContext, "longitude", "0")); - String latitude = String.valueOf(SPUtils.get(mContext, "latitude", "0")); + String address = String.valueOf(mMMKV.decodeString(CommonConfig.MAP_ADDRESS_KEY, "-")); + String longitude = String.valueOf(mMMKV.decodeDouble(CommonConfig.MAP_LONGITUDE_KEY, 0L)); + String latitude = String.valueOf(mMMKV.decodeDouble(CommonConfig.MAP_LATITUDE_KEY, 0L)); JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("address", address); jsonObject.addProperty("longitude", longitude); jsonObject.addProperty("latitude", latitude); + JsonObject softwareJson = new JsonObject(); - softwareJson.addProperty("appstore_version", ApkUtils.getAPPVersionName(mContext, JGYUtils.PACKAGE_APPSTORE)); - softwareJson.addProperty("updatetools_version", ApkUtils.getAPPVersionName(mContext, JGYUtils.PACKAGE_UPDATETOOLS)); + softwareJson.addProperty("appstore_version", ApkUtils.getAPPVersionName(mContext, JgyUtils.PACKAGE_APPSTORE)); + softwareJson.addProperty("updatetools_version", ApkUtils.getAPPVersionName(mContext, JgyUtils.PACKAGE_UPDATETOOLS)); softwareJson.addProperty("info_version", ApkUtils.getAPPVersionName(mContext, "com.fuying.sn")); softwareJson.addProperty("jiaoguanyi_version", ApkUtils.getAPPVersionName(mContext, "com.jiaoguanyi.os")); softwareJson.addProperty("gankao_version", ApkUtils.getAPPVersionName(mContext, "com.gankao.gkwxhd")); @@ -1921,7 +1898,7 @@ public class NetInterfaceManager { nowJson.addProperty("hardware", hardware); nowJson.addProperty("software", software); String string = GsonUtils.toJSONString(nowJson); - String oldString = mMMKV.decodeString(UrlAddress.UPDATE_SNINFO, ""); + String oldString = mMMKV.decodeString(UrlAddress.UPDATE_SN_INFO, ""); Log.e(TAG, "updateAdminInfo: oldString = " + oldString); Log.e(TAG, "updateAdminInfo: string = " + string); if (oldString.equals(string)) { @@ -1945,7 +1922,7 @@ public class NetInterfaceManager { @Override public void onNext(@NonNull BaseResponse baseResponse) { if (baseResponse.code == 200) { - mMMKV.encode(UrlAddress.UPDATE_SNINFO, string); + mMMKV.encode(UrlAddress.UPDATE_SN_INFO, string); } Log.e("updateAdminInfo", "onNext: " + baseResponse); @@ -2127,18 +2104,18 @@ public class NetInterfaceManager { } public void getSystemSettings(BehaviorSubject lifecycle, CompleteCallback callback) { - getsettingControl() + getSettingControl() .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY)) .subscribe(getSystemSettingsObserver(callback)); } public void getSystemSettings(CompleteCallback callback) { - getsettingControl() + getSettingControl() .subscribe(getSystemSettingsObserver(callback)); } public void getSystemSettings() { - getsettingControl() + getSettingControl() .subscribe(getSystemSettingsObserver(null)); } @@ -2308,7 +2285,7 @@ public class NetInterfaceManager { JsonObject jsonObject = GsonUtils.getJsonObject(jsonString); String ids = jsonObject.get("ids").getAsString(); String packages = jsonObject.get("package").getAsString(); - JGYUtils.getInstance().writeDeselectIDtoSystem(ids, packages); + JgyUtils.getInstance().writeDeselectIDtoSystem(ids, packages); } else { Log.e("getAppInside", "onNext: " + baseResponse.msg); Settings.System.putString(crv, CommonConfig.AOLE_ACTION_APP_FORBID_ID, " "); @@ -2710,7 +2687,7 @@ public class NetInterfaceManager { Log.e(TAG, "installOverallApp: " + packageList); for (OverallAppBean overallAppBean : overallAppBeanList) { String packages = overallAppBean.getApp_package(); - if (!JGYUtils.mGlobalUpdatePkgs.contains(packages)) { + if (!JgyUtils.mGlobalUpdatePkgs.contains(packages)) { continue; } String url = overallAppBean.getApp_url(); @@ -3206,7 +3183,7 @@ public class NetInterfaceManager { @Override public void onComplete() { Log.e("getAppAndWhite", "onComplete: "); - JGYUtils.getInstance().putBuiltInApps(); + JgyUtils.getInstance().putBuiltInApps(); if (callback != null) { callback.onComplete(); } diff --git a/app/src/main/java/com/fuying/sn/network/UrlAddress.java b/app/src/main/java/com/fuying/sn/network/UrlAddress.java index 6be52d3..fa89924 100644 --- a/app/src/main/java/com/fuying/sn/network/UrlAddress.java +++ b/app/src/main/java/com/fuying/sn/network/UrlAddress.java @@ -4,80 +4,30 @@ public class UrlAddress { /*主页接口*/ static final String ROOT_URL = "http://47.111.23.154/android/"; - //不需要做缓存的接口 - - /*获取app桌面管控配置*/ - public final static String GET_APP_TIME_CONTROL = "Control/getAppTimeControl"; - /*获取整机管控配置*/ - public final static String GET_SN_TIME_CONTROL = "Control/getSnTimeControl"; - /*获取所有应用时间*/ - public final static String GET_MY_APP_LIST = "app/myAppList"; - - //不需要在后台添加sn的接口 - /*获取所有全局更新*/ - public final static String GE_TOVERALL_APPBYPLATFORM = "app/overallAppByPlatform"; - /*获取应用内置白名单*/ - public static final String GET_APP_AND_WHITE = "getAppAndWhite"; - /*获取管理员上传的应用*/ - public final static String GET_ADMIN_APP = "getAdminApp"; - /*根据包名获取更新*/ - public final static String GET_NEWESTAPPUPDATE = "app/newestAppUpdate"; - /*发送设备基本信息*/ - public final static String UPDATE_SNINFO = "sn/updateAdminSn"; - /*绑定设备消息*/ public final static String BIND_DEVICES = "sn/bindSn"; - + /*获取设备绑定状态*/ public final static String GET_SN_BIND = "sn/getSnBind"; - + /*设备信息接口*/ + public static final String SN_INFO = "sn/getSnInfo"; + /*获取用户头像和信息*/ + public static final String GET_USER_AVATAR_INFO = "sn/getUserAvatarInfo"; /*获取批次*/ public static final String GET_BATCH = "sn/getBatch"; /*获取标签*/ public static final String GET_TAG = "sn/get-tag"; - /*设备信息接口*/ - public static final String SNINFO = "sn/getSnInfo"; - - /*获取正在运行的app*/ - public static final String RUN_NEW_APP = "app/runNewApp"; - /*获取所有应用*/ - public final static String GET_ALL_PACKAGE = "app/queryAllApp"; - /*获取系统设置*/ - public final static String GET_SETTINGS = "control/getSetting"; - /*浏览器网址管控*/ - public final static String SET_BROWSER_URL = "control/getBrowser"; - /*浏览器书签管控*/ - public final static String SET_BROWSER_LABEL = "control/getLabel"; - /*获取强制下载*/ - public final static String GET_FORCE_INSTALL = "app/getForceDownload"; + /*发送设备基本信息*/ + public final static String UPDATE_SN_INFO = "sn/updateAdminSn"; /*获取app管控*/ public final static String GET_APP_START = "sn/querySnAppStart"; - /*发app跳转管控*/ + /*app跳转管控*/ public final static String GET_APP_JUMP = "sn/querySnJump"; - /*全局黑名单列表*/ - public final static String GLOBAL_BLACKLIST = "common/blacklist"; - /*app内部管控*/ - public final static String QUERY_APP_INSIDE = "control/queryAppInside"; - /*发送卸载或者安装信息*/ - public final static String SEND_INSTALLEDORREMOVED = "app/addAppInstall"; - + /*获取时间管控*/ + public final static String GET_TIME_CONTROL = "sn/getTimeControl"; /*上传屏幕截图*/ public final static String UPLOAD_SCREEN_SNAPSHOT = "sn/uploadScreenshot"; - - /*获取时间管控*/ - public final static String GET_TIME_CONTROL = "sn/getTimeControlControl"; - /*获取用户头像和信息*/ - public static final String GET_USER_AVATAR_INFO = "sn/getUserAvatarInfo"; - /*获取小程序二维码*/ - public static final String GET_APPLET_QRCODE = "file/getAppletQrCode"; - /*获取操作指南*/ - public static final String GET_OPERATION_GUIDE = "file/getFiles"; /*上传控制面版截图*/ public static final String UPLOAD_CONTROL_SCREENSHOT = "sn/uploadControlScreenshot"; - /*获取是否为网课模式*/ - public static final String GET_CLOUD_LESSON_SETTING = "Control/getCloudLessonSetting"; - /*应用使用记录*/ - public static final String APP_USAGE_RECORD = "appUsageRecord"; - /*获取屏幕管控*/ public final static String GET_SCREEN_LOCK = "sn/getScreenshot"; /*获取锁屏密码*/ @@ -97,6 +47,52 @@ public class UrlAddress { public static final String UPLOAD_IS_LOG_FILE = "sn/uploadIsLogFile"; + /*获取所有应用时间*/ + public final static String GET_MY_APP_LIST = "app/myAppList"; + /*获取所有全局更新*/ + public final static String GE_TOVERALL_APPBYPLATFORM = "app/overallAppByPlatform"; + /*根据包名获取更新*/ + public final static String GET_NEWEST_APPUPDATE = "app/newestAppUpdate"; + /*获取正在运行的app*/ + public static final String RUN_NEW_APP = "app/runNewApp"; + /*获取所有应用*/ + public final static String GET_ALL_PACKAGE = "app/queryAllApp"; + /*获取强制下载*/ + public final static String GET_FORCE_INSTALL = "app/getForceDownload"; + /*发送卸载或者安装信息*/ + public final static String SEND_INSTALLEDORREMOVED = "app/addAppInstall"; + + /*获取系统设置*/ + public final static String GET_SETTINGS = "control/getSetting"; + /*浏览器网址管控*/ + public final static String SET_BROWSER_URL = "control/getBrowser"; + /*浏览器书签管控*/ + public final static String SET_BROWSER_LABEL = "control/getLabel"; + /*app内部管控*/ + public final static String QUERY_APP_INSIDE = "control/queryAppInside"; + /*获取是否为网课模式*/ + public static final String GET_CLOUD_LESSON_SETTING = "Control/getCloudLessonSetting"; + /*获取app桌面管控配置*/ + public final static String GET_APP_TIME_CONTROL = "Control/getAppTimeControl"; + /*获取整机管控配置*/ + public final static String GET_SN_TIME_CONTROL = "Control/getSnTimeControl"; + + + /*获取小程序二维码*/ + public static final String GET_APPLET_QRCODE = "file/getAppletQrCode"; + /*获取操作指南*/ + public static final String GET_OPERATION_GUIDE = "file/getFiles"; + + /*全局黑名单列表*/ + public final static String GLOBAL_BLACKLIST = "common/blacklist"; + + /*获取应用内置白名单*/ + public static final String GET_APP_AND_WHITE = "getAppAndWhite"; + /*获取管理员上传的应用*/ + public final static String GET_ADMIN_APP = "getAdminApp"; + /*应用使用记录*/ + public static final String APP_USAGE_RECORD = "appUsageRecord"; + /*获取公网IP*/ public static final String SHOUHU_CITYJSON = "http://pv.sohu.com/cityjson/"; } diff --git a/app/src/main/java/com/fuying/sn/network/api/NewestAppUpdate.java b/app/src/main/java/com/fuying/sn/network/api/NewestAppUpdate.java index 769427b..e38a44d 100644 --- a/app/src/main/java/com/fuying/sn/network/api/NewestAppUpdate.java +++ b/app/src/main/java/com/fuying/sn/network/api/NewestAppUpdate.java @@ -9,7 +9,7 @@ import retrofit2.http.GET; import retrofit2.http.Query; public interface NewestAppUpdate { - @GET(UrlAddress.GET_NEWESTAPPUPDATE) + @GET(UrlAddress.GET_NEWEST_APPUPDATE) Observable> getAppUpdate( @Query("packageName") String packageName, @Query("versionCode") String versionCode, diff --git a/app/src/main/java/com/fuying/sn/network/api/SNInfoApi.java b/app/src/main/java/com/fuying/sn/network/api/SnApi.java similarity index 85% rename from app/src/main/java/com/fuying/sn/network/api/SNInfoApi.java rename to app/src/main/java/com/fuying/sn/network/api/SnApi.java index d6cfc48..7b84624 100644 --- a/app/src/main/java/com/fuying/sn/network/api/SNInfoApi.java +++ b/app/src/main/java/com/fuying/sn/network/api/SnApi.java @@ -8,8 +8,8 @@ import io.reactivex.rxjava3.core.Observable; import retrofit2.http.GET; import retrofit2.http.Query; -public interface SNInfoApi { - @GET(UrlAddress.SNINFO) +public interface SnApi { + @GET(UrlAddress.SN_INFO) Observable> getsninfo( @Query("sn") String sn ); diff --git a/app/src/main/java/com/fuying/sn/network/api/UpdateAdminSn.java b/app/src/main/java/com/fuying/sn/network/api/UpdateAdminSn.java index dfe98ff..0457a26 100644 --- a/app/src/main/java/com/fuying/sn/network/api/UpdateAdminSn.java +++ b/app/src/main/java/com/fuying/sn/network/api/UpdateAdminSn.java @@ -10,7 +10,7 @@ import retrofit2.http.POST; public interface UpdateAdminSn { @FormUrlEncoded - @POST(UrlAddress.UPDATE_SNINFO) + @POST(UrlAddress.UPDATE_SN_INFO) Observable sendAdminSn( @Field("sn") String sn, @Field("address") String address, diff --git a/app/src/main/java/com/fuying/sn/network/interceptor/PostCacheInterceptor.java b/app/src/main/java/com/fuying/sn/network/interceptor/PostCacheInterceptor.java index 47b37ab..3e2e998 100644 --- a/app/src/main/java/com/fuying/sn/network/interceptor/PostCacheInterceptor.java +++ b/app/src/main/java/com/fuying/sn/network/interceptor/PostCacheInterceptor.java @@ -4,10 +4,11 @@ import android.text.TextUtils; import com.blankj.utilcode.util.LogUtils; import com.fuying.sn.disklrucache.DiskLruCacheHelper; -import com.fuying.sn.utils.JGYUtils; +import com.fuying.sn.utils.JgyUtils; import java.io.IOException; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import okhttp3.Interceptor; import okhttp3.MediaType; @@ -62,7 +63,7 @@ public class PostCacheInterceptor implements Interceptor { } //没有网络连接的时候读取缓存 - if (!JGYUtils.getInstance().isNetworkConnected()) { + if (!JgyUtils.getInstance().isNetworkConnected()) { LogUtils.d("no network connected jujge cache available"); if (cacheResponse != null) { LogUtils.d("no network connected, return cache: " + cacheResponse); @@ -148,7 +149,7 @@ public class PostCacheInterceptor implements Interceptor { private String createKey(Request request) { RequestBody requestBody = request.body(); - Charset charset = Charset.forName("UTF-8"); + Charset charset = StandardCharsets.UTF_8; String url = request.url().toString(); StringBuilder sb = new StringBuilder(); sb.append(url + "&"); @@ -237,7 +238,7 @@ public class PostCacheInterceptor implements Interceptor { buffer = source.buffer(); Charset charset = response.body().contentType().charset(); if (charset == null) { - charset = Charset.forName("UTF-8"); + charset = StandardCharsets.UTF_8; } caches[REPONSE_BODY] = buffer.clone().readString(charset); } catch (IOException e) { diff --git a/app/src/main/java/com/fuying/sn/push/PushManager.java b/app/src/main/java/com/fuying/sn/push/PushManager.java index dff7c9f..62d55ac 100644 --- a/app/src/main/java/com/fuying/sn/push/PushManager.java +++ b/app/src/main/java/com/fuying/sn/push/PushManager.java @@ -37,7 +37,7 @@ import com.fuying.sn.utils.ApkUtils; import com.fuying.sn.utils.CacheUtils; import com.fuying.sn.utils.CmdUtil; import com.fuying.sn.utils.FileUtils; -import com.fuying.sn.utils.JGYUtils; +import com.fuying.sn.utils.JgyUtils; import com.fuying.sn.utils.SPUtils; import com.fuying.sn.utils.ServiceAliveUtils; import com.fuying.sn.utils.ToastUtil; @@ -319,7 +319,7 @@ public class PushManager { case JIGUANG_CAMRERA: ToastUtil.debugShow("收到推送消息: 摄像头管控"); setCameta(extras); - JGYUtils.getInstance().updateForbidList(); + JgyUtils.getInstance().updateForbidList(); break; case JIGUANG_PHONE: ToastUtil.debugShow("收到推送消息: 电话管控"); @@ -388,8 +388,8 @@ public class PushManager { ToastUtil.debugShow("收到推送消息: 强制停止应用"); JsonObject killJSONObject = GsonUtils.getJsonObject(extras); String packages = killJSONObject.get("app_package").getAsString(); - JGYUtils.getInstance().killPackage(packages); - JGYUtils.getInstance().gotoLauncher(); + JgyUtils.getInstance().killPackage(packages); + JgyUtils.getInstance().gotoLauncher(); Log.e(TAG, extras); break; case JIGUANG_LOCK_SCREEN: @@ -692,7 +692,7 @@ public class PushManager { NetInterfaceManager.getInstance().SendAppInstallInfo(); NetInterfaceManager.getInstance().getAppAndWhite(); NetInterfaceManager.getInstance().getSnInfo(); - if (JGYUtils.getInstance().isScreenOn()) { + if (JgyUtils.getInstance().isScreenOn()) { NetInterfaceManager.getInstance().screenshot(); } } @@ -805,7 +805,7 @@ public class PushManager { } JsonObject extra = GsonUtils.getJsonObject(jsonString); String packages = extra.get("package").getAsString(); - JGYUtils.getInstance().addPkgToForbid(packages); + JgyUtils.getInstance().addPkgToForbid(packages); ToastUtil.debugShow("收到应用安装消息:包名" + packages); String url = extra.get("url").getAsString(); if (TextUtils.isEmpty(url)) { @@ -1280,7 +1280,7 @@ public class PushManager { JsonObject jsonObject = GsonUtils.getJsonObject(jsonString); int search_topic = jsonObject.get("search_topic").getAsInt(); Log.e(TAG, "searchTopic: put = " + SPUtils.put(mContext, "search_topic", search_topic)); - JGYUtils.getInstance().updateForbidList(); + JgyUtils.getInstance().updateForbidList(); } private void setAlarmCLock(String ex) { @@ -1354,7 +1354,7 @@ public class PushManager { JsonElement jsonElement = jsonObject.get("desktop_app"); if (jsonElement != null) { String packeges = jsonElement.getAsString(); - JGYUtils.getInstance().setDefaultDesktop(packeges); + JgyUtils.getInstance().setDefaultDesktop(packeges); } else { } @@ -1365,7 +1365,7 @@ public class PushManager { JsonElement jsonElement = jsonObject.get("browser_app"); if (jsonElement != null) { String packeges = jsonElement.getAsString(); - JGYUtils.getInstance().setDefaultBrowser(packeges); + JgyUtils.getInstance().setDefaultBrowser(packeges); } else { Log.e(TAG, "setDefaultBrowser: packeges is NULL"); } @@ -1374,16 +1374,16 @@ public class PushManager { private void setDefaultInputMethod(String extras) { JsonObject jsonObject = GsonUtils.getJsonObject(extras); String packeges = jsonObject.get("typewriting_app").getAsString(); - JGYUtils.getInstance().setDefaultInputMethod(packeges); + JgyUtils.getInstance().setDefaultInputMethod(packeges); } private void oneKeyNetwork(String extras) { JsonObject jsonObject = GsonUtils.getJsonObject(extras); int is_network = jsonObject.get("is_network").getAsInt(); if (is_network == 1) { - JGYUtils.getInstance().oneKeyDisconnection(); + JgyUtils.getInstance().oneKeyDisconnection(); } else { - JGYUtils.getInstance().restorrNetwork(); + JgyUtils.getInstance().restorrNetwork(); } } diff --git a/app/src/main/java/com/fuying/sn/push/tpush/Constants.java b/app/src/main/java/com/fuying/sn/push/tpush/Constants.java deleted file mode 100644 index ea76bd0..0000000 --- a/app/src/main/java/com/fuying/sn/push/tpush/Constants.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.fuying.sn.push.tpush; - -/** - * Created by chacewang on 2019/7/5. - */ - -public class Constants { - public static final int TEST_LOCAL_NOTIFICATION = 1; - public static final int TEST_NOTIFICATION = 2; - public static final int TEST_SET_TAG = 3; - public static final int TEST_DEL_TAG = 4; - public static final int TEST_SET_ACCOUNT = 5; - public static final int TEST_DEL_ACCOUNT = 6; - - public static final String LOCAL_NOTIFICATION_TITLE = "localtest"; - public static final String TEST_TAG_NAME = "DiagnosisTag"; -} diff --git a/app/src/main/java/com/fuying/sn/push/tpush/MessageReceiver.java b/app/src/main/java/com/fuying/sn/push/tpush/MessageReceiver.java deleted file mode 100644 index d3aa2ed..0000000 --- a/app/src/main/java/com/fuying/sn/push/tpush/MessageReceiver.java +++ /dev/null @@ -1,297 +0,0 @@ -package com.fuying.sn.push.tpush; - -import android.content.Context; -import android.content.Intent; -import android.text.TextUtils; -import android.util.Log; -import android.widget.Toast; - -import com.fuying.sn.push.tpush.common.NotificationService; -import com.fuying.sn.push.tpush.po.XGNotification; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.tencent.android.tpush.NotificationAction; -import com.tencent.android.tpush.XGPushBaseReceiver; -import com.tencent.android.tpush.XGPushClickedResult; -import com.tencent.android.tpush.XGPushRegisterResult; -import com.tencent.android.tpush.XGPushShowedResult; -import com.tencent.android.tpush.XGPushTextMessage; - -import java.text.SimpleDateFormat; -import java.util.Calendar; - -public class MessageReceiver extends XGPushBaseReceiver { - public static final String UPDATE_LISTVIEW_ACTION = "com.qq.xgdemo.activity.UPDATE_LISTVIEW"; - public static final String TEST_ACTION = "com.qq.xgdemo.activity.TEST_ACTION"; - public static final String LogTag = "xg.test"; - - private static final String TAG = "MessageReceiver"; - - /** - * 消息透传处理 - * - * @param context - * @param message 解析自定义的 JSON - */ - @Override - public void onTextMessage(Context context, XGPushTextMessage message) { - - String text = "收到消息:" + message.toString(); - // 获取自定义key-value - String customContent = message.getCustomContent(); - if (customContent != null && customContent.length() != 0) { - JsonObject obj = JsonParser.parseString(customContent).getAsJsonObject(); - // key1为前台配置的key - if (!TextUtils.isEmpty(obj.get("key").getAsString())) { - String value = obj.get("key").getAsString(); - Log.d(LogTag, "get custom value:" + value); - } - // ... - } - // APP自主处理消息的过程... - Log.e(LogTag, text); - show(context, text); - processCustomMessage(context, message); - } - - /** - * 通知展示 - * - * @param context - * @param notifiShowedRlt 包含通知的内容 - */ - @Override - public void onNotificationShowedResult(Context context, XGPushShowedResult notifiShowedRlt) { - if (context == null || notifiShowedRlt == null) { - return; - } - XGNotification notific = new XGNotification(); - notific.setMsg_id(notifiShowedRlt.getMsgId()); - notific.setTitle(notifiShowedRlt.getTitle()); - notific.setContent(notifiShowedRlt.getContent()); - // notificationActionType==1为Activity,2为url,3为intent - notific.setNotificationActionType(notifiShowedRlt - .getNotificationActionType()); - // Activity,url,intent都可以通过getActivity()获得 - notific.setActivity(notifiShowedRlt.getActivity()); - notific.setUpdate_time(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") - .format(Calendar.getInstance().getTime())); - NotificationService.getInstance(context).save(notific); - - Intent testIntent = new Intent(TEST_ACTION); - if (notifiShowedRlt.getTitle().equals(Constants.LOCAL_NOTIFICATION_TITLE)) { - testIntent.putExtra("step", Constants.TEST_LOCAL_NOTIFICATION); - } else { - testIntent.putExtra("step", Constants.TEST_NOTIFICATION); - } - context.sendBroadcast(testIntent); - - Intent viewIntent = new Intent(UPDATE_LISTVIEW_ACTION); - context.sendBroadcast(viewIntent); - show(context, "您有1条新消息, " + "通知被展示 , " + notifiShowedRlt.toString()); - Log.d(LogTag, "您有1条新消息, " + "通知被展示 , " + notifiShowedRlt.toString() + ", PushChannel:" + notifiShowedRlt.getPushChannel()); - } - - /** - * 注册回调 - * - * @param context - * @param errorCode 0 为成功,其它为错误码 - */ - @Override - public void onRegisterResult(Context context, int errorCode, XGPushRegisterResult message) { - if (context == null || message == null) { - return; - } - String text = ""; - if (errorCode == XGPushBaseReceiver.SUCCESS) { - // 在这里拿token - String token = message.getToken(); - text = "注册成功1. token:" + token; - } else { - text = message + "注册失败,错误码:" + errorCode; - } - Log.d(LogTag, text); - show(context, text); - } - - /** - * 反注册回调 - * - * @param context - * @param errorCode 0 为成功,其它为错误码 - */ - @Override - public void onUnregisterResult(Context context, int errorCode) { - if (context == null) { - return; - } - String text = ""; - if (errorCode == XGPushBaseReceiver.SUCCESS) { - text = "反注册成功"; - } else { - text = "反注册失败" + errorCode; - } - Log.d(LogTag, text); - show(context, text); - - } - - /** - * 设置标签回调 - * - * @param context - * @param errorCode 0 为成功,其它为错误码 - * @param tagName 设置的 TAG - */ - @Override - public void onSetTagResult(Context context, int errorCode, String tagName) { - if (context == null) { - return; - } - String text = ""; - if (errorCode == XGPushBaseReceiver.SUCCESS) { - text = "\"" + tagName + "\"设置成功"; - } else { - text = "\"" + tagName + "\"设置失败,错误码:" + errorCode; - } - Log.d(LogTag, text); - show(context, text); - - Intent testIntent = new Intent(TEST_ACTION); - testIntent.putExtra("step", Constants.TEST_SET_TAG); - context.sendBroadcast(testIntent); - } - - /** - * 删除标签的回调 - * - * @param context - * @param errorCode 0 为成功,其它为错误码 - * @param tagName 设置的 TAG - */ - @Override - public void onDeleteTagResult(Context context, int errorCode, String tagName) { - if (context == null) { - return; - } - String text = ""; - if (errorCode == XGPushBaseReceiver.SUCCESS) { - text = "\"" + tagName + "\"删除成功"; - } else { - text = "\"" + tagName + "\"删除失败,错误码:" + errorCode; - } - Log.d(LogTag, text); - show(context, text); - - Intent testIntent = new Intent(TEST_ACTION); - testIntent.putExtra("step", Constants.TEST_DEL_TAG); - context.sendBroadcast(testIntent); - } - - /** - * 设置账号回调 - * - * @param context - * @param errorCode 0 为成功,其它为错误码 - * @param account 设置的账号 - */ - @Override - public void onSetAccountResult(Context context, int errorCode, String account) { - Intent testIntent = new Intent(TEST_ACTION); - testIntent.putExtra("step", Constants.TEST_SET_ACCOUNT); - context.sendBroadcast(testIntent); - } - - - /** - * 删除账号回调 - * - * @param context - * @param errorCode 0 为成功,其它为错误码 - * @param account 设置的账号 - */ - @Override - public void onDeleteAccountResult(Context context, int errorCode, String account) { - Intent testIntent = new Intent(TEST_ACTION); - testIntent.putExtra("step", Constants.TEST_DEL_ACCOUNT); - context.sendBroadcast(testIntent); - } - - @Override - public void onSetAttributeResult(Context context, int i, String s) { - - } - - @Override - public void onDeleteAttributeResult(Context context, int i, String s) { - - } - - @Override - public void onQueryTagsResult(Context context, int errorCode, String data, String operateName) { - Log.i(LogTag, "action - onQueryTagsResult, errorCode:" + errorCode + ", operateName:" + operateName + ", data: " + data); - } - - /** - * 通知点击回调 actionType=1为该消息被清除,actionType=0为该消息被点击 - * - * @param context - * @param message 包含被点击通知的内容 - */ - @Override - public void onNotificationClickedResult(Context context, XGPushClickedResult message) { - if (context == null || message == null) { - return; - } - String text = ""; - if (message.getActionType() == NotificationAction.clicked.getType()) { - // 通知在通知栏被点击啦。。。。。 - // APP自己处理点击的相关动作 - // 这个动作可以在activity的onResume也能监听,请看第3点相关内容 - text = "通知被打开 :" + message; - } else if (message.getActionType() == NotificationAction.delete.getType()) { - // 通知被清除啦。。。。 - // APP自己处理通知被清除后的相关动作 - text = "通知被清除 :" + message; - } - Toast.makeText(context, "广播接收到通知被点击:" + message.toString(), - Toast.LENGTH_SHORT).show(); - // 获取自定义key-value - String customContent = message.getCustomContent(); - if (customContent != null && customContent.length() != 0) { - JsonObject obj = JsonParser.parseString(customContent).getAsJsonObject(); - // key1为前台配置的key - if (!TextUtils.isEmpty(obj.get("key").getAsString())) { - String value = obj.get("key").getAsString(); - Log.d(LogTag, "get custom value:" + value); - } - // ... - } - // APP自主处理的过程。。。 - Log.d(LogTag, text); - show(context, text); - } - - private void show(Context context, String text) { -// Toast.makeText(context, text, Toast.LENGTH_SHORT).show(); - } - - private void processCustomMessage(Context context, XGPushTextMessage message) { - if (context == null || message == null) { - return; - } - - String title = message.getTitle(); - String content = message.getContent(); - JsonObject extrasJson = JsonParser.parseString(content).getAsJsonObject(); - - String extras = ""; - if (extrasJson.get("extras") != null) { - extras = extrasJson.get("extras").toString(); - } - -// PushManager.getInstance().setPushContent(title, extras); - } - -} diff --git a/app/src/main/java/com/fuying/sn/push/tpush/common/DBOpenHelper.java b/app/src/main/java/com/fuying/sn/push/tpush/common/DBOpenHelper.java deleted file mode 100644 index 1504e27..0000000 --- a/app/src/main/java/com/fuying/sn/push/tpush/common/DBOpenHelper.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.fuying.sn.push.tpush.common; - -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; - -public class DBOpenHelper extends SQLiteOpenHelper { - - public DBOpenHelper(Context context) { - super(context, "XGExample.db", null, 1); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE notification (id integer primary key autoincrement,msg_id varchar(64),title varchar(128),activity varchar(256),notificationActionType varchar(512),content text,update_time varchar(16))"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - - } - -} diff --git a/app/src/main/java/com/fuying/sn/push/tpush/common/NotificationService.java b/app/src/main/java/com/fuying/sn/push/tpush/common/NotificationService.java deleted file mode 100644 index 4cda4dc..0000000 --- a/app/src/main/java/com/fuying/sn/push/tpush/common/NotificationService.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.fuying.sn.push.tpush.common; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; - -import com.fuying.sn.push.tpush.po.XGNotification; - -import java.util.ArrayList; -import java.util.List; - -public class NotificationService { - private DBOpenHelper dbOpenHelper; - private static NotificationService instance = null; - - public NotificationService(Context context) { - this.dbOpenHelper = new DBOpenHelper(context); - } - - public synchronized static NotificationService getInstance(Context ctx) { - if (null == instance) { - instance = new NotificationService(ctx); - } - return instance; - } - - public void save(XGNotification notification) { - SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); - ContentValues values = new ContentValues(); - values.put("msg_id", notification.getMsg_id()); - values.put("title", notification.getTitle()); - values.put("content", notification.getContent()); - values.put("activity", notification.getActivity()); - values.put("notificationActionType", notification.getNotificationActionType()); - values.put("update_time", notification.getUpdate_time()); - db.insert("notification", null, values); - } - - public void delete(Integer id) { - SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); - db.delete("notification", "id=?", new String[] { id.toString() }); - } - - public void deleteAll() { - SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); - db.delete("notification", "", null); - } - - public void update(XGNotification notification) { - SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); - ContentValues values = new ContentValues(); - values.put("msg_id", notification.getMsg_id()); - values.put("title", notification.getTitle()); - values.put("content", notification.getContent()); - values.put("activity", notification.getActivity()); - values.put("notificationActionType", notification.getNotificationActionType()); - values.put("update_time", notification.getUpdate_time()); - db.update("notification", values, "id=?", new String[] { notification - .getId().toString() }); - } - - public XGNotification find(Integer id) { - SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); - Cursor cursor = db - .query("notification", - new String[] { "id,msg_id,title,content,activity,notificationActionType,update_time" }, - "id=?", new String[] { id.toString() }, null, null, - null, "1"); - try { - if (cursor.moveToFirst()) { - return new XGNotification(cursor.getInt(cursor - .getColumnIndex("id")), cursor.getLong(cursor - .getColumnIndex("msg_id")), cursor.getString(cursor - .getColumnIndex("title")), cursor.getString(cursor - .getColumnIndex("content")), cursor.getString(cursor - .getColumnIndex("activity")), cursor.getInt(cursor - .getColumnIndex("notificationActionType")), cursor.getString(cursor - .getColumnIndex("update_time"))); - } - return null; - } finally { - cursor.close(); - } - } - - public List getScrollData(int currentPage, int lineSize, - String msg_id) { - String firstResult = String.valueOf((currentPage - 1) * lineSize); - SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); - Cursor cursor = null; - try { - if (msg_id == null || "".equals(msg_id)) { - cursor = db - .query("notification", - new String[] { "id,msg_id,title,content,activity,notificationActionType,update_time" }, - null, null, null, null, "update_time DESC", - firstResult + "," + lineSize); - } else { - cursor = db - .query("notification", - new String[] { "id,msg_id,title,content,activity,notificationActionType,update_time" }, - "msg_id like ?", new String[] { msg_id + "%" }, - null, null, "update_time DESC", firstResult - + "," + lineSize); - } - List notifications = new ArrayList(); - while (cursor.moveToNext()) { - notifications.add(new XGNotification(cursor.getInt(cursor - .getColumnIndex("id")), cursor.getLong(cursor - .getColumnIndex("msg_id")), cursor.getString(cursor - .getColumnIndex("title")), cursor.getString(cursor - .getColumnIndex("content")), cursor.getString(cursor - .getColumnIndex("activity")), cursor.getInt(cursor - .getColumnIndex("notificationActionType")), cursor.getString(cursor - .getColumnIndex("update_time")))); - } - return notifications; - } finally { - cursor.close(); - } - } - - public int getCount() { - SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); - Cursor cursor = db.rawQuery("select count(*) from notification", null); - try { - cursor.moveToFirst(); - return cursor.getInt(0); - } finally { - cursor.close(); - } - } -} diff --git a/app/src/main/java/com/fuying/sn/push/tpush/po/XGNotification.java b/app/src/main/java/com/fuying/sn/push/tpush/po/XGNotification.java deleted file mode 100644 index 438d06b..0000000 --- a/app/src/main/java/com/fuying/sn/push/tpush/po/XGNotification.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.fuying.sn.push.tpush.po; - -public class XGNotification { - private Integer id; - private Long msg_id; - private String title; - private String content; - private String activity; - private int notificationActionType; - private String update_time; - - public XGNotification() { - - } - - public XGNotification(Integer id, Long msg_id, String title, - String content, String activity, int notificationActionType, String update_time) { - super(); - this.id = id; - this.msg_id = msg_id; - this.title = title; - this.content = content; - this.activity = activity; - this.notificationActionType = notificationActionType; - this.update_time = update_time; - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public Long getMsg_id() { - return msg_id; - } - - public void setMsg_id(Long msg_id) { - this.msg_id = msg_id; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getUpdate_time() { - return update_time; - } - - public void setUpdate_time(String update_time) { - this.update_time = update_time; - } - - public String getActivity() { - return activity; - } - - public void setActivity(String activity) { - this.activity = activity; - } - - public int getNotificationActionType() { - return notificationActionType; - } - - public void setNotificationActionType(int notificationActionType) { - this.notificationActionType = notificationActionType; - } -} diff --git a/app/src/main/java/com/fuying/sn/receiver/APKinstallReceiver.java b/app/src/main/java/com/fuying/sn/receiver/APKinstallReceiver.java index 35776b8..af1e6b0 100644 --- a/app/src/main/java/com/fuying/sn/receiver/APKinstallReceiver.java +++ b/app/src/main/java/com/fuying/sn/receiver/APKinstallReceiver.java @@ -11,7 +11,7 @@ import com.fuying.sn.config.CommonConfig; import com.fuying.sn.manager.ControlManager; import com.fuying.sn.network.NetInterfaceManager; import com.fuying.sn.utils.CacheUtils; -import com.fuying.sn.utils.JGYUtils; +import com.fuying.sn.utils.JgyUtils; import com.fuying.sn.utils.SPUtils; import java.util.concurrent.TimeUnit; @@ -38,7 +38,7 @@ public class APKinstallReceiver extends BroadcastReceiver { // an Intent broadcast. String action = intent.getAction(); // ApkUtils.addShortcut(context); - JGYUtils.getInstance().cleanLauncherCache(); + JgyUtils.getInstance().cleanLauncherCache(); ControlManager.getInstance().setDefaultApp(); Log.e(TAG, "onReceive: " + "action = " + action); String state; @@ -50,8 +50,8 @@ public class APKinstallReceiver extends BroadcastReceiver { switch (action) { case Intent.ACTION_PACKAGE_ADDED: state = "安装了:"; - JGYUtils.RemoveTask(context, packageName); - JGYUtils.getInstance().putBuiltInApps(); + JgyUtils.RemoveTask(context, packageName); + JgyUtils.getInstance().putBuiltInApps(); break; case Intent.ACTION_PACKAGE_REPLACED: cleanLauncher3Cache(); @@ -66,10 +66,10 @@ public class APKinstallReceiver extends BroadcastReceiver { } Log.e(TAG, "sendAppInfo: " + state + packageName); newAppListener.setNewAppListener(packageName); - if (JGYUtils.PACKAGE_APPSTORE.equals(packageName)) { + if (JgyUtils.PACKAGE_APPSTORE.equals(packageName)) { //启动应用市场 // JGYUtils.getInstance().wakeUpDeviceInfo(); - } else if (JGYUtils.PACKAGE_UPDATETOOLS.equals(packageName)) { + } else if (JgyUtils.PACKAGE_UPDATETOOLS.equals(packageName)) { //启动升级组件 // JGYUtils.getInstance().wakeUpUpdateTools(); } diff --git a/app/src/main/java/com/fuying/sn/receiver/BootReceiver.java b/app/src/main/java/com/fuying/sn/receiver/BootReceiver.java index 41e34ed..0363aa6 100644 --- a/app/src/main/java/com/fuying/sn/receiver/BootReceiver.java +++ b/app/src/main/java/com/fuying/sn/receiver/BootReceiver.java @@ -5,18 +5,13 @@ import android.content.Context; import android.content.Intent; import android.util.Log; -import com.baidu.location.BDAbstractLocationListener; -import com.baidu.location.BDLocation; -import com.baidu.location.LocationClient; import com.fuying.sn.config.CommonConfig; -import com.fuying.sn.manager.AmapManager; -import com.fuying.sn.network.NetInterfaceManager; import com.fuying.sn.service.ControlPanelService; -import com.fuying.sn.service.GuardService; -import com.fuying.sn.service.main.MainService; -import com.fuying.sn.service.ManagerService; import com.fuying.sn.service.DownloadService; +import com.fuying.sn.service.GuardService; +import com.fuying.sn.service.ManagerService; import com.fuying.sn.service.StepService; +import com.fuying.sn.service.main.MainService; import com.tencent.mmkv.MMKV; diff --git a/app/src/main/java/com/fuying/sn/service/DownloadService.java b/app/src/main/java/com/fuying/sn/service/DownloadService.java index 8e2a92b..ac6ed01 100644 --- a/app/src/main/java/com/fuying/sn/service/DownloadService.java +++ b/app/src/main/java/com/fuying/sn/service/DownloadService.java @@ -17,12 +17,12 @@ import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.task.DownloadTask; import com.blankj.utilcode.util.ToastUtils; -import com.google.gson.JsonObject; import com.fuying.sn.KeepAliveConnection; import com.fuying.sn.R; +import com.fuying.sn.gson.GsonUtils; import com.fuying.sn.service.main.MainService; import com.fuying.sn.utils.ApkUtils; -import com.fuying.sn.gson.GsonUtils; +import com.google.gson.JsonObject; import java.io.File; @@ -30,6 +30,15 @@ import java.io.File; public class DownloadService extends Service { private static final String TAG = "DownloadService"; + @Override + public void onCreate() { + super.onCreate(); + Log.e(TAG, "onCreate: "); + Aria.download(this).register(); + //恢复所有未完成的下载任务 + Aria.download(this).resumeAllTask(); + } + @Override public int onStartCommand(Intent intent, int flags, int startId) { startService(new Intent(this, StepService.class)); @@ -37,21 +46,14 @@ public class DownloadService extends Service { startService(new Intent(this, MainService.class)); startService(new Intent(this, ManagerService.class)); startService(new Intent(this, ControlPanelService.class)); - Aria.download(this).register(); - //恢复所有未完成的下载任务 - Aria.download(this).resumeAllTask(); return START_STICKY; } - - @Override - public void onCreate() { - super.onCreate(); - } - @Override public void onDestroy() { super.onDestroy(); + Log.e(TAG, "onDestroy: "); + Aria.download(this).unRegister(); } @Nullable @@ -156,6 +158,7 @@ public class DownloadService extends Service { } Log.e(TAG, "taskFail: " + packageName + "filepath: " + filepath); Aria.download(this).load(task.getDownloadEntity().getId()).cancel(true); + // Aria.download(this) // .load(task.getDownloadEntity().getRealUrl()) //读取下载地址 // .setFilePath(task.getFilePath()) diff --git a/app/src/main/java/com/fuying/sn/service/RemoteService.java b/app/src/main/java/com/fuying/sn/service/RemoteService.java index 94f9e59..e761b02 100644 --- a/app/src/main/java/com/fuying/sn/service/RemoteService.java +++ b/app/src/main/java/com/fuying/sn/service/RemoteService.java @@ -11,14 +11,6 @@ import com.fuying.sn.BuildConfig; import com.fuying.sn.IGetInfoInterface; import com.fuying.sn.config.CommonConfig; import com.fuying.sn.desktop.RunningAppManager; -import com.fuying.sn.network.NetInterfaceManager; -import com.fuying.sn.utils.Utils; -import com.tencent.android.tpush.XGIOperateCallback; -import com.tencent.android.tpush.XGPushConfig; -import com.tencent.android.tpush.XGPushManager; - -import java.util.ArrayList; -import java.util.List; public class RemoteService extends Service { private static final String TAG = "RemoteService"; @@ -36,38 +28,6 @@ public class RemoteService extends Service { public void onCreate() { super.onCreate(); Log.e(TAG, "onCreate: "); - tPushInit(); - } - - private void tPushInit() { - XGPushConfig.enableDebug(this, true); - XGPushManager.registerPush(this, new XGIOperateCallback() { - @Override - public void onSuccess(Object data, int flag) { - //token在设备卸载重装的时候有可能会变 - Log.e("TPush", "注册成功,设备token为:" + data); - List accountInfoList = new ArrayList<>(); - Log.e("TPush", "onSuccess: " + Utils.getSerial()); - accountInfoList.add(new XGPushManager.AccountInfo(XGPushManager.AccountType.CUSTOM.getValue(), Utils.getSerial())); - XGPushManager.upsertAccounts(RemoteService.this, accountInfoList, new XGIOperateCallback() { - @Override - public void onSuccess(Object data, int flag) { - Log.e("TPush", "upsertAccounts onSuccess, data:" + data + ", flag:" + flag); - NetInterfaceManager.getInstance().setPushTags(); - } - - @Override - public void onFail(Object data, int errCode, String msg) { - Log.e("TPush", "upsertAccounts onFail, data:" + data + ", code:" + errCode + ", msg:" + msg); - } - }); - } - - @Override - public void onFail(Object data, int errCode, String msg) { - Log.e("TPush", "注册失败,错误码:" + errCode + ",错误信息:" + msg); - } - }); } @Override diff --git a/app/src/main/java/com/fuying/sn/service/StepService.java b/app/src/main/java/com/fuying/sn/service/StepService.java index b52d7ab..4d9afa5 100644 --- a/app/src/main/java/com/fuying/sn/service/StepService.java +++ b/app/src/main/java/com/fuying/sn/service/StepService.java @@ -17,16 +17,14 @@ import android.content.ServiceConnection; import android.os.Binder; import android.os.Handler; import android.os.IBinder; -import android.os.PowerManager; import android.text.TextUtils; import android.util.Log; import androidx.annotation.Nullable; import com.blankj.utilcode.util.NetworkUtils; -import com.fuying.sn.BuildConfig; import com.fuying.sn.KeepAliveConnection; -import com.fuying.sn.utils.JGYUtils; +import com.fuying.sn.utils.JgyUtils; import com.fuying.sn.utils.ServiceAliveUtils; import com.fuying.sn.utils.Utils; import com.fuying.sn.websocket.JWebSocketClient; @@ -196,7 +194,7 @@ public class StepService extends Service implements NetworkUtils.OnNetworkStatus Log.i("JWebSocketClientService", "websocket连接关闭:" + reason); // client.close(); // initSocketClient(); - if (JGYUtils.getInstance().isScreenOn()) { + if (JgyUtils.getInstance().isScreenOn()) { mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);//开启心跳检测 } } @@ -207,7 +205,7 @@ public class StepService extends Service implements NetworkUtils.OnNetworkStatus Log.i("JWebSocketClientService", "websocket连接错误:" + ex.getMessage()); // client.close(); // initSocketClient(); - if (JGYUtils.getInstance().isScreenOn()) { + if (JgyUtils.getInstance().isScreenOn()) { mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);//开启心跳检测 } else { Log.i("JWebSocketClientService", "postDelayed off"); @@ -302,7 +300,7 @@ public class StepService extends Service implements NetworkUtils.OnNetworkStatus sendMsg(); } else if (client.isClosed()) { Log.i("JWebSocketClientService", "websocket重连中"); - if (JGYUtils.getInstance().isScreenOn()) { + if (JgyUtils.getInstance().isScreenOn()) { reconnectWs(); } else { Log.i("JWebSocketClientService", "reconnectWs off"); @@ -314,7 +312,7 @@ public class StepService extends Service implements NetworkUtils.OnNetworkStatus initSocketClient(); } //每隔一定的时间,对长连接进行一次心跳检测 - if (JGYUtils.getInstance().isScreenOn()) { + if (JgyUtils.getInstance().isScreenOn()) { mHandler.postDelayed(this, HEART_BEAT_RATE); } else { Log.i("JWebSocketClientService", "websocket息屏不重连"); diff --git a/app/src/main/java/com/fuying/sn/service/main/MainSPresenter.java b/app/src/main/java/com/fuying/sn/service/main/MainSPresenter.java index b37582b..dd61747 100644 --- a/app/src/main/java/com/fuying/sn/service/main/MainSPresenter.java +++ b/app/src/main/java/com/fuying/sn/service/main/MainSPresenter.java @@ -15,7 +15,7 @@ import com.fuying.sn.disklrucache.CacheHelper; import com.fuying.sn.gson.GsonUtils; import com.fuying.sn.network.NetInterfaceManager; import com.fuying.sn.service.ManagerService; -import com.fuying.sn.utils.JGYUtils; +import com.fuying.sn.utils.JgyUtils; import com.fuying.sn.utils.SPUtils; import com.fuying.sn.utils.ServiceAliveUtils; import com.fuying.sn.utils.Utils; @@ -177,7 +177,7 @@ public class MainSPresenter implements MainSContact.Presenter { JsonObject jsonObject = GsonUtils.getJsonObject(jsonString); String ids = jsonObject.get("ids").getAsString(); String packages = jsonObject.get("package").getAsString(); - JGYUtils.getInstance().writeDeselectIDtoSystem(ids, packages); + JgyUtils.getInstance().writeDeselectIDtoSystem(ids, packages); } else { Log.e("getAppInside", "onNext: " + baseResponse.msg); Settings.System.putString(mContext.getContentResolver(), CommonConfig.AOLE_ACTION_APP_FORBID_ID, " "); diff --git a/app/src/main/java/com/fuying/sn/service/main/MainService.java b/app/src/main/java/com/fuying/sn/service/main/MainService.java index 603e7dc..3ae7dcb 100644 --- a/app/src/main/java/com/fuying/sn/service/main/MainService.java +++ b/app/src/main/java/com/fuying/sn/service/main/MainService.java @@ -19,9 +19,6 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.Log; -import com.baidu.location.BDAbstractLocationListener; -import com.baidu.location.BDLocation; -import com.baidu.location.LocationClient; import com.blankj.utilcode.util.NetworkUtils; import com.fuying.sn.BuildConfig; import com.fuying.sn.base.rx.BaseRxService; @@ -39,22 +36,17 @@ import com.fuying.sn.service.GuardService; import com.fuying.sn.service.StepService; import com.fuying.sn.utils.ApkUtils; import com.fuying.sn.utils.CacheUtils; -import com.fuying.sn.utils.JGYUtils; +import com.fuying.sn.utils.JgyUtils; import com.fuying.sn.utils.NetStateUtils; import com.fuying.sn.utils.SPUtils; import com.fuying.sn.utils.TimeUtils; import com.fuying.sn.utils.ToastUtil; import com.fuying.sn.utils.Utils; -import com.tencent.android.tpush.XGIOperateCallback; -import com.tencent.android.tpush.XGPushConfig; -import com.tencent.android.tpush.XGPushManager; import com.tencent.mmkv.MMKV; import com.trello.rxlifecycle4.LifecycleProvider; import com.trello.rxlifecycle4.android.ActivityEvent; import java.io.File; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.TimeUnit; import io.reactivex.rxjava3.core.Observable; @@ -99,7 +91,7 @@ public class MainService extends BaseRxService implements MainSContact.MainView, @Override public void run() { // TODO: 2025/11/11 不确定某些机型状态获取是否正常 - if (JGYUtils.getInstance().isScreenOn()) { + if (JgyUtils.getInstance().isScreenOn()) { Log.e(TAG, "getSnInfo1"); NetInterfaceManager.getInstance().getAppWhiteList(() -> { Log.e("onConnected", "onNext: getAppWhiteList"); @@ -109,7 +101,6 @@ public class MainService extends BaseRxService implements MainSContact.MainView, NetInterfaceManager.getInstance().updateAdminInfo(); NetInterfaceManager.getInstance().SendAppInstallInfo(); SPUtils.put(MainService.this, CommonConfig.JGY_FIRST_BOOT, 1); - tPushInit(); } } }, 15000); @@ -154,7 +145,7 @@ public class MainService extends BaseRxService implements MainSContact.MainView, boolean reboot = mMMKV.decodeBool(CommonConfig.DEVICES_REBOOT, false); Log.e(TAG, "onCreate: reboot = " + reboot); - boolean networkConnected = JGYUtils.getInstance().isNetworkConnected(); + boolean networkConnected = JgyUtils.getInstance().isNetworkConnected(); Log.e(TAG, "onCreate: networkConnected = " + networkConnected); // if (networkConnected && reboot) { Log.e(TAG, "onCreate run: 20秒后请求网络"); @@ -205,7 +196,7 @@ public class MainService extends BaseRxService implements MainSContact.MainView, startService(); Settings.System.putString(getContentResolver(), CommonConfig.APP_SOURCE_WHITE_LIST, DEFAULT_SOURCE); - JGYUtils.getInstance().putBuiltInApps(); + JgyUtils.getInstance().putBuiltInApps(); } private static final String DEFAULT_SOURCE = "com.fuying.sn,com.fuying.appstore,com.fuying.fuxiaoying"; @@ -220,7 +211,7 @@ public class MainService extends BaseRxService implements MainSContact.MainView, ApkUtils.writeAppPackageList(MainService.this, aole_app_forbid); new CacheUtils().cleanApplicationUserData(this, "com.aoleyun.os"); - File file = new File(JGYUtils.getInstance().getDownLoadPath()); + File file = new File(JgyUtils.getInstance().getDownLoadPath()); Log.e(TAG, "versionIsUpdate: " + file.getAbsolutePath()); String[] fileList = file.list(); if (fileList != null) { @@ -247,18 +238,8 @@ public class MainService extends BaseRxService implements MainSContact.MainView, } private void initConfig() { - tPushInit(); // Utils.getPublicIP(MainService.this); - LocationClient locationClient = AmapManager.getInstance().getLocationClient(); - locationClient.stop(); - locationClient.start(); - locationClient.registerLocationListener(new BDAbstractLocationListener() { - @Override - public void onReceiveLocation(BDLocation bdLocation) { - Log.e("initConfig", "onReceiveLocation: "); - locationClient.stop(); - } - }); + AmapManager.getInstance().startLocation(); } private interface ConfigStart { @@ -324,40 +305,9 @@ public class MainService extends BaseRxService implements MainSContact.MainView, // am.setTime(aLong); SystemClock.setCurrentTimeMillis(aLong); Log.e(TAG, "getTimeFromNtpServer: " + aLong); - tPushInit(); } } - private void tPushInit() { - XGPushConfig.enableDebug(this, true); - XGPushManager.registerPush(this, new XGIOperateCallback() { - @Override - public void onSuccess(Object data, int flag) { - //token在设备卸载重装的时候有可能会变 - Log.e("TPush", "注册成功,设备token为:" + data); - List accountInfoList = new ArrayList<>(); - Log.e("TPush", "onSuccess: " + Utils.getSerial()); - accountInfoList.add(new XGPushManager.AccountInfo(XGPushManager.AccountType.CUSTOM.getValue(), Utils.getSerial())); - XGPushManager.upsertAccounts(MainService.this, accountInfoList, new XGIOperateCallback() { - @Override - public void onSuccess(Object data, int flag) { - Log.e("TPush", "upsertAccounts onSuccess, data:" + data + ", flag:" + flag); - NetInterfaceManager.getInstance().setPushTags(); - } - - @Override - public void onFail(Object data, int errCode, String msg) { - Log.e("TPush", "upsertAccounts onFail, data:" + data + ", code:" + errCode + ", msg:" + msg); - } - }); - } - - @Override - public void onFail(Object data, int errCode, String msg) { - Log.e("TPush", "注册失败,错误码:" + errCode + ",错误信息:" + msg); - } - }); - } private void registerReceivers() { registerWiFiReceiver(); diff --git a/app/src/main/java/com/fuying/sn/utils/ApkUtils.java b/app/src/main/java/com/fuying/sn/utils/ApkUtils.java index 47f419e..8847bb3 100644 --- a/app/src/main/java/com/fuying/sn/utils/ApkUtils.java +++ b/app/src/main/java/com/fuying/sn/utils/ApkUtils.java @@ -25,10 +25,11 @@ import android.view.View; import androidx.annotation.RequiresApi; import androidx.core.content.FileProvider; - import com.fuying.sn.BuildConfig; import com.fuying.sn.R; +import com.fuying.sn.bean.AppInfo; import com.fuying.sn.config.CommonConfig; +import com.fuying.sn.gson.GsonUtils; import com.fuying.sn.receiver.InstallResultReceiver; import java.io.BufferedReader; @@ -97,11 +98,7 @@ public class ApkUtils { os.writeBytes("exit\n"); os.flush(); int exitValue = process.waitFor(); - if (exitValue == 0) { - return true; - } else { - return false; - } + return exitValue == 0; } catch (Exception e) { Log.e("*** DEBUG ***", "Unexpected error - Here is what I know: " + e.getMessage()); @@ -419,11 +416,11 @@ public class ApkUtils { Method method; try { activityTherad = Class.forName("android.app.ActivityThread"); - Class paramTypes[] = getParamTypes(activityTherad, "getPackageManager"); + Class[] paramTypes = getParamTypes(activityTherad, "getPackageManager"); method = activityTherad.getMethod("getPackageManager", paramTypes); Object PackageManagerService = method.invoke(activityTherad); pmService = PackageManagerService.getClass(); - Class paramTypes1[] = getParamTypes(pmService, "installPackageAsUser"); + Class[] paramTypes1 = getParamTypes(pmService, "installPackageAsUser"); method = pmService.getMethod("installPackageAsUser", paramTypes1); method.invoke(PackageManagerService, installPath, null, 0x00000040, packageName, getUserId(Binder.getCallingUid()));//getUserId } catch (ClassNotFoundException e) { @@ -629,11 +626,11 @@ public class ApkUtils { Method method; try { activityTherad = Class.forName("android.app.ActivityThread"); - Class paramTypes[] = getParamTypes(activityTherad, "getPackageManager"); + Class[] paramTypes = getParamTypes(activityTherad, "getPackageManager"); method = activityTherad.getMethod("getPackageManager", paramTypes); Object PackageManagerService = method.invoke(activityTherad); pmService = PackageManagerService.getClass(); - Class paramTypes1[] = getParamTypes(pmService, "deletePackageAsUser"); + Class[] paramTypes1 = getParamTypes(pmService, "deletePackageAsUser"); method = pmService.getMethod("deletePackageAsUser", paramTypes1); //getUserId method.invoke(PackageManagerService, packageName, null, getUserId(Binder.getCallingUid()), 0x00000040); @@ -649,7 +646,7 @@ public class ApkUtils { } private static Class[] getParamTypes(Class cls, String mName) { - Class cs[] = null; + Class[] cs = null; Method[] mtd = cls.getMethods(); for (int i = 0; i < mtd.length; i++) { if (!mtd[i].getName().equals(mName)) { @@ -858,9 +855,9 @@ public class ApkUtils { Log.e(TAG, "writeAppPackageList: " + result); // addShortcut(context);//开机之后添加图标到桌面 HashSet factoryAppList = new HashSet<>(); - factoryAppList.addAll(JGYUtils.getInstance().getOwnApp()); - factoryAppList.addAll(JGYUtils.getInstance().getFXYApp()); - factoryAppList.addAll(JGYUtils.getInstance().getAppAndWhite()); + factoryAppList.addAll(JgyUtils.getInstance().getOwnApp()); + factoryAppList.addAll(JgyUtils.getInstance().getFXYApp()); + factoryAppList.addAll(JgyUtils.getInstance().getAppAndWhite()); Log.e("writeAppPackageList", "factoryAppList: " + factoryAppList); if (!TextUtils.isEmpty(result)) { HashSet writeAppSet = new HashSet<>(Arrays.asList(result.split(","))); @@ -950,11 +947,7 @@ public class ApkUtils { for (ResolveInfo info : infos) { hashMap.put(info.activityInfo.packageName, info); } - if (hashMap.get(pkg) == null) { - return false; - } else { - return true; - } + return hashMap.get(pkg) != null; } @@ -982,4 +975,63 @@ public class ApkUtils { return applicationInfos; } + public static boolean isUpdate(Context context, AppInfo appUpdateInfo) { + String packageName = appUpdateInfo.getApp_package(); + long versionCode = appUpdateInfo.getApp_version_code(); + return isUpdate(context, packageName, versionCode); + } + + public static boolean isUpdate(Context context, String packageName, long versionCode) { + PackageInfo packageInfo = null; + try { + packageInfo = context.getPackageManager().getPackageInfo(packageName, 0); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + if (packageInfo == null) { + return true; + } else { + long appVersionCode; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + appVersionCode = packageInfo.getLongVersionCode(); + } else { + appVersionCode = packageInfo.versionCode; + } + if (appVersionCode < versionCode) { + return true; + } else { + Log.e(TAG, "checkUpdate: " + packageName + "\t已经是最新版"); + return false; + } + } + } + + public static void checkAppUpdate(Context context, AppInfo appInfo) { + String packageName = appInfo.getApp_package(); + long versionCode = appInfo.getApp_version_code(); + String url = appInfo.getApp_url(); + PackageInfo packageInfo = null; + try { + packageInfo = context.getPackageManager().getPackageInfo(packageName, 0); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + if (packageInfo == null) { + FileUtils.ariaDownload(context, url, GsonUtils.getJsonObject(GsonUtils.toJSONString(appInfo))); + } else { + long appVersionCode; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + appVersionCode = packageInfo.getLongVersionCode(); + } else { + appVersionCode = packageInfo.versionCode; + } + if (appVersionCode < versionCode) { + FileUtils.ariaDownload(context, url, GsonUtils.getJsonObject(GsonUtils.toJSONString(appInfo))); + } else { + Log.e(TAG, "checkUpdate: " + packageName + "\t已经是最新版"); + } + } + } + + } diff --git a/app/src/main/java/com/fuying/sn/utils/FileUtils.java b/app/src/main/java/com/fuying/sn/utils/FileUtils.java index 8e939f9..d4b17a2 100644 --- a/app/src/main/java/com/fuying/sn/utils/FileUtils.java +++ b/app/src/main/java/com/fuying/sn/utils/FileUtils.java @@ -36,7 +36,7 @@ public class FileUtils { } MessageDigest digest = null; FileInputStream in = null; - byte buffer[] = new byte[1024]; + byte[] buffer = new byte[1024]; int len; try { digest = MessageDigest.getInstance("MD5"); @@ -76,7 +76,7 @@ public class FileUtils { } else { Aria.download(context) .load(url) //读取下载地址 - .setFilePath(JGYUtils.getInstance().getDownLoadPath() + fileName) + .setFilePath(JgyUtils.getInstance().getDownLoadPath() + fileName) .ignoreFilePathOccupy() .setExtendField(jsonObject.toString()) .create(); //启动下载} @@ -85,7 +85,7 @@ public class FileUtils { } else { Aria.download(context) .load(url) //读取下载地址 - .setFilePath(JGYUtils.getInstance().getDownLoadPath() + fileName) + .setFilePath(JgyUtils.getInstance().getDownLoadPath() + fileName) .ignoreFilePathOccupy() .setExtendField(jsonObject.toString()) .create(); //启动下载} diff --git a/app/src/main/java/com/fuying/sn/utils/JGYUtils.java b/app/src/main/java/com/fuying/sn/utils/JgyUtils.java similarity index 97% rename from app/src/main/java/com/fuying/sn/utils/JGYUtils.java rename to app/src/main/java/com/fuying/sn/utils/JgyUtils.java index da79b8b..2447bbc 100644 --- a/app/src/main/java/com/fuying/sn/utils/JGYUtils.java +++ b/app/src/main/java/com/fuying/sn/utils/JgyUtils.java @@ -80,11 +80,11 @@ import io.reactivex.rxjava3.schedulers.Schedulers; import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; -public class JGYUtils { +public class JgyUtils { private static final String TAG = "JGYUtils"; @SuppressLint("StaticFieldLeak") - private static JGYUtils sInstance; + private static JgyUtils sInstance; private Context mContext; private ContentResolver resolver; private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); @@ -232,7 +232,7 @@ public class JGYUtils { } } - private JGYUtils(Context context) { + private JgyUtils(Context context) { if (context == null) { throw new RuntimeException("Context is NULL"); } @@ -242,11 +242,11 @@ public class JGYUtils { public static void init(Context context) { if (sInstance == null) { - sInstance = new JGYUtils(context); + sInstance = new JgyUtils(context); } } - public static JGYUtils getInstance() { + public static JgyUtils getInstance() { if (sInstance == null) { throw new IllegalStateException("You must be init JGYUtils first"); } @@ -259,17 +259,17 @@ public class JGYUtils { public static boolean isOfficialVersion() { - String channelValue = JGYUtils.getInstance().getStringMetaData(); + String channelValue = JgyUtils.getInstance().getStringMetaData(); return "official".equals(channelValue); } public static boolean isNewlyVersion() { - String channelValue = JGYUtils.getInstance().getStringMetaData(); + String channelValue = JgyUtils.getInstance().getStringMetaData(); return "beta".equals(channelValue); } public static boolean isBetaVersion() { - String channelValue = JGYUtils.getInstance().getStringMetaData(); + String channelValue = JgyUtils.getInstance().getStringMetaData(); return "beta".equals(channelValue); } @@ -611,15 +611,15 @@ public class JGYUtils { } public static String getCustomVersion() { - if (JGYUtils.getInstance().checkAppPlatform() == JGYUtils.ZhanruiPlatform - || JGYUtils.getInstance().checkAppPlatform() == JGYUtils.M40sePlatform - || JGYUtils.getInstance().checkAppPlatform() == JGYUtils.T30ProPlatform - || JGYUtils.getInstance().checkAppPlatform() == JGYUtils.MTK8515Platform - || JGYUtils.getInstance().checkAppPlatform() == JGYUtils.G13Platform - || JGYUtils.getInstance().checkAppPlatform() == JGYUtils.iPlay50SEPlatform - || JGYUtils.getInstance().checkAppPlatform() == JGYUtils.G6Platform - || JGYUtils.getInstance().checkAppPlatform() == JGYUtils.G11JPlatform - || JGYUtils.getInstance().checkAppPlatform() == JGYUtils.G12NLPlatform + if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.ZhanruiPlatform + || JgyUtils.getInstance().checkAppPlatform() == JgyUtils.M40sePlatform + || JgyUtils.getInstance().checkAppPlatform() == JgyUtils.T30ProPlatform + || JgyUtils.getInstance().checkAppPlatform() == JgyUtils.MTK8515Platform + || JgyUtils.getInstance().checkAppPlatform() == JgyUtils.G13Platform + || JgyUtils.getInstance().checkAppPlatform() == JgyUtils.iPlay50SEPlatform + || JgyUtils.getInstance().checkAppPlatform() == JgyUtils.G6Platform + || JgyUtils.getInstance().checkAppPlatform() == JgyUtils.G11JPlatform + || JgyUtils.getInstance().checkAppPlatform() == JgyUtils.G12NLPlatform ) { return getProperty("ro.build.display.id", "获取失败"); } else { @@ -628,8 +628,8 @@ public class JGYUtils { } public static String getRomVersion() { - if (JGYUtils.getInstance().checkAppPlatform() == JGYUtils.ZhanruiPlatform - || JGYUtils.getInstance().checkAppPlatform() == JGYUtils.M40sePlatform) { + if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.ZhanruiPlatform + || JgyUtils.getInstance().checkAppPlatform() == JgyUtils.M40sePlatform) { return getProperty("ro.build.id", "获取失败"); } else { return getProperty("ro.build.display.id", "获取失败"); @@ -854,7 +854,7 @@ public class JGYUtils { UserHandle user = Process.myUserHandle(); Log.i("settingssssssstemf", (add ? "Adding" : "Removing") + " package as role holder, role: " + roleName + ", package: " + packageName); - if (JGYUtils.getInstance().checkAppPlatform() != JGYUtils.MTKPlatform) { + if (JgyUtils.getInstance().checkAppPlatform() != JgyUtils.MTKPlatform) { RoleManager roleManager = context.getSystemService(RoleManager.class); Executor executor = context.getMainExecutor(); Consumer callback = successful -> { @@ -953,7 +953,7 @@ public class JGYUtils { Intent intent = new Intent("setDefaultLauncher"); intent.putExtra("package", pkg); intent.putExtra("className", className); - if (JGYUtils.getInstance().checkAppPlatform() == MTKPlatform) { + if (JgyUtils.getInstance().checkAppPlatform() == MTKPlatform) { Log.e(TAG, "setDefaultDesktop: MTK"); //爱华定制 intent.setComponent(new ComponentName("com.android.settings", "com.android.settings.AoleReceiver")); diff --git a/app/src/main/java/com/fuying/sn/utils/Utils.java b/app/src/main/java/com/fuying/sn/utils/Utils.java index f2aed6c..99cdcaa 100644 --- a/app/src/main/java/com/fuying/sn/utils/Utils.java +++ b/app/src/main/java/com/fuying/sn/utils/Utils.java @@ -304,14 +304,14 @@ public class Utils { return weeks[week_index]; } - // 非空判断 - public static boolean isEmpty(String s) { - if (null == s) - return true; - if (s.length() == 0) - return true; - return s.trim().length() == 0; - } +// // 非空判断 +// public static boolean isEmpty(String s) { +// if (null == s) +// return true; +// if (s.length() == 0) +// return true; +// return s.trim().length() == 0; +// } // 手动隐藏键盘 public static void CloseKeyBoard(Context context) { @@ -604,12 +604,12 @@ public class Utils { * @return */ public static String getSerial() { - if (JGYUtils.getInstance().checkAppPlatform() == JGYUtils.G13Platform - || JGYUtils.getInstance().checkAppPlatform() == JGYUtils.iPlay50SEPlatform + if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.G13Platform + || JgyUtils.getInstance().checkAppPlatform() == JgyUtils.iPlay50SEPlatform ) { return getSn(); } else { - return JGYUtils.getInstance().getIMEI(); + return JgyUtils.getInstance().getIMEI(); } } @@ -1102,10 +1102,21 @@ public class Utils { ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); activityManager.getMemoryInfo(memoryInfo); // 当前系统可用内存 ,将获得的内存大小规格化 - + Log.e(TAG, "getAvailMemory: " + memoryInfo.availMem); return memoryInfo.availMem; } + public static long getTotalMem(Context context) { + // 获取android当前可用内存大小 + ActivityManager activityManager = (ActivityManager) context + .getSystemService(Context.ACTIVITY_SERVICE); + ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); + activityManager.getMemoryInfo(memoryInfo); + // 当前系统可用内存 ,将获得的内存大小规格化 + Log.e(TAG, "getTotalMem: " + memoryInfo.totalMem); + return memoryInfo.totalMem; + } + /** * 描述:总内存. * @@ -1199,8 +1210,8 @@ public class Utils { String imei = getIMEI(context); Log.e(TAG, "getMachine: " + imei); String system_version = Build.VERSION.RELEASE; - String firmware_version = JGYUtils.getRomVersion(); - String rom = JGYUtils.getCustomVersion(); + String firmware_version = JgyUtils.getRomVersion(); + String rom = JgyUtils.getCustomVersion(); String screen_rate = getAndroiodScreenProperty(context); JsonObject jsonObject = new JsonObject(); @@ -1237,7 +1248,7 @@ public class Utils { jsonObject.addProperty("memory", memory); jsonObject.addProperty("mac", getMAC(context)); jsonObject.addProperty("storage", storage); - jsonObject.addProperty("is_wifi", JGYUtils.getInstance().isWifiConnect()); + jsonObject.addProperty("is_wifi", JgyUtils.getInstance().isWifiConnect()); jsonObject.addProperty("CPU", CPU + "核"); jsonObject.addProperty("use_space", use_space); jsonObject.addProperty("use_ram", use_ram); diff --git a/app/src/main/res/drawable-hdpi/icon_blue_back.png b/app/src/main/res/drawable-hdpi/icon_blue_back.png new file mode 100644 index 0000000000000000000000000000000000000000..aadbdb97e98056ca5618f8076848984c343761cb GIT binary patch literal 2613 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 Mi|9?L#E0zp5AhsOe*gdg literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-hdpi/icon_close.png b/app/src/main/res/drawable-hdpi/icon_close.png new file mode 100644 index 0000000000000000000000000000000000000000..9ebcb0bdeb422cf2d31ca104d46a721006051d43 GIT binary patch literal 1701 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/bt_normnl.xml b/app/src/main/res/drawable/bt_normnl.xml index 5d03ac7..ff271d5 100644 --- a/app/src/main/res/drawable/bt_normnl.xml +++ b/app/src/main/res/drawable/bt_normnl.xml @@ -2,8 +2,7 @@ - + - + - + diff --git a/app/src/main/res/drawable/dialog_background.xml b/app/src/main/res/drawable/dialog_background.xml new file mode 100644 index 0000000..4937102 --- /dev/null +++ b/app/src/main/res/drawable/dialog_background.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/app/src/main/res/drawable/radio_checked.xml b/app/src/main/res/drawable/radio_checked.xml index 341dbca..c351730 100644 --- a/app/src/main/res/drawable/radio_checked.xml +++ b/app/src/main/res/drawable/radio_checked.xml @@ -2,8 +2,7 @@ - + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/update_cancel_background.xml b/app/src/main/res/drawable/update_cancel_background.xml new file mode 100644 index 0000000..b33e68e --- /dev/null +++ b/app/src/main/res/drawable/update_cancel_background.xml @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml index fed34c1..9437779 100644 --- a/app/src/main/res/layout/activity_home.xml +++ b/app/src/main/res/layout/activity_home.xml @@ -1,7 +1,79 @@ - - - \ No newline at end of file + xmlns:tools="http://schemas.android.com/tools" + tools:context=".activity.home.HomeActivity"> + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_selecte_grade.xml b/app/src/main/res/layout/activity_selecte_grade.xml index 5a96dec..d278a44 100644 --- a/app/src/main/res/layout/activity_selecte_grade.xml +++ b/app/src/main/res/layout/activity_selecte_grade.xml @@ -238,7 +238,7 @@ android:layout_height="wrap_content" android:layout_marginTop="24dp" android:text="@string/hint" - android:textColor="@color/activation_color" /> + android:textColor="@color/default_blue" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_device.xml b/app/src/main/res/layout/fragment_device.xml new file mode 100644 index 0000000..ba9abaa --- /dev/null +++ b/app/src/main/res/layout/fragment_device.xml @@ -0,0 +1,648 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_dialog_restart.xml b/app/src/main/res/layout/fragment_dialog_restart.xml index 4f2a583..e8d4297 100644 --- a/app/src/main/res/layout/fragment_dialog_restart.xml +++ b/app/src/main/res/layout/fragment_dialog_restart.xml @@ -89,7 +89,7 @@ android:gravity="center" android:onClick="@{click::restart}" android:text="确定" - android:textColor="@color/activation_color" + android:textColor="@color/default_blue" android:textSize="@dimen/sp_11" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@+id/tv_content" diff --git a/app/src/main/res/layout/fragment_usage.xml b/app/src/main/res/layout/fragment_usage.xml new file mode 100644 index 0000000..30951a2 --- /dev/null +++ b/app/src/main/res/layout/fragment_usage.xml @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index dcb66d7..328a759 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -7,17 +7,258 @@ - #ffffff - #000000 - #80808080 - #D3D3D3 - #CD000000 #D3D3D3 - #4880ff - + #4880ff #E5000000 - #9a9a9a + #000000 + #000000 + #000000 + #d3d3d3 + + + + #00000000 + #FFE2C59B + #FFFFFFF0 + #FFFFFFE0 + #FFFFFF00 + #FFFFFAFA + #FFFFFAF0 + #FFFFFACD + #FFFFF8DC + #FFFFF5EE + #FFFFF0F5 + #FFFFEFD5 + #FFFFEBCD + #FFFFE4E1 + #FFFFE4C4 + #FFFFE4B5 + #FFFFDEAD + #FFFFDAB9 + #FFFFD700 + #FFFFC0CB + #FFFFB6C1 + #FFFFA500 + #FFFFA07A + #FFFF8C00 + #FFFF7F50 + #FFFF69B4 + #FFFF6347 + #FFFF4500 + #FFFF1493 + #FFFF00FF + #FFFF0000 + #FFFDF5E6 + #FFFAFAD2 + #FFFAF0E6 + #FFFAEBD7 + #FFFA8072 + #FFF8F8FF + #FFF5FFFA + #FFF5F5F5 + #FFF5F5DC + #FFF5DEB3 + #FFF4A460 + #FFF0FFFF + #FFF0F8FF + #FFF0E68C + #FFF08080 + #FFEEE8AA + #FFEE82EE + #FFE9967A + #FFE6E6FA + #FFE0FFFF + #FFDEB887 + #FFDDA0DD + #FFDCDCDC + #FFDC143C + #FFDB7093 + #FFDAA520 + #FFDA70D6 + #FFD8BFD8 + #FFD3D3D3 + #FFD2B48C + #FFD2691E + #FFCD853F + #FFCD5C5C + #FFC71585 + #FFC0C0C0 + #FFBDB76B + #FFBC8F8F + #FFBA55D3 + #FFB8860B + #FFB22222 + #FFB0E0E6 + #FFB0C4DE + #FFAFEEEE + #FFADFF2F + #FFADD8E6 + #FFA9A9A9 + #FFA52A2A + #FFA0522D + #FF9932CC + #FF98FB98 + #FF9400D3 + #FF9370DB + #FF90EE90 + #FF8FBC8F + #FF8B4513 + #FF8B008B + #FF8B0000 + #FF8A2BE2 + #FF87CEFA + #FF87CEEB + #FF808080 + #FF808000 + #FF800080 + #FF800000 + #FF7FFFD4 + #FF7FFF00 + #FF7CFC00 + #FF7B68EE + #FF778899 + #FF708090 + #FF6B8E23 + #FF6A5ACD + #FF696969 + #FF66CDAA + #FF6495ED + #FF5F9EA0 + #FF556B2F + #FF4B0082 + #FF48D1CC + #FF483D8B + #FF4682B4 + #FF4169E1 + #FF40E0D0 + #FF3CB371 + #FF32CD32 + #FF2F4F4F + #FF2E8B57 + #FF228B22 + #FF20B2AA + #FF1E90FF + #FF191970 + #FF00FFFF + #FF00FF7F + #FF00FF00 + #FF00FA9A + #FF00CED1 + #FF00BFFF + #FF008B8B + #FF008080 + #FF008000 + #FF006400 + #FF0000FF + #FF0000CD + #FF00008B + #FF000080 + #FF2B2B2B + + + #ffffff + #ffffe0 + #fffaf0 + #fffacd + #fff8dc + #fff5ee + #fff0f5 + #ffefd5 + #ffebcd + #ffe4e1 + #ffdead + #ffdab9 + #ffb6c1 + #ffa07a + #ff8c00 + #ff69b4 + #ff4500 + #ff1493 + #ff00ff + #fdf5e6 + #fafad2 + #faebd7 + #f8f8ff + #f5fffa + #f5f5f5 + #f4a460 + #f0fff0 + #f0f8ff + #f08080 + #eee8aa + #e9967a + #e0ffff + #deb887 + #dcdcdc + #db7093 + #d3d3d3 + #d3d3d3 + #cd5c5c + #c71585 + #bdb76b + #bc8f8f + #ba55d3 + #b8860b + #b0e0e6 + #b0c4de + #afeeee + #adff2f + #add8e6 + #a9a9a9 + #a9a9a9 + #9932cc + #98fb98 + #9400d3 + #9370db + #90ee90 + #8fbc8f + #8b4513 + #8b008b + #8b0000 + #8a2be2 + #87cefa + #87ceeb + #808080 + #7cfc00 + #7b68ee + #778899 + #778899 + #708090 + #708090 + #6b8e23 + #6a5acd + #696969 + #696969 + #66cdaa + #6495ed + #5f9ea0 + #556b2f + #48d1cc + #483d8b + #4682b4 + #4169e1 + #3cb371 + #32cd32 + #2f4f4f + #2f4f4f + #2e8b57 + #228b22 + #20b2aa + #1e90ff + #191970 + #00ffff + #00ff7f + #00fa9a + #00ced1 + #00bfff + #008b8b + #006400 + #0000cd + #00008b + #000000 + + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 5022e6c..2be8bd1 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -66,4 +66,25 @@ true + diff --git a/app/tpns-configs.json b/app/tpns-configs.json deleted file mode 100644 index 81f3138..0000000 --- a/app/tpns-configs.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "tpns": { - "access_id": "1500031216", - "access_key": "A1HBG2922B9Z" - }, - "com.fuying.sn": { - "channel": { - "enable": true - } - }, - "debug": false, - "version": "1.4.3.4", - "upgrade": false -} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 0538901..56b0369 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,6 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.6.4' - classpath "com.tencent.android.tpns:tpnsplugin:1.8.0" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/settings.gradle b/settings.gradle index f8118b6..5968770 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,2 @@ -include ':app' +include ':app', ':FlycoTabLayoutZ_Lib' rootProject.name='FLY我的设备'