├── .gitignore ├── LICENSE ├── README.md ├── aframelibrary ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── xhb │ │ └── aframelibrary │ │ ├── base │ │ ├── BaseActivity.java │ │ ├── BaseApplication.java │ │ ├── BaseDialogFragment.java │ │ ├── BaseFragment.java │ │ ├── BaseMvpActivity.java │ │ └── BaseMvpFragment.java │ │ ├── http │ │ ├── ApiException.java │ │ ├── BaseResponse.java │ │ ├── CacheInterceptor.java │ │ ├── HttpManager.java │ │ ├── HttpResult.java │ │ ├── HttpResultSubscriber.java │ │ ├── NormalInterceptor.java │ │ ├── TransformUtils.java │ │ └── https │ │ │ └── HttpsUtils.java │ │ ├── mvp │ │ ├── BasePresenter.java │ │ ├── IModel.java │ │ ├── IPresenter.java │ │ └── IView.java │ │ ├── receiver │ │ └── SimStateReceiver.java │ │ ├── utils │ │ ├── AESUtil.java │ │ ├── AppManager.java │ │ ├── CheckUtil.java │ │ ├── DESUtil.java │ │ ├── GsonUtil.java │ │ ├── ImageUtils.java │ │ ├── NotchScreenUtils.java │ │ ├── PermissionUtils.java │ │ ├── RoomUtils.java │ │ ├── RxImage.java │ │ ├── TripleDESUtil.java │ │ ├── UriTofilePath.java │ │ └── WallPaperHepler.java │ │ └── widget │ │ ├── ClearEditText.java │ │ ├── CountDownButton.java │ │ ├── CustomScrollView.java │ │ ├── HidePwEditText.java │ │ ├── IconLabelIndicatorView.java │ │ ├── IndicatorView.java │ │ ├── LabelIndicatorView.java │ │ ├── SpaceDecoration.java │ │ ├── StickyNavLayout.java │ │ ├── TipsLoadingView.java │ │ ├── ViewPagerSlide.java │ │ ├── dialog │ │ ├── BottomDialog.java │ │ ├── DialogMaker.java │ │ └── EasyProgressDialog.java │ │ └── keyboard │ │ ├── KeyboardType.java │ │ ├── SecurityConfigure.java │ │ └── SecurityKeyboard.java │ └── res │ ├── anim │ ├── push_bottom_in.xml │ ├── push_bottom_out.xml │ ├── slide_in_from_bottom.xml │ └── slide_out_to_bottom.xml │ ├── drawable-hdpi │ ├── dialog_toast_bg.png │ ├── keyboard_delete.png │ ├── keyboard_shift.png │ ├── keyboard_shift_c.png │ └── keyboard_space.png │ ├── drawable-xhdpi │ ├── icon_clear_normal.png │ ├── icon_clear_pressed.png │ ├── icon_notify_done.png │ └── icon_notify_error.png │ ├── drawable-xxhdpi │ ├── eye_close3x.png │ ├── eye_open3x.png │ ├── icon_next.png │ ├── icon_notify_done.png │ ├── icon_notify_error.png │ └── icon_notify_info.png │ ├── drawable │ ├── icon_notify_info.png │ ├── keyboard_key.xml │ ├── keyboard_key_bg.xml │ ├── keyboard_key_bg_c.xml │ └── selector_clear.xml │ ├── layout │ ├── easy_progress_dialog.xml │ ├── gs_keyboard.xml │ ├── layout_widget_icon_label_indicator_view.xml │ └── layout_widget_label_indicator_view.xml │ ├── values │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── ids_sticky_nav_layout.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ ├── gs_keyboard_english.xml │ ├── gs_keyboard_english_land.xml │ ├── gs_keyboard_number.xml │ ├── gs_keyboard_number_land.xml │ ├── gs_keyboard_symbols_shift.xml │ ├── gs_keyboard_symbols_shift_land.xml │ └── provider_paths.xml ├── app ├── .gitignore ├── build.gradle ├── libs │ └── aframelibrary-release.aar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── stx │ │ └── xhb │ │ └── aframe │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── stx │ │ │ └── xhb │ │ │ └── aframe │ │ │ ├── MainActivity.java │ │ │ ├── MyApplication.java │ │ │ └── TranscetActivity.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ └── activity_transcet.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── stx │ └── xhb │ └── aframe │ └── ExampleUnitTest.java ├── build.gradle ├── config.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AFrame 2 | 3 | [![](https://jitpack.io/v/xiaohaibin/AFrame.svg)](https://jitpack.io/#xiaohaibin/AFrame) 4 | 5 | #### Android 快速开发框架,提升开发效率,不断完善中~~~~~ 6 | 7 | 8 | ## 基本使用 9 | 10 | #### 1.添加 Gradle 11 | 12 | ## Jitpack 13 | 14 | Add it in your root build.gradle at the end of repositories: 15 | ``` 16 | allprojects { 17 | repositories { 18 | ... 19 | maven { url 'https://jitpack.io' } 20 | } 21 | } 22 | 23 | ``` 24 | Step 2. Add the dependency 25 | ``` 26 | dependencies { 27 | implementation 'com.github.xiaohaibin:AFrame:v0.1.0' 28 | } 29 | ``` 30 | 31 | ## 关于我 32 | 33 | * **Email**: 34 | * **Home**: 35 | * **掘金**: 36 | * **简书**: 37 | 38 | ### Contract 39 | 40 | [QQ群:271127803](http://qm.qq.com/cgi-bin/qm/qr?k=cM-ytK5bbZZZ4v7S1fMrTDzkjlFT0C9K) 41 | 42 | ![欢迎关注“大话微信”公众号](http://upload-images.jianshu.io/upload_images/1956769-2f49dcb0dc5195b6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/400) 43 | 44 | 45 | ### 你的 Statr 是我最大的动力,谢谢~~~ 46 | 47 | 48 | License 49 | -- 50 | Copyright (C) 2016 xhb_199409@163.com 51 | 52 | Licensed under the Apache License, Version 2.0 (the "License"); 53 | you may not use this file except in compliance with the License. 54 | You may obtain a copy of the License at 55 | 56 | http://www.apache.org/licenses/LICENSE-2.0 57 | 58 | Unless required by applicable law or agreed to in writing, software 59 | distributed under the License is distributed on an "AS IS" BASIS, 60 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 61 | See the License for the specific language governing permissions and 62 | limitations under the License. 63 | -------------------------------------------------------------------------------- /aframelibrary/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /aframelibrary/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.android["compileSdkVersion"] 5 | useLibrary 'org.apache.http.legacy' 6 | defaultConfig { 7 | minSdkVersion rootProject.ext.android["minSdkVersion"] 8 | targetSdkVersion rootProject.ext.android["targetSdkVersion"] 9 | } 10 | 11 | buildTypes { 12 | release { 13 | minifyEnabled false 14 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 15 | } 16 | } 17 | 18 | } 19 | 20 | dependencies { 21 | api fileTree(dir: 'libs', include: ['*.jar']) 22 | api(rootProject.ext.dependencies["appcompat-v7"]) { 23 | exclude module: 'support-annotations' 24 | } 25 | api(rootProject.ext.dependencies["recyclerview-v7"]) { 26 | exclude module: 'support-annotations' 27 | } 28 | api(rootProject.ext.dependencies["butterknife"]) { 29 | exclude module: 'support-annotations' 30 | exclude module: 'support-compat' 31 | } 32 | api rootProject.ext.dependencies["design"] 33 | api rootProject.ext.dependencies["picasso"] 34 | api rootProject.ext.dependencies["gson"] 35 | api rootProject.ext.dependencies["statusbarutil"] 36 | api rootProject.ext.dependencies["utilcode"] 37 | api rootProject.ext.dependencies["rxjava"] 38 | api rootProject.ext.dependencies["rxandroid"] 39 | api rootProject.ext.dependencies["retrofit"] 40 | api rootProject.ext.dependencies["retrofit-converter-gson"] 41 | api rootProject.ext.dependencies["retrofit-adapter-rxjava"] 42 | api rootProject.ext.dependencies["logging-interceptor"] 43 | api 'com.android.support.constraint:constraint-layout:1.1.3' 44 | } 45 | -------------------------------------------------------------------------------- /aframelibrary/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /aframelibrary/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/base/BaseApplication.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.base; 2 | 3 | import android.app.Application; 4 | 5 | import com.blankj.utilcode.util.Utils; 6 | 7 | /** 8 | * @author: xiaohaibin. 9 | * @time: 2018/10/12 10 | * @mail:xhb_199409@163.com 11 | * @github:https://github.com/xiaohaibin 12 | * @describe: BaseApplication 13 | */ 14 | public class BaseApplication extends Application { 15 | 16 | private static BaseApplication ourInstance; 17 | 18 | public static BaseApplication getInstance() { 19 | return ourInstance; 20 | } 21 | @Override 22 | public void onCreate() { 23 | super.onCreate(); 24 | ourInstance=this; 25 | Utils.init(this); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/base/BaseDialogFragment.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.base; 2 | 3 | import android.content.DialogInterface; 4 | import android.graphics.Color; 5 | import android.graphics.drawable.ColorDrawable; 6 | import android.os.Bundle; 7 | import android.support.annotation.IdRes; 8 | import android.support.annotation.LayoutRes; 9 | import android.support.annotation.NonNull; 10 | import android.support.annotation.Nullable; 11 | import android.support.v4.app.DialogFragment; 12 | import android.support.v4.app.FragmentManager; 13 | import android.view.Gravity; 14 | import android.view.LayoutInflater; 15 | import android.view.View; 16 | import android.view.ViewGroup; 17 | import android.view.Window; 18 | import android.view.WindowManager; 19 | 20 | /** 21 | * author: xiaohaibin. 22 | * time: 2018/7/31 23 | * mail:xhb_199409@163.com 24 | * github:https://github.com/xiaohaibin 25 | * describe: BaseDialogFragment 26 | */ 27 | public abstract class BaseDialogFragment extends DialogFragment { 28 | protected View contentView; 29 | private static final String TAG = "BaseDialogFragment"; 30 | private static final float DEFAULT_DIM = 0.2f; 31 | 32 | @Nullable 33 | @Override 34 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 35 | getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); 36 | getDialog().setCanceledOnTouchOutside(getCancelOutside()); 37 | setContentView(); 38 | BindView(contentView); 39 | registerViews(); 40 | return contentView; 41 | } 42 | 43 | @Override 44 | public void onStart() { 45 | super.onStart(); 46 | Window window = getDialog().getWindow(); 47 | WindowManager.LayoutParams params = window.getAttributes(); 48 | window.setGravity(getGravity()); 49 | window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 50 | params.dimAmount = getDimAmount(); 51 | params.width = getWidth(); 52 | if (getHeight() > 0) { 53 | params.height = getHeight(); 54 | } else { 55 | params.height = WindowManager.LayoutParams.WRAP_CONTENT; 56 | } 57 | window.setAttributes(params); 58 | } 59 | 60 | public int getHeight() { 61 | return -1; 62 | } 63 | 64 | public int getWidth() { 65 | return WindowManager.LayoutParams.MATCH_PARENT; 66 | } 67 | 68 | protected int getGravity() { 69 | return Gravity.CENTER; 70 | } 71 | 72 | protected boolean getCancelOutside() { 73 | return true; 74 | } 75 | 76 | public float getDimAmount() { 77 | return DEFAULT_DIM; 78 | } 79 | 80 | @LayoutRes 81 | protected abstract int getLayoutRes(); 82 | 83 | private void setContentView() { 84 | contentView = LayoutInflater.from(getActivity()).inflate(getLayoutRes(), null); 85 | } 86 | 87 | public View findViewById(@IdRes int id) { 88 | return contentView.findViewById(id); 89 | } 90 | 91 | public abstract void BindView(View v); 92 | 93 | /** 94 | * 省略findViewById的强制类型转换 95 | * @param id 96 | * @param 97 | * @return 98 | */ 99 | public T findView(@IdRes int id) { 100 | return (T) findViewById(id); 101 | } 102 | 103 | protected void initViews() { 104 | } 105 | 106 | protected void registerViews() { 107 | } 108 | 109 | public String getFragmentTag() { 110 | return TAG; 111 | } 112 | 113 | public void show(FragmentManager fragmentManager) { 114 | show(fragmentManager, getFragmentTag()); 115 | } 116 | 117 | 118 | public interface onDismissListener { 119 | void onDismiss(); 120 | } 121 | 122 | public onDismissListener mOnDismissListener; 123 | 124 | public void setOnDismissListener(onDismissListener onDismissListener) { 125 | mOnDismissListener = onDismissListener; 126 | } 127 | 128 | @Override 129 | public void onDismiss(DialogInterface dialog) { 130 | super.onDismiss(dialog); 131 | if (mOnDismissListener!=null){ 132 | mOnDismissListener.onDismiss(); 133 | } 134 | } 135 | 136 | /** 137 | * 判断对话框是否显示 138 | * @return 139 | */ 140 | public boolean isShow() { 141 | if (getDialog() != null) { 142 | return getDialog().isShowing(); 143 | } 144 | return false; 145 | } 146 | 147 | } 148 | 149 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/base/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.base; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | import butterknife.ButterKnife; 12 | import butterknife.Unbinder; 13 | 14 | /** 15 | * @author: xiaohaibin. 16 | * @time: 2018/9/6 17 | * @mail:xhb_199409@163.com 18 | * @github:https://github.com/xiaohaibin 19 | * @describe: BaseFragment 20 | */ 21 | public abstract class BaseFragment extends Fragment { 22 | 23 | /** 是否可见 */ 24 | protected boolean isViable = false; 25 | 26 | /** 标志位,标志Fragment已经初始化完成 */ 27 | protected boolean isPrepared = false; 28 | 29 | /** 标记已加载完成,保证懒加载只能加载一次 */ 30 | protected boolean hasLoaded = false; 31 | protected View rootView; 32 | protected Context mContext = null; 33 | private Unbinder mUnbinder; 34 | 35 | protected abstract int getLayoutResource(); 36 | 37 | protected void initView() { 38 | } 39 | 40 | protected void setListener() { 41 | } 42 | 43 | protected abstract void initData(Bundle savedInstanceState); 44 | 45 | @Nullable 46 | @Override 47 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 48 | if (getLayoutResource() != 0) { 49 | rootView = inflater.inflate(getLayoutResource(), null); 50 | } else { 51 | rootView = super.onCreateView(inflater, container, savedInstanceState); 52 | } 53 | mUnbinder = ButterKnife.bind(this, rootView); 54 | initData(savedInstanceState); 55 | initView(); 56 | setListener(); 57 | return rootView; 58 | } 59 | 60 | @Override 61 | public void onCreate(Bundle savedInstanceState) { 62 | super.onCreate(savedInstanceState); 63 | mContext = getActivity(); 64 | } 65 | 66 | @Override 67 | public void onViewCreated(View view, Bundle savedInstanceState) { 68 | super.onViewCreated(view, savedInstanceState); 69 | if (!isPrepared && getUserVisibleHint()) { 70 | onFragmentVisibleChange(true); 71 | isViable = true; 72 | } 73 | } 74 | 75 | @Override 76 | public void onDestroy() { 77 | super.onDestroy(); 78 | if (mUnbinder != null && mUnbinder != Unbinder.EMPTY) { 79 | mUnbinder.unbind(); 80 | } 81 | mUnbinder = null; 82 | } 83 | 84 | @Override 85 | public void onDestroyView() { 86 | super.onDestroyView(); 87 | isPrepared = false; 88 | hasLoaded = false; 89 | } 90 | 91 | /** 92 | * 在这里实现Fragment数据的缓加载. 93 | * @param isVisibleToUser 94 | */ 95 | @Override 96 | public void setUserVisibleHint(boolean isVisibleToUser) { 97 | super.setUserVisibleHint(isVisibleToUser); 98 | if (rootView == null) { 99 | return; 100 | } 101 | isPrepared = true; 102 | if (isVisibleToUser) { 103 | onFragmentVisibleChange(true); 104 | isViable = true; 105 | return; 106 | } 107 | 108 | if (isViable) { 109 | onFragmentVisibleChange(false); 110 | isViable = false; 111 | } 112 | } 113 | 114 | /** 115 | * 当界面可见时的操作 116 | */ 117 | protected void onVisible() { 118 | if (hasLoaded) { 119 | return; 120 | } 121 | lazyLoad(); 122 | hasLoaded = true; 123 | } 124 | 125 | 126 | /** 127 | * 数据懒加载 128 | */ 129 | protected void lazyLoad() { 130 | 131 | } 132 | 133 | /** 134 | * 当界面不可见时的操作 135 | */ 136 | protected void onInVisible() { 137 | 138 | } 139 | 140 | /** 141 | * 当前fragment可见状态发生变化时会回调该方法 142 | * @param isVisible 143 | */ 144 | protected void onFragmentVisibleChange(boolean isVisible) { 145 | if (isVisible) { 146 | onVisible(); 147 | } else { 148 | onInVisible(); 149 | } 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/base/BaseMvpActivity.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.base; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.Window; 7 | 8 | import com.jaeger.library.StatusBarUtil; 9 | import com.xhb.aframelibrary.R; 10 | import com.xhb.aframelibrary.mvp.IPresenter; 11 | 12 | import butterknife.ButterKnife; 13 | import butterknife.Unbinder; 14 | 15 | /** 16 | * @author: xiaohaibin. 17 | * @time: 2019/5/6 18 | * @mail:xhb_199409@163.com 19 | * @github:https://github.com/xiaohaibin 20 | * @describe: BaseMvpActivity 21 | */ 22 | public abstract class BaseMvpActivity

extends AppCompatActivity { 23 | protected final String TAG = this.getClass().getSimpleName(); 24 | private Unbinder mUnbinder; 25 | 26 | protected abstract P createPresenter(); 27 | 28 | protected abstract int getLayoutResource(); 29 | 30 | protected abstract void onInitData(Bundle bundle); 31 | 32 | /** 33 | * 如果当前页面逻辑简单, Presenter 可以为 null 34 | */ 35 | @Nullable 36 | protected P mPresenter; 37 | 38 | @Override 39 | protected void onCreate(@Nullable Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | requestWindowFeature(Window.FEATURE_NO_TITLE); 42 | StatusBarUtil.setColor(this, getResources().getColor(R.color.common_main)); 43 | try { 44 | int layoutResID = getLayoutResource(); 45 | if (layoutResID != 0) { 46 | setContentView(layoutResID); 47 | mUnbinder = ButterKnife.bind(this); 48 | } 49 | } catch (Exception e) { 50 | e.printStackTrace(); 51 | } 52 | onInitData(savedInstanceState); 53 | mPresenter=createPresenter(); 54 | if (mPresenter != null && mPresenter.isViewBind()) { 55 | mPresenter.onStart(); 56 | } 57 | } 58 | 59 | @Override 60 | protected void onDestroy() { 61 | super.onDestroy(); 62 | if (mUnbinder != null && mUnbinder != Unbinder.EMPTY) { 63 | mUnbinder.unbind(); 64 | mUnbinder = null; 65 | } 66 | if (mPresenter != null) { 67 | mPresenter.onDestory(); 68 | mPresenter = null; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/base/BaseMvpFragment.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.base; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.support.annotation.Nullable; 7 | import android.support.v4.app.Fragment; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | import com.xhb.aframelibrary.mvp.IPresenter; 13 | 14 | import butterknife.ButterKnife; 15 | import butterknife.Unbinder; 16 | 17 | /** 18 | * @author: xiaohaibin. 19 | * @time: 2019/5/6 20 | * @mail:xhb_199409@163.com 21 | * @github:https://github.com/xiaohaibin 22 | * @describe: BaseMvpFragment 23 | */ 24 | public abstract class BaseMvpFragment

extends Fragment { 25 | protected final String TAG = this.getClass().getSimpleName(); 26 | private Unbinder mUnbinder; 27 | 28 | protected abstract P createPresenter(); 29 | 30 | protected abstract int getLayoutResource(); 31 | 32 | protected abstract void onInitData(Bundle bundle); 33 | 34 | /** 35 | * 如果当前页面逻辑简单, Presenter 可以为 null 36 | */ 37 | @Nullable 38 | protected P mPresenter; 39 | 40 | /** 41 | * 是否可见 42 | */ 43 | protected boolean isViable = false; 44 | 45 | /** 46 | * 标志位,标志Fragment已经初始化完成 47 | */ 48 | protected boolean isPrepared = false; 49 | 50 | /** 51 | * 标记已加载完成,保证懒加载只能加载一次 52 | */ 53 | protected boolean hasLoaded = false; 54 | protected View rootView; 55 | 56 | @Nullable 57 | @Override 58 | public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 59 | if (getLayoutResource() != -1) { 60 | rootView = inflater.inflate(getLayoutResource(), null); 61 | } else { 62 | rootView = super.onCreateView(inflater, container, savedInstanceState); 63 | } 64 | if (rootView != null) { 65 | mUnbinder = ButterKnife.bind(this, rootView); 66 | } 67 | this.onInitData(savedInstanceState); 68 | return rootView; 69 | } 70 | 71 | @Override 72 | public void onAttach(Context context) { 73 | super.onAttach(context); 74 | mPresenter = createPresenter(); 75 | if (mPresenter != null && mPresenter.isViewBind()) { 76 | mPresenter.onStart(); 77 | } 78 | } 79 | 80 | @Override 81 | public void onDetach() { 82 | super.onDetach(); 83 | if (mPresenter != null) { 84 | mPresenter.onDestory(); 85 | mPresenter = null; 86 | } 87 | } 88 | 89 | @Override 90 | public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { 91 | super.onViewCreated(view, savedInstanceState); 92 | if (!isPrepared && getUserVisibleHint()) { 93 | onFragmentVisibleChange(true); 94 | isViable = true; 95 | } 96 | } 97 | 98 | @Override 99 | public void onDestroyView() { 100 | super.onDestroyView(); 101 | isPrepared = false; 102 | hasLoaded = false; 103 | } 104 | 105 | @Override 106 | public void onDestroy() { 107 | super.onDestroy(); 108 | if (mUnbinder != null && mUnbinder != Unbinder.EMPTY) { 109 | mUnbinder.unbind(); 110 | mUnbinder = null; 111 | } 112 | } 113 | 114 | /** 115 | * 在这里实现Fragment数据的缓加载. 116 | * @param isVisibleToUser 117 | */ 118 | @Override 119 | public void setUserVisibleHint(boolean isVisibleToUser) { 120 | super.setUserVisibleHint(isVisibleToUser); 121 | if (rootView == null) { 122 | return; 123 | } 124 | isPrepared = true; 125 | if (isVisibleToUser) { 126 | onFragmentVisibleChange(true); 127 | isViable = true; 128 | return; 129 | } 130 | if (isViable) { 131 | onFragmentVisibleChange(false); 132 | isViable = false; 133 | } 134 | } 135 | 136 | /** 137 | * 当界面可见时的操作 138 | */ 139 | protected void onVisible() { 140 | if (hasLoaded) { 141 | return; 142 | } 143 | lazyLoad(); 144 | hasLoaded = true; 145 | } 146 | 147 | 148 | /** 149 | * 数据懒加载 150 | */ 151 | protected void lazyLoad() { 152 | } 153 | 154 | /** 155 | * 当界面不可见时的操作 156 | */ 157 | protected void onInVisible() { 158 | } 159 | 160 | /** 161 | * 当前fragment可见状态发生变化时会回调该方法 162 | * @param isVisible 163 | */ 164 | protected void onFragmentVisibleChange(boolean isVisible) { 165 | if (isVisible) { 166 | onVisible(); 167 | } else { 168 | onInVisible(); 169 | } 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/http/ApiException.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.http; 2 | 3 | /** 4 | * @author: Mr.xiao on 2017/3/15 5 | * @mail:xhb_199409@163.com 6 | * @github:https://github.com/xiaohaibin 7 | * @describe: 捕获服务器约定的错误类型 8 | */ 9 | public class ApiException extends RuntimeException { 10 | /** 11 | * 未知错误 12 | */ 13 | public static final int UNKNOWN = 1000; 14 | /** 15 | * 数据解析错误 16 | */ 17 | public static final int PARSE_ERROR = 1001; 18 | 19 | private int errCode; 20 | private String errorMsg; 21 | 22 | public ApiException(int errCode, String msg) { 23 | super(msg); 24 | this.errCode = errCode; 25 | this.errorMsg=msg; 26 | } 27 | 28 | public int getErrCode() { 29 | return errCode; 30 | } 31 | 32 | public String getErrorMsg() { 33 | return errorMsg; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/http/BaseResponse.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.http; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @author : Mr.xiao on 2017/3/15 7 | * @mail:xhb_199409@163.com 8 | * @github:https://github.com/xiaohaibin 9 | * @describe: BaseResponse 10 | */ 11 | public class BaseResponse implements Serializable{ 12 | 13 | } 14 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/http/CacheInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.http; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | 6 | import com.blankj.utilcode.util.NetworkUtils; 7 | 8 | import java.io.IOException; 9 | 10 | import okhttp3.Interceptor; 11 | import okhttp3.Request; 12 | import okhttp3.Response; 13 | 14 | /** 15 | * @author:xiaohaibin 16 | * @Time:2017/6/20 17 | * @Emil:xhb_199409@163.com 18 | * @Github:https://github.com/xiaohaibin/ 19 | * @Describe:缓存拦截器 20 | */ 21 | public class CacheInterceptor implements Interceptor { 22 | 23 | private Context context; 24 | 25 | public CacheInterceptor(Context context) { 26 | this.context = context; 27 | } 28 | 29 | @Override 30 | public Response intercept(Chain chain) throws IOException { 31 | Request originalRequest = chain.request(); 32 | Request.Builder requestBuilder = originalRequest.newBuilder(); 33 | Request request = requestBuilder.build(); 34 | Response response = chain.proceed(request); 35 | if (NetworkUtils.isConnected()) { 36 | // 有网络时 设置缓存超时时间0个小时 37 | int maxAge = 0; 38 | // 如果单个请求不同请在请求中写上Cache-control头则按照对应的配置进行本地缓存时间配置 39 | String cacheControl = request.cacheControl().toString(); 40 | if (TextUtils.isEmpty(cacheControl)) { 41 | return response.newBuilder() 42 | .header("Cache-Control", "public, max-age=" + maxAge) 43 | //清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效 44 | .removeHeader("Pragma") 45 | .build(); 46 | } else { 47 | return response.newBuilder() 48 | .header("Cache-Control", cacheControl) 49 | //清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效 50 | .removeHeader("Pragma") 51 | .build(); 52 | } 53 | } else { 54 | // 无网络时,设置超时为1周 55 | int maxStale = 60 * 60 * 24 * 7; 56 | return response.newBuilder() 57 | //清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效 58 | .removeHeader("Pragma") 59 | .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale) 60 | .build(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/http/HttpManager.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.http; 2 | 3 | import com.blankj.utilcode.util.LogUtils; 4 | import com.xhb.aframelibrary.base.BaseApplication; 5 | import com.xhb.aframelibrary.http.https.HttpsUtils; 6 | import com.xhb.aframelibrary.utils.CheckUtil; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import okhttp3.Cache; 13 | import okhttp3.Interceptor; 14 | import okhttp3.OkHttpClient; 15 | import okhttp3.Request; 16 | import okhttp3.Response; 17 | import okhttp3.logging.HttpLoggingInterceptor; 18 | import retrofit2.Retrofit; 19 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 20 | import retrofit2.converter.gson.GsonConverterFactory; 21 | 22 | /** 23 | * Author: Mr.xiao on 2017/3/15 24 | * @mail:xhb_199409@163.com 25 | * @github:https://github.com/xiaohaibin 26 | * @describe: HTTP请求工厂类 27 | */ 28 | public class HttpManager { 29 | 30 | private volatile static HttpManager instance; 31 | private static boolean isDebug = false; 32 | private String baseUrl = ""; 33 | 34 | public static HttpManager getInstance() { 35 | if (instance == null) { 36 | synchronized (HttpManager.class) { 37 | if (instance == null) { 38 | instance = new HttpManager(); 39 | } 40 | } 41 | } 42 | return instance; 43 | } 44 | 45 | public void init(String ipUrl, boolean isDebug) { 46 | baseUrl = CheckUtil.checkNotNull(ipUrl, "baseUrl == null"); 47 | HttpManager.isDebug = isDebug; 48 | } 49 | 50 | 51 | /** 52 | * 无缓存模式 53 | * @param serviceClass 54 | * @param 55 | * @return 56 | */ 57 | public S createService(Class serviceClass) { 58 | Retrofit retrofit = new Retrofit.Builder() 59 | .baseUrl(baseUrl) 60 | .client(getOkHttpClient()) 61 | .addConverterFactory(GsonConverterFactory.create()) 62 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 63 | .build(); 64 | return retrofit.create(serviceClass); 65 | 66 | } 67 | 68 | /** 69 | * 有缓存模式 70 | * @param serviceClass 71 | * @param 72 | * @return 73 | */ 74 | public S createServiceWithCache(Class serviceClass) { 75 | Retrofit retrofit = new Retrofit.Builder() 76 | .baseUrl(baseUrl) 77 | .client(getOkHttpClientWithCache()) 78 | .addConverterFactory(GsonConverterFactory.create()) 79 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 80 | .build(); 81 | return retrofit.create(serviceClass); 82 | 83 | } 84 | 85 | 86 | HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() { 87 | @Override 88 | public void log(String message) { 89 | //打印retrofit日志 90 | LogUtils.i(message); 91 | } 92 | }); 93 | 94 | private static final long DEFAULT_TIMEOUT = 5; 95 | 96 | private OkHttpClient getOkHttpClient() { 97 | loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 98 | HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(null, null, null); 99 | //定制OkHttp 100 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 101 | //支持https 102 | builder.sslSocketFactory(sslParams.sSLSocketFactory); 103 | builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS); 104 | builder.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS); 105 | builder.addInterceptor(loggingInterceptor); 106 | builder.addInterceptor(new Interceptor() { 107 | @Override 108 | public Response intercept(Chain chain) throws IOException { 109 | Request originalRequest = chain.request(); 110 | Request.Builder requestBuilder = originalRequest.newBuilder(); 111 | Request request = requestBuilder.build(); 112 | return chain.proceed(request); 113 | } 114 | }); 115 | 116 | return builder.build(); 117 | } 118 | 119 | /** 120 | * 带缓存模式 121 | * @return 122 | */ 123 | private OkHttpClient getOkHttpClientWithCache() { 124 | HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(null, null, null); 125 | //定制OkHttp 126 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 127 | //支持https 128 | builder.sslSocketFactory(sslParams.sSLSocketFactory); 129 | builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS); 130 | builder.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS); 131 | builder.retryOnConnectionFailure(true); 132 | //debug模式开启日志输出 133 | loggingInterceptor.setLevel(isDebug ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.NONE); 134 | builder.addInterceptor(loggingInterceptor); 135 | 136 | //设置缓存 137 | File httpCacheDirectory = new File(BaseApplication.getInstance().getApplicationContext().getCacheDir(), "AFrameAppcache"); 138 | builder.cache(new Cache(httpCacheDirectory, 10 * 1024 * 1024)); 139 | builder.addNetworkInterceptor(new CacheInterceptor(BaseApplication.getInstance().getApplicationContext())); 140 | builder.addInterceptor(new NormalInterceptor(BaseApplication.getInstance().getApplicationContext())); 141 | 142 | return builder.build(); 143 | } 144 | 145 | 146 | } 147 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/http/HttpResult.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.http; 2 | 3 | /** 4 | * @author: Mr.xiao on 2017/3/15 5 | * @mail:xhb_199409@163.com 6 | * @github:https://github.com/xiaohaibin 7 | * @describe: 网络请求的实体基类 8 | */ 9 | public class HttpResult extends BaseResponse { 10 | 11 | public int code; 12 | 13 | private String msg; 14 | 15 | private T data; 16 | 17 | public String getMsg() { 18 | return msg; 19 | } 20 | 21 | public int getCode() { 22 | return code; 23 | } 24 | 25 | /** 26 | * 连接服务器是否成功 27 | * 28 | * @return 29 | */ 30 | public boolean isHttpSuccess() { 31 | return code == 1; 32 | } 33 | 34 | public T getData() { 35 | return data; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/http/HttpResultSubscriber.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.http; 2 | 3 | import android.net.ParseException; 4 | import android.util.Log; 5 | 6 | import com.google.gson.JsonParseException; 7 | 8 | import org.json.JSONException; 9 | 10 | import java.net.ConnectException; 11 | import java.net.SocketTimeoutException; 12 | import java.net.UnknownHostException; 13 | import java.util.concurrent.TimeoutException; 14 | 15 | import retrofit2.adapter.rxjava.HttpException; 16 | import rx.Subscriber; 17 | 18 | /** 19 | * @author: Mr.xiao on 2017/3/15 20 | * @mail:xhb_199409@163.com 21 | * @github:https://github.com/xiaohaibin 22 | * @describe: 网络请求结果订阅 23 | */ 24 | public abstract class HttpResultSubscriber extends Subscriber> { 25 | 26 | /** 27 | * 网络错误 28 | */ 29 | public static final int NETWORK_ERROR = 1002; 30 | 31 | /** 32 | * 证书出错 33 | */ 34 | public static final int SSL_ERROR = 1005; 35 | 36 | @Override 37 | public void onNext(HttpResult t) { 38 | if (isUnsubscribed()) { 39 | return; 40 | } 41 | if (t.isHttpSuccess()) { 42 | T data = t.getData(); 43 | if (data != null) { 44 | onSuccess(data); 45 | } 46 | } else { 47 | throw new ApiException(t.getCode(), t.getMsg()); 48 | } 49 | } 50 | 51 | @Override 52 | public void onCompleted() { 53 | if (!isUnsubscribed()) { 54 | onFinished(); 55 | } 56 | } 57 | 58 | @Override 59 | public void onError(Throwable e) { 60 | Log.e("==error==", e.getMessage() + "==="); 61 | if (isUnsubscribed()) { 62 | return; 63 | } 64 | //在这里做全局的错误处理 65 | if (e instanceof HttpException || 66 | e instanceof ConnectException || 67 | e instanceof SocketTimeoutException || 68 | e instanceof TimeoutException || 69 | e instanceof UnknownHostException) { 70 | //网络错误 71 | onError("服务器连接失败,请稍后再试", NETWORK_ERROR); 72 | } else if (e instanceof JsonParseException 73 | || e instanceof JSONException 74 | || e instanceof ParseException) { 75 | //数据解析错误 76 | onError("数据解析错误", ApiException.PARSE_ERROR); 77 | } else if (e instanceof ApiException) { 78 | ApiException apiException = (ApiException) e; 79 | //自定义的ApiException 80 | onError(apiException.getErrorMsg(), apiException.getErrCode()); 81 | } else if (e instanceof javax.net.ssl.SSLException) { 82 | onError("证书验证失败", SSL_ERROR); 83 | } else { 84 | onError("未知错误", ApiException.UNKNOWN); 85 | } 86 | onFinished(); 87 | } 88 | 89 | public abstract void onSuccess(T t); 90 | 91 | public abstract void onError(String msg, int code); 92 | 93 | /** 94 | * 成功或失败到最后都会调用 95 | */ 96 | public abstract void onFinished(); 97 | 98 | } 99 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/http/NormalInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.http; 2 | 3 | import android.content.Context; 4 | 5 | import com.blankj.utilcode.util.NetworkUtils; 6 | 7 | import java.io.IOException; 8 | 9 | import okhttp3.CacheControl; 10 | import okhttp3.Interceptor; 11 | import okhttp3.Request; 12 | import okhttp3.Response; 13 | 14 | /** 15 | * Author:xiaohaibin 16 | * Time:2017/6/21 17 | * Emil:xhb_199409@163.com 18 | * Github:https://github.com/xaohaibin/ 19 | * Describe:设置无网读取本地缓存 20 | */ 21 | public class NormalInterceptor implements Interceptor { 22 | private Context context; 23 | 24 | public NormalInterceptor(Context context) { 25 | this.context = context; 26 | } 27 | 28 | @Override 29 | public Response intercept(Chain chain) throws IOException { 30 | Request request = chain.request(); 31 | if (!NetworkUtils.isConnected()) { 32 | request = request.newBuilder() 33 | .cacheControl(CacheControl.FORCE_CACHE) 34 | .build(); 35 | } 36 | return chain.proceed(request); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/http/TransformUtils.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.http; 2 | 3 | import rx.Observable; 4 | import rx.Scheduler; 5 | import rx.android.schedulers.AndroidSchedulers; 6 | import rx.schedulers.Schedulers; 7 | 8 | /** 9 | * Author: Mr.xiao on 2017/3/15 10 | * 11 | * @mail:xhb_199409@163.com 12 | * @github:https://github.com/xiaohaibin 13 | * @describe: Rxjava线程切换工具类 14 | */ 15 | public class TransformUtils { 16 | 17 | public static Observable.Transformer defaultSchedulers() { 18 | return new Observable.Transformer() { 19 | @Override 20 | public Observable call(Observable tObservable) { 21 | return tObservable.observeOn(AndroidSchedulers.mainThread()) 22 | .unsubscribeOn(Schedulers.io()) 23 | .subscribeOn(Schedulers.io()); 24 | 25 | } 26 | }; 27 | } 28 | 29 | 30 | /** 31 | * 工作线程 32 | * 33 | * @param 34 | * @return 35 | */ 36 | public static Observable.Transformer all_io() { 37 | return new Observable.Transformer() { 38 | @Override 39 | public Observable call(Observable tObservable) { 40 | return tObservable 41 | .observeOn(Schedulers.io()) 42 | .unsubscribeOn(Schedulers.io()) 43 | .subscribeOn(Schedulers.io()); 44 | } 45 | }; 46 | } 47 | 48 | /** 49 | * 可自定义线程 50 | */ 51 | public static Observable.Transformer rxSchedulerHelper(final Scheduler scheduler) { 52 | return new Observable.Transformer() { 53 | @Override 54 | public Observable call(Observable tObservable) { 55 | return tObservable.subscribeOn(scheduler) 56 | .unsubscribeOn(AndroidSchedulers.mainThread()) 57 | .observeOn(AndroidSchedulers.mainThread()); 58 | } 59 | }; 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/mvp/BasePresenter.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.mvp; 2 | 3 | import com.xhb.aframelibrary.utils.CheckUtil; 4 | 5 | import java.lang.ref.WeakReference; 6 | 7 | /** 8 | * @author: xiaohaibin. 9 | * @time: 2019/5/6 10 | * @mail:xhb_199409@163.com 11 | * @github:https://github.com/xiaohaibin 12 | * @describe: BasePresenter 13 | */ 14 | public abstract class BasePresenter implements IPresenter { 15 | 16 | /** 17 | * 使用弱引用来防止内存泄漏 18 | */ 19 | protected WeakReference mRootView; 20 | protected M mModel; 21 | 22 | public BasePresenter(V rootView, M model) { 23 | CheckUtil.checkNotNull(model, "%s cannot be null", IModel.class.getName()); 24 | CheckUtil.checkNotNull(rootView, "%s cannot be null", IView.class.getName()); 25 | mRootView = new WeakReference(rootView); 26 | mModel = model; 27 | onStart(); 28 | } 29 | 30 | public BasePresenter(V rootView) { 31 | CheckUtil.checkNotNull(rootView, "%s cannot be null", IView.class.getName()); 32 | mRootView = new WeakReference(rootView); 33 | onStart(); 34 | } 35 | 36 | @Override 37 | public void onStart() {} 38 | 39 | @Override 40 | public void onDestory() { 41 | if (mRootView != null) { 42 | mRootView.clear(); 43 | mRootView = null; 44 | } 45 | if (mModel != null) { 46 | mModel.onDestroy(); 47 | mModel = null; 48 | } 49 | } 50 | 51 | @Override 52 | public boolean isViewBind() { 53 | return mRootView != null; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/mvp/IModel.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.mvp; 2 | 3 | /** 4 | * @author: xiaohaibin. 5 | * @time: 2019/5/6 6 | * @mail:xhb_199409@163.com 7 | * @github:https://github.com/xiaohaibin 8 | * @describe: IModel 9 | */ 10 | public interface IModel { 11 | void onDestroy(); 12 | } 13 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/mvp/IPresenter.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.mvp; 2 | 3 | /** 4 | * @author: xiaohaibin. 5 | * @time: 2019/5/6 6 | * @mail:xhb_199409@163.com 7 | * @github:https://github.com/xiaohaibin 8 | * @describe: IPresenter 9 | */ 10 | public interface IPresenter { 11 | 12 | void onStart(); 13 | 14 | void onDestory(); 15 | 16 | boolean isViewBind(); 17 | } 18 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/mvp/IView.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.mvp; 2 | 3 | /** 4 | * @author: xiaohaibin. 5 | * @time: 2019/5/6 6 | * @mail:xhb_199409@163.com 7 | * @github:https://github.com/xiaohaibin 8 | * @describe: IView 9 | */ 10 | public interface IView { 11 | void showMsg(String msg); 12 | 13 | void showLoading(); 14 | 15 | void hideLoadind(); 16 | } 17 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/receiver/SimStateReceiver.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.receiver; 2 | 3 | import android.app.Service; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.telephony.TelephonyManager; 8 | 9 | import com.blankj.utilcode.util.LogUtils; 10 | 11 | /** 12 | * @author: xiaohaibin. 13 | * @time: 2018/11/19 14 | * @mail:xhb_199409@163.com 15 | * @github:https://github.com/xiaohaibin 16 | * @describe: 监听sim状态改变的广播,返回sim卡的状态, 有效或者无效。 17 | * 双卡中只要有一张卡的状态有效即返回状态为有效,两张卡都无效则返回无效。 18 | */ 19 | public class SimStateReceiver extends BroadcastReceiver { 20 | 21 | private static final String TAG = "SimStateReceive"; 22 | public final static String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED"; 23 | public final static int SIM_VALID = 0; 24 | public final static int SIM_INVALID = 1; 25 | private int simState = SIM_INVALID; 26 | 27 | public int getSimState() { 28 | return simState; 29 | } 30 | 31 | public SimStateReceiver() { 32 | } 33 | 34 | public SimStateReceiver(SimCardStateChangeListener simCardStateChangeListener) { 35 | mSimCardStateChangeListener = simCardStateChangeListener; 36 | } 37 | 38 | @Override 39 | public void onReceive(Context context, Intent intent) { 40 | if (intent.getAction().equals(ACTION_SIM_STATE_CHANGED)) { 41 | if (mSimCardStateChangeListener!=null) { 42 | mSimCardStateChangeListener.simCardStateChange(); 43 | } 44 | TelephonyManager tm = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE); 45 | int state = tm.getSimState(); 46 | switch (state) { 47 | case TelephonyManager.SIM_STATE_READY: 48 | simState = SIM_VALID; 49 | LogUtils.i(TAG, "sim state :SIM_VALID"); 50 | break; 51 | case TelephonyManager.SIM_STATE_UNKNOWN: 52 | case TelephonyManager.SIM_STATE_ABSENT: 53 | case TelephonyManager.SIM_STATE_PIN_REQUIRED: 54 | case TelephonyManager.SIM_STATE_PUK_REQUIRED: 55 | case TelephonyManager.SIM_STATE_NETWORK_LOCKED: 56 | default: 57 | LogUtils.i(TAG, "sim state :SIM_INVALID"); 58 | simState = SIM_INVALID; 59 | break; 60 | } 61 | } 62 | } 63 | 64 | public SimCardStateChangeListener mSimCardStateChangeListener; 65 | 66 | public interface SimCardStateChangeListener { 67 | void simCardStateChange(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/utils/AESUtil.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.utils; 2 | 3 | import java.security.NoSuchAlgorithmException; 4 | 5 | import javax.crypto.Cipher; 6 | import javax.crypto.KeyGenerator; 7 | import javax.crypto.SecretKey; 8 | import javax.crypto.spec.SecretKeySpec; 9 | 10 | /** 11 | * AES加密工具类 12 | */ 13 | public class AESUtil { 14 | 15 | /* 16 | * 加密用的Key 可以用26个字母和数字组成 此处使用AES-128-CBC加密模式,key需要为16位。 17 | */ 18 | private static final String S_KEY = "smkldospdosldaaa";//key,可自行修改 19 | 20 | /** 21 | * 获取密钥 22 | * @return 密钥 23 | * @throws NoSuchAlgorithmException 24 | */ 25 | public static byte[] initKey() throws NoSuchAlgorithmException { 26 | KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); 27 | keyGenerator.init(128); //192,256 28 | SecretKey secretKey = keyGenerator.generateKey(); 29 | return secretKey.getEncoded(); 30 | } 31 | 32 | /** 33 | * AES加密 34 | * @param data 要加密的数据 35 | * @param key 加密所使用的密钥 36 | * @return 加密后的数据 37 | * @throws Exception 38 | */ 39 | public static byte[] encrypt(byte[] data, byte[] key) throws Exception { 40 | SecretKey secretKey = new SecretKeySpec(key, "AES"); 41 | Cipher cipher = Cipher.getInstance("AES"); 42 | cipher.init(Cipher.ENCRYPT_MODE, secretKey); 43 | return cipher.doFinal(data); 44 | } 45 | 46 | /** 47 | * AES解密 48 | * @param data 要解密的数据 49 | * @param key 解密所使用的密钥 50 | * @return 解密后的数据, 即源数据 51 | * @throws Exception 52 | */ 53 | public static byte[] decrypt(byte[] data, byte[] key) throws Exception { 54 | SecretKey secretKey = new SecretKeySpec(key, "AES"); 55 | Cipher cipher = Cipher.getInstance("AES"); 56 | cipher.init(Cipher.DECRYPT_MODE, secretKey); 57 | return cipher.doFinal(data); 58 | } 59 | 60 | public static String encrypt(String data, String pwd) { 61 | try { 62 | return parseByte2HexStr(encrypt(data.getBytes(), pwd.getBytes())); 63 | } catch (Exception e) { 64 | e.printStackTrace(); 65 | return data; 66 | } 67 | } 68 | 69 | public static String decrypt(String data, String pwd) { 70 | try { 71 | return new String(decrypt(parseHexStr2Byte(data), pwd.getBytes())); 72 | } catch (Exception e) { 73 | e.printStackTrace(); 74 | return data; 75 | } 76 | } 77 | 78 | public static String encrypt(String data) { 79 | return encrypt(data, S_KEY); 80 | } 81 | 82 | public static String decrypt(String data) { 83 | return decrypt(data, S_KEY); 84 | } 85 | 86 | /** 87 | * 将二进制转换成16进制 88 | * @param buf 89 | * @return 90 | */ 91 | public static String parseByte2HexStr(byte buf[]) { 92 | StringBuffer sb = new StringBuffer(); 93 | for (int i = 0; i < buf.length; i++) { 94 | String hex = Integer.toHexString(buf[i] & 0xFF); 95 | if (hex.length() == 1) { 96 | hex = '0' + hex; 97 | } 98 | sb.append(hex.toUpperCase()); 99 | } 100 | return sb.toString(); 101 | } 102 | 103 | /** 104 | * 将16进制转换为二进制 105 | * @param hexStr 106 | * @return 107 | */ 108 | public static byte[] parseHexStr2Byte(String hexStr) { 109 | if (hexStr.length() < 1) { 110 | return null; 111 | } 112 | byte[] result = new byte[hexStr.length() / 2]; 113 | for (int i = 0; i < hexStr.length() / 2; i++) { 114 | int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16); 115 | int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16); 116 | result[i] = (byte) (high * 16 + low); 117 | } 118 | return result; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/utils/AppManager.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.utils; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | 6 | import java.util.Iterator; 7 | import java.util.Stack; 8 | 9 | /** 10 | * link https://xiaohaibin.github.io/ 11 | * email: xhb_199409@163.com 12 | * github: https://github.com/xiaohaibin 13 | * describe: Activity管理工具类 14 | */ 15 | public class AppManager { 16 | 17 | private static Stack activityStack; 18 | private static AppManager instance; 19 | 20 | private AppManager() { 21 | } 22 | 23 | /** 24 | * @return 获取activity管理实例 25 | */ 26 | public static AppManager getInstance() { 27 | if (instance == null) { 28 | instance = new AppManager(); 29 | } 30 | return instance; 31 | } 32 | 33 | /** 34 | * 添加Activity到堆栈 35 | */ 36 | public void pushActivity(Activity activity) { 37 | if (activityStack == null) { 38 | activityStack = new Stack(); 39 | } 40 | activityStack.add(activity); 41 | } 42 | 43 | /** 44 | * 获取当前Activity(堆栈中最后一个压入的) 45 | */ 46 | public Activity currentActivity() { 47 | Activity activity = null; 48 | if (activityStack != null && !activityStack.empty()) { 49 | activity = activityStack.lastElement(); 50 | } 51 | return activity; 52 | } 53 | 54 | /** 55 | * 结束当前Activity(堆栈中最后一个压入的) 56 | */ 57 | public void finishActivity() { 58 | if (activityStack != null && !activityStack.empty()) { 59 | Activity activity = activityStack.lastElement(); 60 | killActivity(activity); 61 | } 62 | } 63 | 64 | /** 65 | * 结束指定的Activity 66 | */ 67 | public void killActivity(Activity activity) { 68 | if (activity != null && activityStack != null && !activityStack.empty()) { 69 | activity.finish(); 70 | activityStack.remove(activity); 71 | } 72 | } 73 | 74 | /** 75 | * 结束指定类名的Activity 76 | */ 77 | public void killActivity(Class cls) { 78 | if (activityStack != null && !activityStack.empty()) { 79 | Iterator iterator = activityStack.iterator(); 80 | while (iterator.hasNext()) { 81 | Activity activity = iterator.next(); 82 | if (activity.getClass().equals(cls)) { 83 | activity.finish(); 84 | iterator.remove(); 85 | } 86 | } 87 | } 88 | } 89 | 90 | /** 91 | * 结束所有Activity 92 | */ 93 | public void killAllActivity() { 94 | while (activityStack != null && !activityStack.empty()) { 95 | Activity activity = currentActivity(); 96 | killActivity(activity); 97 | activityStack.clear(); 98 | } 99 | } 100 | 101 | /** 102 | * 退出应用程序 103 | */ 104 | public void AppExit(Context context) { 105 | try { 106 | killAllActivity(); 107 | android.app.ActivityManager activityMgr = (android.app.ActivityManager) context 108 | .getSystemService(Context.ACTIVITY_SERVICE); 109 | activityMgr.killBackgroundProcesses(context.getPackageName()); 110 | System.exit(0); 111 | } catch (Exception ignored) { 112 | } 113 | } 114 | 115 | public int getSize() { 116 | return activityStack.size(); 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/utils/CheckUtil.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.utils; 2 | 3 | import android.support.annotation.Nullable; 4 | 5 | /** 6 | * @author: xiaohaibin. 7 | * @time: 2018/10/19 8 | * @mail:xhb_199409@163.com 9 | * @github:https://github.com/xiaohaibin 10 | * @describe: 11 | */ 12 | public class CheckUtil { 13 | 14 | public static T checkNotNull(@Nullable T object, String message) { 15 | if (object == null) { 16 | throw new NullPointerException(message); 17 | } 18 | return object; 19 | } 20 | 21 | public static T checkNotNull(T reference, @Nullable Object errorMessage) { 22 | if(reference == null) { 23 | throw new NullPointerException(String.valueOf(errorMessage)); 24 | } else { 25 | return reference; 26 | } 27 | } 28 | 29 | public static T checkNotNull(T reference, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) { 30 | if(reference == null) { 31 | throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs)); 32 | } else { 33 | return reference; 34 | } 35 | } 36 | 37 | static String format(String template, @Nullable Object... args) { 38 | template = String.valueOf(template); 39 | StringBuilder builder = new StringBuilder(template.length() + 16 * args.length); 40 | int templateStart = 0; 41 | 42 | int i; 43 | int placeholderStart; 44 | for(i = 0; i < args.length; templateStart = placeholderStart + 2) { 45 | placeholderStart = template.indexOf("%s", templateStart); 46 | if(placeholderStart == -1) { 47 | break; 48 | } 49 | 50 | builder.append(template.substring(templateStart, placeholderStart)); 51 | builder.append(args[i++]); 52 | } 53 | 54 | builder.append(template.substring(templateStart)); 55 | if(i < args.length) { 56 | builder.append(" ["); 57 | builder.append(args[i++]); 58 | 59 | while(i < args.length) { 60 | builder.append(", "); 61 | builder.append(args[i++]); 62 | } 63 | 64 | builder.append(']'); 65 | } 66 | 67 | return builder.toString(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/utils/DESUtil.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.utils; 2 | import android.util.Log; 3 | 4 | import javax.crypto.Cipher; 5 | import javax.crypto.KeyGenerator; 6 | import javax.crypto.SecretKey; 7 | import javax.crypto.spec.SecretKeySpec; 8 | 9 | /** 10 | * DES加密工具类 11 | */ 12 | public class DESUtil { 13 | 14 | private static final String TAG = "TAG"; 15 | 16 | 17 | /** 18 | * @return 19 | */ 20 | public static byte[] initKey() { 21 | try { 22 | //KeyGenerator 密钥生成器 23 | KeyGenerator keyGenerator = KeyGenerator.getInstance("DES"); 24 | //初始化密钥生成器 25 | keyGenerator.init(64); 26 | //生成密钥 27 | SecretKey secretKey = keyGenerator.generateKey(); 28 | return secretKey.getEncoded(); 29 | } catch (Exception e) { 30 | Log.e(TAG, "initKey: " + e.getMessage()); 31 | } 32 | return null; 33 | } 34 | 35 | /** 36 | * DES加密 37 | * 38 | * @param data 需要加密的数据 39 | * @param key 加密使用的密钥 40 | * @return 加密后获取的字节数组 41 | */ 42 | public static byte[] encrypt(byte[] data, byte[] key) { 43 | //恢复密钥 44 | SecretKey secretKey = new SecretKeySpec(key, "DES"); 45 | try { 46 | //Cipher完成加密或解密工作 47 | Cipher cipher = Cipher.getInstance("DES"); 48 | //根据密钥对Cipher进行初始化 ENCRYPT_MODE, DECRYPT_MODE 49 | cipher.init(Cipher.ENCRYPT_MODE, secretKey); 50 | //加密 51 | return cipher.doFinal(data); 52 | } catch (Exception e) { 53 | Log.e(TAG, "encrypt: " + e.getMessage()); 54 | } 55 | return null; 56 | } 57 | 58 | /** 59 | * DES解密 60 | */ 61 | /** 62 | * @param data 密文对应的字节数组 63 | * @param key 算法名字 64 | * @return 解密后的字节数组 65 | */ 66 | public static byte[] decrypt(byte[] data, byte[] key) { 67 | SecretKey secretKey = new SecretKeySpec(key, "DES"); 68 | try { 69 | Cipher cipher = Cipher.getInstance("DES"); 70 | cipher.init(Cipher.DECRYPT_MODE, secretKey); 71 | return cipher.doFinal(data); 72 | } catch (Exception e) { 73 | Log.e(TAG, "decrypt: " + e.getMessage()); 74 | } 75 | return null; 76 | } 77 | } 78 | 79 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/utils/GsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.utils; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.TypeAdapter; 6 | import com.google.gson.TypeAdapterFactory; 7 | import com.google.gson.reflect.TypeToken; 8 | import com.google.gson.stream.JsonReader; 9 | import com.google.gson.stream.JsonToken; 10 | import com.google.gson.stream.JsonWriter; 11 | 12 | import java.io.IOException; 13 | 14 | /** 15 | * Description:将null转换为空字符串 16 | */ 17 | public class GsonUtil { 18 | 19 | public static Gson newGson() { 20 | return new GsonBuilder().registerTypeAdapterFactory(new NullStringToEmptyAdapterFactory()).create(); 21 | } 22 | 23 | /** 24 | * http://www.jianshu.com/p/c6dd9a6b10ee 25 | */ 26 | public static class NullStringToEmptyAdapterFactory implements TypeAdapterFactory { 27 | @Override 28 | @SuppressWarnings("unchecked") 29 | public TypeAdapter create(Gson gson, TypeToken type) { 30 | Class rawType = (Class) type.getRawType(); 31 | if (rawType != String.class) { 32 | return null; 33 | } 34 | return (TypeAdapter) new StringNullAdapter(); 35 | } 36 | } 37 | 38 | /** 39 | * http://www.jianshu.com/p/c6dd9a6b10ee 40 | */ 41 | public static class StringNullAdapter extends TypeAdapter { 42 | @Override 43 | public String read(JsonReader reader) throws IOException { 44 | if (reader.peek() == JsonToken.NULL) { 45 | reader.nextNull(); 46 | return ""; 47 | } 48 | return reader.nextString(); 49 | } 50 | 51 | @Override 52 | public void write(JsonWriter writer, String value) throws IOException { 53 | if (value == null) { 54 | writer.nullValue(); 55 | return; 56 | } 57 | writer.value(value); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/utils/ImageUtils.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.utils; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapFactory; 5 | import android.text.TextUtils; 6 | 7 | import java.io.File; 8 | 9 | /** 10 | * @author: xiaohaibin. 11 | * @time: 2018/12/27 12 | * @mail:xhb_199409@163.com 13 | * @github:https://github.com/xiaohaibin 14 | * @describe: ImageUtils 15 | */ 16 | public class ImageUtils { 17 | /** 18 | * Return bitmap. 19 | * @param filePath The path of file. 20 | * @return bitmap 21 | */ 22 | public static Bitmap getBitmap(final String filePath) { 23 | if (TextUtils.isEmpty(filePath)) { 24 | return null; 25 | } 26 | return BitmapFactory.decodeFile(filePath); 27 | } 28 | 29 | /** 30 | * Return bitmap. 31 | * @param file The file. 32 | * @return bitmap 33 | */ 34 | public static Bitmap getBitmap(final File file) { 35 | if (file == null) { 36 | return null; 37 | } 38 | return BitmapFactory.decodeFile(file.getAbsolutePath()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/utils/NotchScreenUtils.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.utils; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.util.Log; 6 | import android.view.DisplayCutout; 7 | import android.view.View; 8 | import android.view.WindowInsets; 9 | 10 | import java.lang.reflect.InvocationTargetException; 11 | import java.lang.reflect.Method; 12 | 13 | /** 14 | * author: xiaohaibin. 15 | * time: 2018/12/4 16 | * mail:xhb_199409@163.com 17 | * github:https://github.com/xiaohaibin 18 | * describe: Android 判断是否是刘海屏工具类 19 | */ 20 | public class NotchScreenUtils { 21 | 22 | private static final String TAG = "NotchScreenUtils"; 23 | public static final int VIVO_NOTCH = 0x00000020;//是否有刘海 24 | public static final int VIVO_FILLET = 0x00000008;//是否有圆角 25 | 26 | 27 | /** 28 | * 判断是否是刘海屏 29 | * @return 30 | */ 31 | public static boolean hasNotchScreen(Activity activity){ 32 | return hasNotchAtXiaoMi(activity) || hasNotchAtHuawei(activity) || hasNotchAtOPPO(activity) 33 | || hasNotchAtVivo(activity) || isAndroidP(activity) != null; 34 | } 35 | 36 | /** 37 | * Android P 刘海屏判断 38 | * @param activity 39 | * @return 40 | */ 41 | public static DisplayCutout isAndroidP(Activity activity) { 42 | View decorView = activity.getWindow().getDecorView(); 43 | if (decorView != null && android.os.Build.VERSION.SDK_INT >= 28) { 44 | WindowInsets windowInsets = decorView.getRootWindowInsets(); 45 | if (windowInsets != null) { 46 | return windowInsets.getDisplayCutout(); 47 | } 48 | } 49 | return null; 50 | } 51 | 52 | 53 | /** 54 | * OPPO刘海屏判断 55 | * @return 56 | */ 57 | public static boolean hasNotchAtOPPO(Context context) { 58 | return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism"); 59 | } 60 | 61 | /** 62 | * VIVO刘海屏判断 63 | * @return 64 | */ 65 | public static boolean hasNotchAtVivo(Context context) { 66 | boolean ret = false; 67 | try { 68 | ClassLoader classLoader = context.getClassLoader(); 69 | Class FtFeature = classLoader.loadClass("android.util.FtFeature"); 70 | Method method = FtFeature.getMethod("isFeatureSupport", int.class); 71 | ret = (boolean) method.invoke(FtFeature, VIVO_NOTCH); 72 | } catch (ClassNotFoundException e) { 73 | Log.e(TAG, "hasNotchAtVivo ClassNotFoundException"); 74 | } catch (NoSuchMethodException e) { 75 | Log.e(TAG, "hasNotchAtVivo NoSuchMethodException"); 76 | } catch (Exception e) { 77 | Log.e(TAG, "hasNotchAtVivo Exception"); 78 | } finally { 79 | return ret; 80 | } 81 | } 82 | 83 | /** 84 | * 华为刘海屏判断 85 | * @return 86 | */ 87 | public static boolean hasNotchAtHuawei(Context context) { 88 | boolean ret = false; 89 | try { 90 | ClassLoader classLoader = context.getClassLoader(); 91 | Class HwNotchSizeUtil = classLoader.loadClass("com.huawei.android.util.HwNotchSizeUtil"); 92 | Method get = HwNotchSizeUtil.getMethod("hasNotchInScreen"); 93 | ret = (boolean) get.invoke(HwNotchSizeUtil); 94 | } catch (ClassNotFoundException e) { 95 | Log.e(TAG, "hasNotchAtHuawei ClassNotFoundException"); 96 | } catch (NoSuchMethodException e) { 97 | Log.e(TAG, "hasNotchAtHuawei NoSuchMethodException"); 98 | } catch (Exception e) { 99 | Log.e(TAG, "hasNotchAtHuawei Exception"); 100 | } finally { 101 | return ret; 102 | } 103 | } 104 | 105 | /** 106 | * 获取华为手机刘海屏尺寸 107 | * @param context 108 | * @return int[0] 宽 int[1] 高 109 | */ 110 | public static int[] getNotchSizeAtHuawei(Context context) { 111 | int[] ret = new int[]{0, 0}; 112 | try { 113 | ClassLoader cl = context.getClassLoader(); 114 | Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil"); 115 | Method get = HwNotchSizeUtil.getMethod("getNotchSize"); 116 | ret = (int[]) get.invoke(HwNotchSizeUtil); 117 | } catch (ClassNotFoundException e) { 118 | Log.e(TAG, "getNotchSizeAtHuawei ClassNotFoundException"); 119 | } catch (NoSuchMethodException e) { 120 | Log.e(TAG, "getNotchSizeAtHuawei NoSuchMethodException"); 121 | } catch (Exception e) { 122 | Log.e(TAG, "getNotchSizeAtHuawei Exception"); 123 | } finally { 124 | return ret; 125 | } 126 | } 127 | 128 | 129 | /** 130 | * 小米刘海屏判断. 131 | * @return 系统增加了 property ro.miui.notch,值为1时则是 Notch 屏手机。 132 | * @throws IllegalArgumentException if the key exceeds 32 characters 133 | */ 134 | public static boolean hasNotchAtXiaoMi(Activity activity) { 135 | if (isXiaomi()) { 136 | try { 137 | ClassLoader classLoader = activity.getClassLoader(); 138 | @SuppressWarnings("rawtypes") 139 | Class SystemProperties = classLoader.loadClass("android.os.SystemProperties"); 140 | //参数类型 141 | @SuppressWarnings("rawtypes") 142 | Class[] paramTypes = new Class[2]; 143 | paramTypes[0] = String.class; 144 | paramTypes[1] = int.class; 145 | Method getInt = SystemProperties.getMethod("getInt", paramTypes); 146 | //参数 147 | Object[] params = new Object[2]; 148 | params[0] = new String("ro.miui.notch"); 149 | params[1] = new Integer(0); 150 | return (Integer) getInt.invoke(SystemProperties, params) == 1; 151 | 152 | } catch (ClassNotFoundException e) { 153 | e.printStackTrace(); 154 | } catch (NoSuchMethodException e) { 155 | e.printStackTrace(); 156 | } catch (IllegalAccessException e) { 157 | e.printStackTrace(); 158 | } catch (IllegalArgumentException e) { 159 | e.printStackTrace(); 160 | } catch (InvocationTargetException e) { 161 | e.printStackTrace(); 162 | } 163 | } 164 | return false; 165 | } 166 | 167 | /** 168 | * 获取小米刘海屏高度 169 | * @param context 170 | * @return 171 | */ 172 | public static int getNotchSizeAtXiaoMi(Context context) { 173 | int resourceId = context.getResources().getIdentifier("notch_height", "dimen", "android"); 174 | if (resourceId > 0) { 175 | return context.getResources().getDimensionPixelSize(resourceId); 176 | } 177 | return 0; 178 | } 179 | 180 | /** 181 | * 判断是否是小米设备 182 | * @return 183 | */ 184 | private static boolean isXiaomi() { 185 | return android.os.Build.MANUFACTURER.contains("Xiaomi") || "Xiaomi".equalsIgnoreCase(android.os.Build.MANUFACTURER); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/utils/PermissionUtils.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.utils; 2 | 3 | import android.app.Activity; 4 | import android.app.AppOpsManager; 5 | import android.content.Context; 6 | import android.content.pm.PackageManager; 7 | import android.os.Binder; 8 | import android.os.Build; 9 | import android.support.annotation.RequiresApi; 10 | import android.support.v4.app.ActivityCompat; 11 | import android.support.v4.content.ContextCompat; 12 | import android.text.TextUtils; 13 | import android.util.Log; 14 | 15 | import java.io.BufferedReader; 16 | import java.io.IOException; 17 | import java.io.InputStreamReader; 18 | 19 | public class PermissionUtils { 20 | private static final String TAG = "PermissionUtils"; 21 | private static final String BRAND_VIVO = "vivo"; 22 | private static final String BRAND_XIAOMI = "xiaomi"; 23 | private static final String BRAND_HUAWEI = "HUAWEI"; 24 | private static final String BRAND_GOOGLE = "google"; 25 | /** 26 | * 申请权限 27 | * @param activity 28 | * @param permissions 29 | * @param requestCode 30 | */ 31 | public static void requestPermissions(Activity activity, String[] permissions, int requestCode) { 32 | // 先检查是否已经授权 33 | if (!checkPermissionsGroup(activity, permissions)) { 34 | ActivityCompat.requestPermissions(activity, permissions, requestCode); 35 | } 36 | } 37 | 38 | public String checkBrand() { 39 | return Build.BRAND; 40 | } 41 | 42 | /** 43 | * 检查单个权限 44 | * @param context 45 | * @param permission 46 | * @return 47 | */ 48 | public static boolean checkPersmission(Context context, String permission) { 49 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 50 | return true; 51 | } 52 | 53 | if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) { 54 | return false; 55 | } 56 | 57 | return true; 58 | } 59 | 60 | /** 61 | * 检查多个权限 62 | * @param context 63 | * @param permissions 64 | * @return 65 | */ 66 | public static boolean checkPermissionsGroup(Context context, String[] permissions) { 67 | boolean result = false; 68 | for (String permission : permissions) { 69 | result = checkPersmission(context, permission); 70 | Log.e("PermissionUtils","result"+result); 71 | } 72 | return result; 73 | } 74 | 75 | 76 | /** 77 | * 78 | * 通过AppOpsManager判断小米手机授权情况 79 | * @return 80 | */ 81 | @RequiresApi(api = Build.VERSION_CODES.KITKAT) 82 | public static boolean checkXiaomi(Context context, String[] opstrArrays) { 83 | AppOpsManager appOpsManager = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); 84 | String packageName = context.getPackageName(); 85 | for (String opstr : opstrArrays) { 86 | int locationOp = appOpsManager.checkOp(opstr, Binder.getCallingUid(), packageName); 87 | if (locationOp == AppOpsManager.MODE_IGNORED) { 88 | return false; 89 | } 90 | } 91 | return true; 92 | } 93 | public static boolean checkIsOppoRom() { 94 | //https://github.com/zhaozepeng/FloatWindowPermission/pull/26 95 | return Build.MANUFACTURER.contains("OPPO") || Build.MANUFACTURER.contains("oppo"); 96 | } 97 | public static boolean checkIsMeizuRom() { 98 | //return Build.MANUFACTURER.contains("Meizu"); 99 | String meizuFlymeOSFlag = getSystemProperty("ro.build.display.id"); 100 | if (TextUtils.isEmpty(meizuFlymeOSFlag)){ 101 | return false; 102 | }else { 103 | return meizuFlymeOSFlag.contains("flyme") || meizuFlymeOSFlag.toLowerCase().contains("flyme"); 104 | } 105 | } 106 | 107 | public static String getSystemProperty(String propName) { 108 | String line; 109 | BufferedReader input = null; 110 | try { 111 | Process p = Runtime.getRuntime().exec("getprop " + propName); 112 | input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024); 113 | line = input.readLine(); 114 | input.close(); 115 | } catch (IOException ex) { 116 | Log.e(TAG, "Unable to read sysprop " + propName, ex); 117 | return null; 118 | } finally { 119 | if (input != null) { 120 | try { 121 | input.close(); 122 | } catch (IOException e) { 123 | Log.e(TAG, "Exception while closing InputStream", e); 124 | } 125 | } 126 | } 127 | return line; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/utils/RoomUtils.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.utils; 2 | 3 | import android.os.Build; 4 | import android.text.TextUtils; 5 | import android.util.Log; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.IOException; 9 | import java.io.InputStreamReader; 10 | 11 | /** 12 | * @author: xiaohaibin. 13 | * @time: 2018/12/27 14 | * @mail:xhb_199409@163.com 15 | * @github:https://github.com/xiaohaibin 16 | * @describe: 判断手机ROM,检测ROM是MIUI、EMUI还是Flyme 17 | */ 18 | public class RoomUtils { 19 | 20 | private static final String TAG = "Rom"; 21 | 22 | public static final String ROM_MIUI = "MIUI"; 23 | public static final String ROM_EMUI = "EMUI"; 24 | public static final String ROM_FLYME = "FLYME"; 25 | public static final String ROM_OPPO = "OPPO"; 26 | public static final String ROM_SMARTISAN = "SMARTISAN"; 27 | public static final String ROM_VIVO = "VIVO"; 28 | public static final String ROM_QIKU = "QIKU"; 29 | 30 | private static final String KEY_VERSION_MIUI = "ro.miui.ui.version.name"; 31 | private static final String KEY_VERSION_EMUI = "ro.build.version.emui"; 32 | private static final String KEY_VERSION_OPPO = "ro.build.version.opporom"; 33 | private static final String KEY_VERSION_SMARTISAN = "ro.smartisan.version"; 34 | private static final String KEY_VERSION_VIVO = "ro.vivo.os.version"; 35 | 36 | private static String sName; 37 | private static String sVersion; 38 | 39 | //华为 40 | public static boolean isEmui() { 41 | return check(ROM_EMUI); 42 | } 43 | //小米 44 | public static boolean isMiui() { 45 | return check(ROM_MIUI); 46 | } 47 | //vivo 48 | public static boolean isVivo() { 49 | return check(ROM_VIVO); 50 | } 51 | //oppo 52 | public static boolean isOppo() { 53 | return check(ROM_OPPO); 54 | } 55 | //魅族 56 | public static boolean isFlyme() { 57 | return check(ROM_FLYME); 58 | } 59 | //360手机 60 | public static boolean is360() { 61 | return check(ROM_QIKU) || check("360"); 62 | } 63 | 64 | public static boolean isSmartisan() { 65 | return check(ROM_SMARTISAN); 66 | } 67 | 68 | public static String getName() { 69 | if (sName == null) { 70 | check(""); 71 | } 72 | return sName; 73 | } 74 | 75 | public static String getVersion() { 76 | if (sVersion == null) { 77 | check(""); 78 | } 79 | return sVersion; 80 | } 81 | 82 | public static boolean check(String rom) { 83 | if (sName != null) { 84 | return sName.equals(rom); 85 | } 86 | 87 | if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_MIUI))) { 88 | sName = ROM_MIUI; 89 | } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_EMUI))) { 90 | sName = ROM_EMUI; 91 | } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_OPPO))) { 92 | sName = ROM_OPPO; 93 | } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_VIVO))) { 94 | sName = ROM_VIVO; 95 | } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_SMARTISAN))) { 96 | sName = ROM_SMARTISAN; 97 | } else { 98 | sVersion = Build.DISPLAY; 99 | if (sVersion.toUpperCase().contains(ROM_FLYME)) { 100 | sName = ROM_FLYME; 101 | } else { 102 | sVersion = Build.UNKNOWN; 103 | sName = Build.MANUFACTURER.toUpperCase(); 104 | } 105 | } 106 | return sName.equals(rom); 107 | } 108 | 109 | public static String getProp(String name) { 110 | String line = null; 111 | BufferedReader input = null; 112 | try { 113 | Process p = Runtime.getRuntime().exec("getprop " + name); 114 | input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024); 115 | line = input.readLine(); 116 | input.close(); 117 | } catch (IOException ex) { 118 | Log.e(TAG, "Unable to read prop " + name, ex); 119 | return null; 120 | } finally { 121 | if (input != null) { 122 | try { 123 | input.close(); 124 | } catch (IOException e) { 125 | e.printStackTrace(); 126 | } 127 | } 128 | } 129 | return line; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/utils/RxImage.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.utils; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.WallpaperManager; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.graphics.Bitmap; 8 | import android.net.Uri; 9 | import android.os.Environment; 10 | 11 | import com.squareup.picasso.Picasso; 12 | 13 | import java.io.File; 14 | import java.io.FileOutputStream; 15 | import java.io.IOException; 16 | 17 | import rx.Observable; 18 | import rx.Subscriber; 19 | import rx.functions.Func1; 20 | import rx.schedulers.Schedulers; 21 | 22 | /** 23 | * @author: xiaohaibin. 24 | * @time: 2018/12/26 25 | * @mail:xhb_199409@163.com 26 | * @github:https://github.com/xiaohaibin 27 | * @describe: RxImage 28 | */ 29 | public class RxImage { 30 | 31 | public static Observable saveImageAndGetPathObservable(final Context context, final String url) { 32 | return Observable.create(new Observable.OnSubscribe() { 33 | @Override 34 | public void call(Subscriber subscriber) { 35 | Bitmap bitmap = null; 36 | try { 37 | bitmap = Picasso.with(context).load(url).get(); 38 | } catch (IOException e) { 39 | subscriber.onError(e); 40 | } 41 | if (bitmap == null) { 42 | subscriber.onError(new Exception("无法下载到图片")); 43 | } 44 | subscriber.onNext(bitmap); 45 | subscriber.onCompleted(); 46 | } 47 | }).flatMap(new Func1>() { 48 | @Override 49 | public Observable call(Bitmap bitmap) { 50 | File appDir = new File(Environment.getExternalStorageDirectory(), "EnjoyLife"); 51 | if (!appDir.exists()) { 52 | appDir.mkdir(); 53 | } 54 | String fileName = System.currentTimeMillis() + ".jpg"; 55 | File file = new File(appDir, fileName); 56 | try { 57 | FileOutputStream outputStream = new FileOutputStream(file); 58 | assert bitmap != null; 59 | bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream); 60 | outputStream.flush(); 61 | outputStream.close(); 62 | } catch (IOException e) { 63 | e.printStackTrace(); 64 | } 65 | 66 | Uri uri = Uri.fromFile(file); 67 | // 通知图库更新 68 | Intent scannerIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri); 69 | context.sendBroadcast(scannerIntent); 70 | return Observable.just(uri); 71 | } 72 | }).subscribeOn(Schedulers.io()); 73 | } 74 | 75 | /** 76 | * 设置壁纸 77 | * @param context 78 | * @param url 79 | * @return 80 | */ 81 | public static Observable setWallPaper(final Context context, final String url) { 82 | return Observable.create(new Observable.OnSubscribe() { 83 | @Override 84 | public void call(Subscriber subscriber) { 85 | Bitmap bitmap = null; 86 | try { 87 | bitmap = Picasso.with(context).load(url).get(); 88 | } catch (IOException e) { 89 | subscriber.onError(e); 90 | } 91 | if (bitmap == null) { 92 | subscriber.onError(new Exception("壁纸设置失败")); 93 | } 94 | subscriber.onNext(bitmap); 95 | subscriber.onCompleted(); 96 | } 97 | }).flatMap(new Func1>() { 98 | @SuppressLint("MissingPermission") 99 | @Override 100 | public Observable call(Bitmap bitmap) { 101 | boolean isSuccess = false; 102 | WallpaperManager manager = WallpaperManager.getInstance(context); 103 | try { 104 | manager.setBitmap(bitmap); 105 | isSuccess = true; 106 | } catch (IOException e) { 107 | e.printStackTrace(); 108 | } 109 | return Observable.just(isSuccess); 110 | } 111 | }).subscribeOn(Schedulers.io()); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/utils/TripleDESUtil.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.utils; 2 | import javax.crypto.Cipher; 3 | import javax.crypto.KeyGenerator; 4 | import javax.crypto.SecretKey; 5 | import javax.crypto.spec.SecretKeySpec; 6 | 7 | /** 8 | * 3DES加密工具类 9 | */ 10 | public class TripleDESUtil { 11 | 12 | /** 13 | * 生成密钥 14 | * 15 | * @return 密钥 16 | * @throws Exception 17 | */ 18 | public static byte[] initKey() throws Exception { 19 | KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede"); 20 | keyGenerator.init(168);//密钥长度 112 168 21 | SecretKey secretKey = keyGenerator.generateKey(); 22 | return secretKey.getEncoded(); 23 | } 24 | 25 | /** 26 | * 3DES加密 27 | * 28 | * @param data 要加密的数据 29 | * @param key 加密所使用的密钥 30 | * @return 加密后的数据 31 | * @throws Exception 32 | */ 33 | public static byte[] encrypt(byte[] data, byte[] key) throws Exception { 34 | SecretKey secretKey = new SecretKeySpec(key, "DESede"); 35 | Cipher cipher = Cipher.getInstance("DESede"); 36 | cipher.init(Cipher.ENCRYPT_MODE, secretKey); 37 | return cipher.doFinal(data); 38 | } 39 | 40 | /** 41 | * 3DES解密 42 | * 43 | * @param data 加密后的数据 44 | * @param key 解密所需要的key 45 | * @return 解密后的数据 46 | * @throws Exception 47 | */ 48 | public static byte[] decrypt(byte[] data, byte[] key) throws Exception { 49 | SecretKey secretKey = new SecretKeySpec(key, "DESede"); 50 | Cipher cipher = Cipher.getInstance("DESede"); 51 | cipher.init(Cipher.DECRYPT_MODE, secretKey); 52 | return cipher.doFinal(data); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/utils/WallPaperHepler.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.utils; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.WallpaperManager; 5 | import android.content.ComponentName; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.net.Uri; 9 | import android.os.Build; 10 | import android.support.v4.content.FileProvider; 11 | 12 | import java.io.File; 13 | import java.io.IOException; 14 | 15 | /** 16 | * @author: xiaohaibin. 17 | * @time: 2018/12/27 18 | * @mail:xhb_199409@163.com 19 | * @github:https://github.com/xiaohaibin 20 | * @describe: 壁纸工具类 21 | */ 22 | public class WallPaperHepler { 23 | 24 | /** 25 | * 设置壁纸(兼容大部分手机) 26 | * @param context 27 | * @param path 28 | */ 29 | @SuppressLint("MissingPermission") 30 | private static void intent2SetWallPaper(Context context, String path) { 31 | Uri uriPath = getUriWithPath(context, path); 32 | Intent intent; 33 | if (RoomUtils.isEmui()) { 34 | try { 35 | ComponentName componentName = new ComponentName("com.android.gallery3d", "com.android.gallery3d.app.Wallpaper"); 36 | intent = new Intent(Intent.ACTION_VIEW); 37 | intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 38 | intent.setDataAndType(uriPath, "image/*"); 39 | intent.putExtra("mimeType", "image/*"); 40 | intent.setComponent(componentName); 41 | context.startActivity(intent); 42 | } catch (Exception e) { 43 | e.printStackTrace(); 44 | try { 45 | WallpaperManager.getInstance(context.getApplicationContext()).setBitmap(ImageUtils.getBitmap(path)); 46 | } catch (IOException e1) { 47 | e1.printStackTrace(); 48 | } 49 | } 50 | } else if (RoomUtils.isMiui()) { 51 | try { 52 | ComponentName componentName = new ComponentName("com.android.thememanager", "com.android.thememanager.activity.WallpaperDetailActivity"); 53 | intent = new Intent("miui.intent.action.START_WALLPAPER_DETAIL"); 54 | intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 55 | intent.setDataAndType(uriPath, "image/*"); 56 | intent.putExtra("mimeType", "image/*"); 57 | intent.setComponent(componentName); 58 | context.startActivity(intent); 59 | } catch (Exception e) { 60 | e.printStackTrace(); 61 | try { 62 | WallpaperManager.getInstance(context.getApplicationContext()).setBitmap(ImageUtils.getBitmap(path)); 63 | } catch (IOException e1) { 64 | e1.printStackTrace(); 65 | } 66 | } 67 | } else { 68 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 69 | context.startActivity(WallpaperManager.getInstance(context.getApplicationContext()) 70 | .getCropAndSetWallpaperIntent(getUriWithPath(context, path))); 71 | } else { 72 | try { 73 | WallpaperManager.getInstance(context.getApplicationContext()).setBitmap(ImageUtils.getBitmap(path)); 74 | } catch (IOException e1) { 75 | e1.printStackTrace(); 76 | } 77 | } 78 | } 79 | } 80 | 81 | private static Uri getUriWithPath(Context context, String path) { 82 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 83 | return FileProvider.getUriForFile(context, context.getPackageName() + ".provider", new File(path)); 84 | } else { 85 | return Uri.fromFile(new File(path)); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/widget/ClearEditText.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.widget; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.graphics.drawable.Drawable; 6 | import android.text.Editable; 7 | import android.text.TextWatcher; 8 | import android.util.AttributeSet; 9 | import android.view.MotionEvent; 10 | import android.view.View; 11 | import android.view.View.OnFocusChangeListener; 12 | import android.view.animation.Animation; 13 | import android.view.animation.CycleInterpolator; 14 | import android.view.animation.TranslateAnimation; 15 | import android.widget.EditText; 16 | 17 | import com.xhb.aframelibrary.R; 18 | 19 | 20 | /** 21 | * link https://xiaohaibin.github.io/ 22 | * email: xhb_199409@163.com 23 | * github: https://github.com/xiaohaibin 24 | * descibe 输入框一键清除所有的内容 25 | */ 26 | @SuppressLint("AppCompatCustomView") 27 | public class ClearEditText extends EditText implements OnFocusChangeListener, TextWatcher { 28 | /** 29 | * 删除按钮的引用 30 | */ 31 | private Drawable mClearDrawable; 32 | 33 | /** 34 | * 控件是否有焦点 35 | */ 36 | private boolean hasFoucs; 37 | 38 | public ClearEditText(Context context) { 39 | this(context, null); 40 | } 41 | 42 | public ClearEditText(Context context, AttributeSet attrs) { 43 | //这里构造方法也很重要,不加这个很多属性不能再XML里面定义 44 | this(context, attrs, android.R.attr.editTextStyle); 45 | } 46 | 47 | public ClearEditText(Context context, AttributeSet attrs, int defStyle) { 48 | super(context, attrs, defStyle); 49 | init(); 50 | } 51 | 52 | 53 | private void init() { 54 | //获取EditText的DrawableRight,假如没有设置我们就使用默认的图片 55 | mClearDrawable = getCompoundDrawables()[2]; 56 | if (mClearDrawable == null) { 57 | //throw new NullPointerException("You can add drawableRight attribute in XML"); 58 | mClearDrawable = getResources().getDrawable(R.drawable.selector_clear); 59 | } 60 | 61 | mClearDrawable.setBounds(0, 0, mClearDrawable.getIntrinsicWidth(), mClearDrawable.getIntrinsicHeight()); 62 | 63 | setClearIconVisible(false); 64 | //设置焦点改变的监听 65 | setOnFocusChangeListener(this); 66 | //设置输入框里面内容发生改变的监听 67 | addTextChangedListener(this); 68 | } 69 | 70 | /** 71 | * 因为我们不能直接给EditText设置点击事件,所以我们用记住我们按下的位置来模拟点击事件 72 | * 当我们按下的位置 在 EditText的宽度 - 图标到控件右边的间距 - 图标的宽度 和 73 | * EditText的宽度 - 图标到控件右边的间距之间我们就算点击了图标,竖直方向就没有考虑 74 | *

75 | * getWidth() - getTotalPaddingRight()表示: 76 | * 控件左边到clean的图标左边缘的区域 77 | *

78 | * getWidth() - getPaddingRight()表示: 79 | * 控件左边到clean的图标右边缘的区域 80 | *

81 | * 所以这两者之间的区域刚好是clean的图标的区域 82 | */ 83 | @Override 84 | public boolean onTouchEvent(MotionEvent event) { 85 | if (event.getAction() == MotionEvent.ACTION_UP) { 86 | if (getCompoundDrawables()[2] != null) { 87 | 88 | boolean touchable = event.getX() > (getWidth() - getTotalPaddingRight()) 89 | && (event.getX() < ((getWidth() - getPaddingRight()))); 90 | 91 | if (touchable) { //点击清除图标 92 | setText(""); 93 | } 94 | } 95 | } 96 | return super.onTouchEvent(event); 97 | } 98 | 99 | /** 100 | * 当ClearEditText焦点发生变化的时候,判断里面字符串长度设置清除图标的显示与隐藏 101 | */ 102 | @Override 103 | public void onFocusChange(View v, boolean hasFocus) { 104 | this.hasFoucs = hasFocus; 105 | if (hasFocus) { 106 | setClearIconVisible(getText().length() > 0); 107 | } else { 108 | setClearIconVisible(false); 109 | } 110 | } 111 | 112 | 113 | /** 114 | * 设置清除图标的显示与隐藏,调用setCompoundDrawables为EditText绘制上去 115 | * 116 | * @param visible 117 | */ 118 | protected void setClearIconVisible(boolean visible) { 119 | Drawable right = visible ? mClearDrawable : null; 120 | setCompoundDrawables(getCompoundDrawables()[0], 121 | getCompoundDrawables()[1], right, getCompoundDrawables()[3]); 122 | } 123 | 124 | 125 | /** 126 | * 当输入框里面内容发生变化的时候回调的方法 127 | */ 128 | @Override 129 | public void onTextChanged(CharSequence s, int start, int count, 130 | int after) { 131 | if (hasFoucs) { 132 | setClearIconVisible(s.length() > 0); 133 | } 134 | } 135 | 136 | @Override 137 | public void beforeTextChanged(CharSequence s, int start, int count, 138 | int after) { 139 | 140 | } 141 | 142 | @Override 143 | public void afterTextChanged(Editable s) { 144 | 145 | } 146 | 147 | 148 | /** 149 | * 设置晃动动画 150 | */ 151 | public void setShakeAnimation() { 152 | this.startAnimation(shakeAnimation(5)); 153 | } 154 | 155 | 156 | /** 157 | * 晃动动画 158 | * 159 | * @param counts 1秒钟晃动多少下 160 | * @return 161 | */ 162 | public static Animation shakeAnimation(int counts) { 163 | Animation translateAnimation = new TranslateAnimation(0, 10, 0, 0); 164 | translateAnimation.setInterpolator(new CycleInterpolator(counts)); 165 | translateAnimation.setDuration(1000); 166 | return translateAnimation; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/widget/CountDownButton.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.widget; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.os.CountDownTimer; 6 | import android.support.annotation.Nullable; 7 | import android.text.TextUtils; 8 | import android.util.AttributeSet; 9 | import android.widget.TextView; 10 | 11 | 12 | /** 13 | * link https://xiaohaibin.github.io/ 14 | * email: xhb_199409@163.com 15 | * github: https://github.com/xiaohaibin 16 | * descibe 短信验证码倒计时button 17 | */ 18 | @SuppressLint("AppCompatCustomView") 19 | public class CountDownButton extends TextView { 20 | 21 | private long time = 60 * 1000; 22 | 23 | private String beforeText = "获取验证码"; 24 | 25 | private String afterText = "重新获取"; 26 | 27 | private CountDownTimer timer; 28 | 29 | 30 | public CountDownButton(Context context) { 31 | super(context); 32 | initView(); 33 | } 34 | 35 | public CountDownButton(Context context, AttributeSet attrs) { 36 | super(context, attrs); 37 | initView(); 38 | } 39 | 40 | public CountDownButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 41 | super(context, attrs, defStyleAttr); 42 | initView(); 43 | } 44 | 45 | 46 | /** 47 | * 初始化操作 48 | */ 49 | private void initView() { 50 | if (!TextUtils.isEmpty(getText())) { 51 | beforeText = getText().toString().trim(); 52 | } 53 | setText(beforeText); 54 | initTimer(); 55 | } 56 | 57 | /** 58 | * 开始倒计时 59 | */ 60 | public void start() { 61 | timer.start(); 62 | setText(String.valueOf(time / 1000 + "S后重试")); 63 | } 64 | 65 | /** 66 | * 清除倒计时 67 | */ 68 | private void clearTimer() { 69 | if (timer != null) { 70 | timer.cancel(); 71 | timer = null; 72 | } 73 | } 74 | 75 | @Override 76 | protected void onDetachedFromWindow() { 77 | clearTimer(); 78 | super.onDetachedFromWindow(); 79 | } 80 | 81 | private void initTimer() { 82 | if (timer == null) { 83 | timer = new CountDownTimer(time, 1000) { 84 | @Override 85 | public void onTick(long millisUntilFinished) { 86 | setEnabled(false); 87 | setText(String.valueOf(millisUntilFinished / 1000 + "秒后重发")); 88 | } 89 | 90 | @Override 91 | public void onFinish() { 92 | setEnabled(true); 93 | setText(afterText); 94 | } 95 | }; 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/widget/CustomScrollView.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.ScrollView; 6 | 7 | /** 8 | * @author: xiaohaibin. 9 | * @time: 2018/10/22 10 | * @mail:xhb_199409@163.com 11 | * @github:https://github.com/xiaohaibin 12 | * @describe: CustomScrollView 13 | */ 14 | public class CustomScrollView extends ScrollView { 15 | 16 | private OnScollChangedListener onScollChangedListener; 17 | 18 | public CustomScrollView(Context context) { 19 | super(context); 20 | } 21 | 22 | public CustomScrollView(Context context, AttributeSet attrs) { 23 | super(context, attrs); 24 | } 25 | 26 | public CustomScrollView(Context context, AttributeSet attrs, int defStyleAttr) { 27 | super(context, attrs, defStyleAttr); 28 | } 29 | 30 | @Override 31 | protected void onScrollChanged(int l, int t, int oldl, int oldt) { 32 | super.onScrollChanged(l, t, oldl, oldt); 33 | if (onScollChangedListener != null) { 34 | onScollChangedListener.onScrollChanged(l, t, oldl, oldt); 35 | } 36 | } 37 | 38 | public void setOnScollChangedListener(OnScollChangedListener onScollChangedListener) { 39 | this.onScollChangedListener = onScollChangedListener; 40 | } 41 | 42 | public interface OnScollChangedListener { 43 | void onScrollChanged(int x, int y, int oldx, int oldy); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/widget/HidePwEditText.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.widget; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.content.res.TypedArray; 6 | import android.graphics.drawable.Drawable; 7 | import android.support.annotation.DrawableRes; 8 | import android.text.Editable; 9 | import android.text.InputType; 10 | import android.text.TextWatcher; 11 | import android.util.AttributeSet; 12 | import android.view.MotionEvent; 13 | import android.view.View; 14 | import android.widget.EditText; 15 | 16 | import com.xhb.aframelibrary.R; 17 | 18 | 19 | /** 20 | * link https://xiaohaibin.github.io/ 21 | * email: xhb_199409@163.com 22 | * github: https://github.com/xiaohaibin 23 | * descibe 自定View实现点击图标 具有隐藏/显示密码功能的EditText 24 | * 可自定义设置隐藏/显示的图标 25 | */ 26 | @SuppressLint("AppCompatCustomView") 27 | public class HidePwEditText extends EditText implements View.OnFocusChangeListener, TextWatcher { 28 | 29 | private int hideImage; 30 | private int showImage; 31 | /** 32 | * 输入框右侧图标 33 | */ 34 | private Drawable hideDrawable; 35 | /** 36 | * 控件是否有焦点 37 | */ 38 | private boolean hasFoucs; 39 | /** 40 | * 输入框内容是否隐藏 41 | */ 42 | private boolean isHide = true; 43 | 44 | private onCallBackListener listener; 45 | 46 | public HidePwEditText(Context context, AttributeSet attrs) { 47 | super(context, attrs); 48 | init(attrs); 49 | } 50 | 51 | public HidePwEditText(Context context, AttributeSet attrs, int defStyleAttr) { 52 | super(context, attrs, defStyleAttr); 53 | init(attrs); 54 | } 55 | 56 | public HidePwEditText(Context context) { 57 | super(context); 58 | } 59 | 60 | public void init(AttributeSet attrs) { 61 | hideDrawable = getCompoundDrawables()[2]; 62 | if (attrs != null) { 63 | TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.HidePwEditText); 64 | hideImage = typedArray.getResourceId(R.styleable.HidePwEditText_hideDrawable, R.drawable.eye_close3x); 65 | showImage = typedArray.getResourceId(R.styleable.HidePwEditText_showDrawable, R.drawable.eye_open3x); 66 | typedArray.recycle(); 67 | } else { 68 | hideImage = R.drawable.eye_close3x; 69 | showImage = R.drawable.eye_open3x; 70 | } 71 | //默认设置隐藏图片 72 | setHideDrawable(hideImage); 73 | //默认设置隐藏图标 74 | setHideDrawableVisible(false); 75 | //设置焦点改变的监听 76 | setOnFocusChangeListener(this); 77 | //设置输入框里面内容发生改变的监听 78 | addTextChangedListener(this); 79 | } 80 | 81 | /** 82 | * 设置右侧隐藏/显示图标 83 | * 84 | * @param resId 85 | */ 86 | private void setHideDrawable(@DrawableRes int resId) { 87 | hideDrawable = getResources().getDrawable(resId); 88 | if (hideDrawable != null) { 89 | hideDrawable.setBounds(0, 0, hideDrawable.getIntrinsicWidth(), hideDrawable.getIntrinsicHeight()); 90 | } 91 | } 92 | 93 | private void setHideDrawableVisible(boolean isVisible) { 94 | Drawable right = isVisible ? hideDrawable : null; 95 | setCompoundDrawables(getCompoundDrawables()[0], 96 | getCompoundDrawables()[1], right, getCompoundDrawables()[3]); 97 | } 98 | 99 | 100 | @Override 101 | public void onFocusChange(View v, boolean hasFocus) { 102 | this.hasFoucs = hasFocus; 103 | if (hasFocus) { 104 | setHideDrawableVisible(getText().length() > 0); 105 | } else { 106 | setHideDrawableVisible(false); 107 | } 108 | if (listener != null) { 109 | listener.onFocusChange(hasFocus); 110 | } 111 | } 112 | 113 | @Override 114 | public boolean onTouchEvent(MotionEvent event) { 115 | if (event.getAction() == MotionEvent.ACTION_UP) { 116 | boolean touchable = event.getX() > (getWidth() - getTotalPaddingRight()) 117 | && (event.getX() < ((getWidth() - getPaddingRight()))); 118 | if (touchable) { 119 | if (isHide) { 120 | setHideDrawable(hideImage); 121 | this.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); 122 | isHide = false; 123 | } else { 124 | setHideDrawable(showImage); 125 | this.setInputType(InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); 126 | isHide = true; 127 | } 128 | } 129 | } 130 | return super.onTouchEvent(event); 131 | } 132 | 133 | @Override 134 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 135 | 136 | } 137 | 138 | @Override 139 | public void afterTextChanged(Editable s) { 140 | } 141 | 142 | @Override 143 | public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { 144 | if (hasFoucs) { 145 | setHideDrawableVisible(text.length() > 0); 146 | setSelection(text.length()); 147 | } 148 | if (listener != null) { 149 | listener.onTextChange(text); 150 | } 151 | } 152 | 153 | public void setListener(onCallBackListener listener) { 154 | this.listener = listener; 155 | } 156 | 157 | /** 158 | * 接口回调,方便在输入框的事件监听中处理其他事件 159 | */ 160 | public interface onCallBackListener { 161 | void onTextChange(CharSequence s); 162 | 163 | void onFocusChange(boolean hasFocus); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/widget/IconLabelIndicatorView.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.util.AttributeSet; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import com.xhb.aframelibrary.R; 13 | 14 | 15 | /** 16 | * 带icon的label标签 17 | * 18 | * @author Mr.xiao 19 | */ 20 | public class IconLabelIndicatorView extends ViewGroup { 21 | 22 | private View contentView; 23 | private ImageView iconImageView; 24 | private TextView labelTextView; 25 | private ImageView indicatorImageView; 26 | 27 | public IconLabelIndicatorView(Context context) { 28 | super(context); 29 | init(null); 30 | } 31 | 32 | public IconLabelIndicatorView(Context context, AttributeSet attrs) { 33 | super(context, attrs); 34 | init(attrs); 35 | } 36 | 37 | @Override 38 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 39 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 40 | contentView.measure(widthMeasureSpec, heightMeasureSpec); 41 | } 42 | 43 | @Override 44 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 45 | contentView.layout(0, 0, getMeasuredWidth(), getMeasuredHeight()); 46 | } 47 | 48 | private void init(AttributeSet attrs) { 49 | contentView = LayoutInflater.from(getContext()).inflate(R.layout.layout_widget_icon_label_indicator_view, null); 50 | iconImageView = contentView.findViewById(R.id.icon); 51 | labelTextView = contentView.findViewById(R.id.label); 52 | indicatorImageView = contentView.findViewById(R.id.indicator); 53 | addView(contentView); 54 | 55 | if (attrs != null) { 56 | TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.IconLabelIndicatorView); 57 | iconImageView.setImageResource(typedArray.getResourceId(R.styleable.IconLabelIndicatorView_viewIcon, 0)); 58 | labelTextView.setText(typedArray.getString(R.styleable.IconLabelIndicatorView_viewLabel)); 59 | indicatorImageView.setImageResource(typedArray.getResourceId(R.styleable.IconLabelIndicatorView_viewIndicator, 0)); 60 | typedArray.recycle(); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/widget/IndicatorView.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.Paint; 8 | import android.graphics.RectF; 9 | import android.os.Handler; 10 | import android.os.Message; 11 | import android.util.AttributeSet; 12 | import android.view.View; 13 | import com.xhb.aframelibrary.R; 14 | 15 | 16 | /** 17 | * 圆点指示器控件 18 | */ 19 | public class IndicatorView extends View { 20 | 21 | private int indicatorColor = Color.rgb(0, 0, 0); 22 | private int indicatorColorSelected = Color.rgb(0, 0, 0); 23 | private int indicatorWidth = 0; 24 | private int gravity = 0; 25 | private int indicatorCount = 0; 26 | private int currentIndicator = 0; 27 | private Paint mPaint; 28 | 29 | private Handler handler = new Handler() { 30 | @Override 31 | public void handleMessage(Message msg) { 32 | if (msg.what == 0x12) { 33 | invalidate(); 34 | } 35 | } 36 | }; 37 | 38 | public IndicatorView(Context context) { 39 | super(context); 40 | init(); 41 | } 42 | 43 | public IndicatorView(Context context, AttributeSet attrs) { 44 | super(context, attrs); 45 | if (attrs != null) { 46 | TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.IndicatorView); 47 | indicatorColor = typedArray.getColor(R.styleable.IndicatorView_indicatorColor, Color.rgb(0, 0, 0)); 48 | indicatorColorSelected = typedArray.getColor(R.styleable.IndicatorView_indicatorColorSelected, Color.rgb(0, 0, 0)); 49 | indicatorWidth = dp2px(context, typedArray.getInt(R.styleable.IndicatorView_indicatorWidth, 0)); 50 | gravity = typedArray.getInt(R.styleable.IndicatorView_gravity, 0); 51 | typedArray.recycle(); 52 | } 53 | init(); 54 | } 55 | 56 | private void init(){ 57 | mPaint = new Paint(); 58 | mPaint.setAntiAlias(true); 59 | } 60 | 61 | @Override 62 | protected void onDraw(Canvas canvas) { 63 | int viewWidth = getWidth(); 64 | int viewHeight = getHeight(); 65 | int totalWidth = indicatorWidth * (2 * indicatorCount - 1); 66 | 67 | if (indicatorCount > 0) { 68 | for (int i = 0; i < indicatorCount; i++) { 69 | if (i == currentIndicator) { 70 | mPaint.setColor(indicatorColorSelected); 71 | } else { 72 | mPaint.setColor(indicatorColor); 73 | } 74 | int left = (viewWidth - totalWidth) / 2 + (i * 2 * indicatorWidth); 75 | switch (gravity) { 76 | case 0: 77 | left = (viewWidth - totalWidth) / 2 + (i * 2 * indicatorWidth); 78 | break; 79 | case 1: 80 | left = i * 2 * indicatorWidth; 81 | break; 82 | case 2: 83 | left = viewWidth - totalWidth + (i * 2 * indicatorWidth); 84 | break; 85 | default: 86 | break; 87 | } 88 | int top = (viewHeight - indicatorWidth) / 2; 89 | int right = left + indicatorWidth; 90 | int bottom = top + indicatorWidth; 91 | RectF rectF = new RectF(left, top, right, bottom); 92 | canvas.drawOval(rectF, mPaint); 93 | } 94 | } 95 | } 96 | 97 | public void setIndicatorCount(int indicatorCount) { 98 | this.indicatorCount = indicatorCount; 99 | } 100 | 101 | public void setCurrentIndicator(int currentIndicator) { 102 | this.currentIndicator = currentIndicator; 103 | handler.sendEmptyMessage(0x12); 104 | } 105 | 106 | public static int dp2px(Context context, final float dpValue) { 107 | final float scale = context.getResources().getDisplayMetrics().density; 108 | return (int) (dpValue * scale + 0.5f); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/widget/LabelIndicatorView.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.support.annotation.ColorRes; 6 | import android.util.AttributeSet; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.ImageView; 11 | import android.widget.TextView; 12 | 13 | import com.xhb.aframelibrary.R; 14 | 15 | 16 | /** 17 | * link https://xiaohaibin.github.io/ 18 | * email: xhb_199409@163.com 19 | * github: https://github.com/xiaohaibin 20 | * description: 自定义组合控件实现 个人中心常见标签项效果(无icon图标,只有文字) 21 | */ 22 | public class LabelIndicatorView extends ViewGroup { 23 | 24 | private View contentView; 25 | private TextView tipTextView; 26 | 27 | public LabelIndicatorView(Context context) { 28 | super(context); 29 | init(null); 30 | } 31 | 32 | public LabelIndicatorView(Context context, AttributeSet attrs) { 33 | super(context, attrs); 34 | init(attrs); 35 | } 36 | 37 | @Override 38 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 39 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 40 | contentView.measure(widthMeasureSpec, heightMeasureSpec); 41 | } 42 | 43 | @Override 44 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 45 | contentView.layout(0, 0, getMeasuredWidth(), getMeasuredHeight()); 46 | } 47 | 48 | private void init(AttributeSet attrs) { 49 | contentView = LayoutInflater.from(getContext()).inflate(R.layout.layout_widget_label_indicator_view, null); 50 | TextView labelTextView = (TextView) contentView.findViewById(R.id.label); 51 | tipTextView = (TextView) contentView.findViewById(R.id.tips); 52 | ImageView indicatorImageView = (ImageView) contentView.findViewById(R.id.indicator); 53 | addView(contentView); 54 | 55 | if (attrs != null) { 56 | TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.LabelIndicatorView); 57 | labelTextView.setText(typedArray.getString(R.styleable.LabelIndicatorView_label_title)); 58 | tipTextView.setText(typedArray.getString(R.styleable.LabelIndicatorView_label_tips)); 59 | indicatorImageView.setImageResource(typedArray.getResourceId(R.styleable.LabelIndicatorView_label_Indicator, 0)); 60 | typedArray.recycle(); 61 | } 62 | } 63 | 64 | public void setTipText(String text) { 65 | if (tipTextView != null) { 66 | tipTextView.setText(text); 67 | } 68 | } 69 | 70 | public void setTipColor(@ColorRes int color) { 71 | if (tipTextView != null) { 72 | tipTextView.setTextColor(getResources().getColor(color)); 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /aframelibrary/src/main/java/com/xhb/aframelibrary/widget/SpaceDecoration.java: -------------------------------------------------------------------------------- 1 | package com.xhb.aframelibrary.widget; 2 | 3 | import android.graphics.Rect; 4 | import android.support.v7.widget.GridLayoutManager; 5 | import android.support.v7.widget.LinearLayoutManager; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.support.v7.widget.StaggeredGridLayoutManager; 8 | import android.view.View; 9 | 10 | import static android.widget.LinearLayout.VERTICAL; 11 | 12 | /** 13 | * @author: xiaohaibin. 14 | * @time: 2018/10/25 15 | * @mail:xhb_199409@163.com 16 | * @github:https://github.com/xiaohaibin 17 | * @describe:SpaceDecoration 18 | */ 19 | public class SpaceDecoration extends RecyclerView.ItemDecoration { 20 | 21 | private int space; 22 | private boolean mPaddingEdgeSide = true; 23 | private boolean mPaddingStart = true; 24 | private boolean mPaddingHeaderFooter = false; 25 | 26 | 27 | public SpaceDecoration(int space) { 28 | this.space = space ; 29 | } 30 | 31 | public void setPaddingEdgeSide(boolean mPaddingEdgeSide) { 32 | this.mPaddingEdgeSide = mPaddingEdgeSide; 33 | } 34 | 35 | public void setPaddingStart(boolean mPaddingStart) { 36 | this.mPaddingStart = mPaddingStart; 37 | } 38 | 39 | public void setPaddingHeaderFooter(boolean mPaddingHeaderFooter) { 40 | this.mPaddingHeaderFooter = mPaddingHeaderFooter; 41 | } 42 | 43 | @Override 44 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 45 | int position = parent.getChildAdapterPosition(view); 46 | int spanCount = 0; 47 | int orientation = 0; 48 | int spanIndex = 0; 49 | int headerCount = 0,footerCount = 0; 50 | 51 | RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); 52 | if (layoutManager instanceof StaggeredGridLayoutManager){ 53 | orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation(); 54 | spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount(); 55 | spanIndex = ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex(); 56 | }else if (layoutManager instanceof GridLayoutManager){ 57 | orientation = ((GridLayoutManager) layoutManager).getOrientation(); 58 | spanCount = ((GridLayoutManager) layoutManager).getSpanCount(); 59 | spanIndex = ((GridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex(); 60 | }else if (layoutManager instanceof LinearLayoutManager){ 61 | orientation = ((LinearLayoutManager) layoutManager).getOrientation(); 62 | spanCount = 1; 63 | spanIndex = 0; 64 | } 65 | 66 | /** 67 | * 普通Item的尺寸 68 | */ 69 | if ((position>=headerCount&&position 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/anim/push_bottom_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/anim/slide_in_from_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/anim/slide_out_to_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable-hdpi/dialog_toast_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/aframelibrary/src/main/res/drawable-hdpi/dialog_toast_bg.png -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable-hdpi/keyboard_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/aframelibrary/src/main/res/drawable-hdpi/keyboard_delete.png -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable-hdpi/keyboard_shift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/aframelibrary/src/main/res/drawable-hdpi/keyboard_shift.png -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable-hdpi/keyboard_shift_c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/aframelibrary/src/main/res/drawable-hdpi/keyboard_shift_c.png -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable-hdpi/keyboard_space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/aframelibrary/src/main/res/drawable-hdpi/keyboard_space.png -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable-xhdpi/icon_clear_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/aframelibrary/src/main/res/drawable-xhdpi/icon_clear_normal.png -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable-xhdpi/icon_clear_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/aframelibrary/src/main/res/drawable-xhdpi/icon_clear_pressed.png -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable-xhdpi/icon_notify_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/aframelibrary/src/main/res/drawable-xhdpi/icon_notify_done.png -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable-xhdpi/icon_notify_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/aframelibrary/src/main/res/drawable-xhdpi/icon_notify_error.png -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable-xxhdpi/eye_close3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/aframelibrary/src/main/res/drawable-xxhdpi/eye_close3x.png -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable-xxhdpi/eye_open3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/aframelibrary/src/main/res/drawable-xxhdpi/eye_open3x.png -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable-xxhdpi/icon_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/aframelibrary/src/main/res/drawable-xxhdpi/icon_next.png -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable-xxhdpi/icon_notify_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/aframelibrary/src/main/res/drawable-xxhdpi/icon_notify_done.png -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable-xxhdpi/icon_notify_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/aframelibrary/src/main/res/drawable-xxhdpi/icon_notify_error.png -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable-xxhdpi/icon_notify_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/aframelibrary/src/main/res/drawable-xxhdpi/icon_notify_info.png -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable/icon_notify_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/aframelibrary/src/main/res/drawable/icon_notify_info.png -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable/keyboard_key.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable/keyboard_key_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable/keyboard_key_bg_c.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/drawable/selector_clear.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/layout/easy_progress_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 20 | 21 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/layout/gs_keyboard.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 24 | 25 | 26 | 33 | 34 | 44 | 45 | 55 | 56 | 66 | 67 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/layout/layout_widget_icon_label_indicator_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 23 | 24 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/layout/layout_widget_label_indicator_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 17 | 18 | 23 | 24 | 31 | 32 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #1c1c1c 5 | #FF6347 6 | #888888 7 | 8 | #ededed 9 | 10 | #f3f3f3 11 | #ffffff 12 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 48dp 4 | 40dp 5 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/values/ids_sticky_nav_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AFrameLibrary 3 | abcdefghijklmnopqrstuvwxyz 4 | 1234567890 5 | 6 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 26 | 27 | 34 | 35 | 39 | 40 | 43 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/xml/gs_keyboard_english.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 40 | 41 | 42 | 47 | 50 | 53 | 56 | 59 | 62 | 65 | 68 | 72 | 73 | 74 | 81 | 84 | 87 | 90 | 93 | 96 | 99 | 102 | 108 | 109 | 110 | 111 | 115 | 120 | 121 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/xml/gs_keyboard_english_land.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 40 | 41 | 42 | 47 | 50 | 53 | 56 | 59 | 62 | 65 | 68 | 72 | 73 | 74 | 81 | 84 | 87 | 90 | 93 | 96 | 99 | 102 | 108 | 109 | 110 | 111 | 115 | 120 | 121 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/xml/gs_keyboard_number.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 14 | 17 | 18 | 19 | 20 | 23 | 26 | 29 | 30 | 31 | 34 | 37 | 41 | 42 | 43 | 46 | 49 | 52 | 53 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/xml/gs_keyboard_number_land.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 14 | 17 | 18 | 19 | 22 | 25 | 28 | 29 | 30 | 33 | 36 | 40 | 41 | 42 | 45 | 48 | 51 | 52 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/xml/gs_keyboard_symbols_shift.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 37 | 38 | 39 | 43 | 46 | 49 | 52 | 55 | 58 | 61 | 64 | 68 | 69 | 70 | 71 | 74 | 77 | 80 | 83 | 86 | 89 | 92 | 98 | 99 | 100 | 101 | 104 | 107 | 110 | 113 | 116 | 119 | 122 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/xml/gs_keyboard_symbols_shift_land.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 37 | 38 | 39 | 43 | 46 | 49 | 52 | 55 | 58 | 61 | 64 | 68 | 69 | 70 | 71 | 74 | 77 | 80 | 83 | 86 | 89 | 92 | 98 | 99 | 100 | 101 | 104 | 107 | 110 | 113 | 116 | 119 | 122 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /aframelibrary/src/main/res/xml/provider_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.android["compileSdkVersion"] 5 | defaultConfig { 6 | applicationId "com.stx.xhb.aframe" 7 | minSdkVersion rootProject.ext.android["minSdkVersion"] 8 | targetSdkVersion rootProject.ext.android["targetSdkVersion"] 9 | versionCode rootProject.ext.android["versionCode"] 10 | versionName rootProject.ext.android["versionName"] 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(include: ['*.jar'], dir: 'libs') 23 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 24 | testImplementation 'junit:junit:4.12' 25 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 26 | annotationProcessor(rootProject.ext.dependencies["butterknife-compiler"]) { 27 | exclude module: 'support-annotations' 28 | } 29 | implementation project(':aframelibrary') 30 | } 31 | -------------------------------------------------------------------------------- /app/libs/aframelibrary-release.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohaibin/AFrame/8a8580035c78591bb653875cdfaf09bf3917e8d8/app/libs/aframelibrary-release.aar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/stx/xhb/aframe/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.stx.xhb.aframe; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.stx.xhb.aframe", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/stx/xhb/aframe/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.stx.xhb.aframe; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | 7 | import com.blankj.utilcode.util.LogUtils; 8 | import com.xhb.aframelibrary.base.BaseActivity; 9 | import com.xhb.aframelibrary.utils.AESUtil; 10 | 11 | import butterknife.OnClick; 12 | 13 | 14 | /** 15 | * @author Mr.xiao 16 | */ 17 | public class MainActivity extends BaseActivity { 18 | 19 | @Override 20 | protected int getLayoutId() { 21 | return R.layout.activity_main; 22 | } 23 | 24 | @Override 25 | protected void initData(Bundle bundle) { 26 | String helloWorld = AESUtil.encrypt("{hello world}"); 27 | Log.i("==encrypt",helloWorld); 28 | String decrypt = AESUtil.decrypt(helloWorld); 29 | Log.i("==decrypt",decrypt); 30 | } 31 | 32 | @Override 33 | protected void initView() { 34 | 35 | } 36 | 37 | @OnClick(R.id.btn) 38 | public void onViewClicked() { 39 | // DialogMaker.showProgressDialog(this, "加载中..."); 40 | startActivity(new Intent(this,TranscetActivity.class)); 41 | } 42 | 43 | 44 | // @Override 45 | // protected void setStatusBar() { 46 | // StatusBarUtil.setColor(this, ContextCompat.getColor(this, R.color.colorPrimary)); 47 | // } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/stx/xhb/aframe/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.stx.xhb.aframe; 2 | 3 | import android.app.Application; 4 | 5 | import com.xhb.aframelibrary.http.HttpManager; 6 | 7 | /** 8 | * @author: xiaohaibin. 9 | * @time: 2018/10/19 10 | * @mail:xhb_199409@163.com 11 | * @github:https://github.com/xiaohaibin 12 | * @describe: 13 | */ 14 | public class MyApplication extends Application { 15 | @Override 16 | public void onCreate() { 17 | super.onCreate(); 18 | HttpManager.getInstance().init("",BuildConfig.DEBUG); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/stx/xhb/aframe/TranscetActivity.java: -------------------------------------------------------------------------------- 1 | package com.stx.xhb.aframe; 2 | 3 | import android.content.DialogInterface; 4 | import android.os.Bundle; 5 | 6 | import com.xhb.aframelibrary.base.BaseActivity; 7 | 8 | /** 9 | * @author: xiaohaibin. 10 | * @time: 2018/10/23 11 | * @mail:xhb_199409@163.com 12 | * @github:https://github.com/xiaohaibin 13 | * @describe: 14 | */ 15 | public class TranscetActivity extends BaseActivity { 16 | 17 | @Override 18 | protected int getLayoutId() { 19 | return R.layout.activity_transcet; 20 | } 21 | 22 | @Override 23 | protected void initData(Bundle bundle) { 24 | android.support.v7.app.AlertDialog dialog = new android.support.v7.app.AlertDialog.Builder(this) 25 | .setTitle("警告") 26 | .setMessage("需要必要的权限才可以正常使用该功能,您已拒绝获得该权限。 如果需要重新授权,您可以点击“允许”按钮进入系统设置进行授权") 27 | .setNegativeButton("取消", new DialogInterface.OnClickListener() { 28 | @Override 29 | public void onClick(DialogInterface dialog, int which) { 30 | } 31 | }) 32 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 33 | @Override 34 | public void onClick(DialogInterface dialog, int which) { 35 | } 36 | }).show(); 37 | dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { 38 | @Override 39 | public void onDismiss(DialogInterface dialog) { 40 | finish(); 41 | } 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 16 | 21 | 26 | 31 | 36 | 41 | 46 | 51 | 56 | 61 | 66 | 71 | 76 | 81 | 86 | 91 | 96 | 101 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 156 | 161 | 166 | 171 | 172 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 14 |