From 8f194481afc66528a308043732eeec2027c27a40 Mon Sep 17 00:00:00 2001 From: Administrator <981964879@qq.com> Date: Sat, 30 Nov 2019 18:38:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=81=87=E5=A6=82=E7=AE=A1=E6=8E=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 37 +- app/src/keys/SchoolEbaiFeng.jks | Bin 2193 -> 0 bytes app/src/keys/xueshibaoos.jks | Bin 0 -> 2552 bytes app/src/main/AndroidManifest.xml | 198 ++++++- .../com/appstore/uiui/IMyAidlInterface.aidl | 13 - .../appstore/uiui/KeepAliveConnection.aidl | 7 + .../java/com/appstore/uiui/MyApplication.java | 300 +++++++++- .../appstore/uiui/TextCode/MessageWhat.java | 6 + .../uiui/activity/DetailsActivity.java | 116 ++++ .../activity/DownloadManagerActivity.java | 68 +++ .../uiui/activity/KindDetailActivity.java | 58 ++ .../uiui/activity/LocalManagerActivity.java | 132 +++++ .../appstore/uiui/activity/MainActivity.java | 274 +++++++-- .../com/appstore/uiui/adapter/AppAdapter.java | 210 +++++-- .../uiui/adapter/DownloadManagerAdapter.java | 252 +++++++++ .../appstore/uiui/adapter/ImageAdapter.java | 56 ++ .../appstore/uiui/adapter/KindAdapter.java | 25 +- .../uiui/adapter/LocalAppAdapter.java | 63 +-- .../uiui/adapter/ShowImageAdapter.java | 56 ++ .../uiui/adapter/UpdateAppAdapter.java | 195 +++++++ .../com/appstore/uiui/base/BaseFragment.java | 24 +- .../java/com/appstore/uiui/base/UserInfo.java | 123 ++++ .../java/com/appstore/uiui/bean/AppInfo.java | 12 +- .../java/com/appstore/uiui/bean/LocalApp.java | 12 +- .../com/appstore/uiui/bean/UpdateAppInfo.java | 90 +++ .../uiui/fragment/FeaturedFragment.java | 21 +- .../appstore/uiui/fragment/KindFragment.java | 44 +- .../uiui/fragment/ManageFragment.java | 140 +++-- .../appstore/uiui/fragment/RankFragment.java | 2 +- .../uiui/helper/CustomSnapHelper.java | 82 +++ .../com/appstore/uiui/jpush/ExampleUtil.java | 133 +++++ .../jpush/Invalid/ExampleApplication.java | 25 + .../uiui/jpush/Invalid/MainActivity.java | 181 ++++++ .../uiui/jpush/Invalid/PushSetActivity.java | 265 +++++++++ .../uiui/jpush/Invalid/SettingActivity.java | 197 +++++++ .../uiui/jpush/Invalid/TestActivity.java | 32 ++ .../uiui/jpush/LocalBroadcastManager.java | 263 +++++++++ .../java/com/appstore/uiui/jpush/Logger.java | 40 ++ .../uiui/jpush/MyJPushMessageReceiver.java | 44 ++ .../com/appstore/uiui/jpush/MyReceiver.java | 129 +++++ .../com/appstore/uiui/jpush/PushService.java | 7 + .../uiui/jpush/TagAliasOperatorHelper.java | 338 +++++++++++ .../uiui/listener/LogDownloadListener.java | 63 +++ .../com/appstore/uiui/network/OKGOPost.java | 217 +++++++- .../com/appstore/uiui/network/URLs/Url.java | 8 + .../uiui/receiver/AppManagerReceiver.java | 53 ++ .../appstore/uiui/receiver/BootReceiver.java | 27 + .../appstore/uiui/service/GuardService.java | 22 +- .../uiui/service/InitJpushServer.java | 16 + .../uiui/service/MyDownloadService.java | 304 ---------- .../appstore/uiui/service/StepService.java | 7 +- .../com/appstore/uiui/utils/ApkUtils.java | 56 +- .../java/com/appstore/uiui/utils/SPUtils.java | 202 +++++++ .../uiui/utils/ServiceAliveUtils.java | 2 +- .../com/appstore/uiui/utils/ToastUtil.java | 22 +- .../appstore/uiui/view/NumberProgressBar.java | 524 ++++++++++++++++++ app/src/main/res/drawable/bt_delete.png | Bin 0 -> 3349 bytes app/src/main/res/drawable/icon_biology.png | Bin 0 -> 10406 bytes app/src/main/res/drawable/icon_chemistry.png | Bin 0 -> 8774 bytes app/src/main/res/drawable/icon_english.png | Bin 0 -> 8709 bytes app/src/main/res/drawable/icon_game.png | Bin 0 -> 5089 bytes app/src/main/res/drawable/icon_geography.png | Bin 0 -> 11735 bytes app/src/main/res/drawable/icon_history.png | Bin 0 -> 7192 bytes app/src/main/res/drawable/icon_language.png | Bin 0 -> 8898 bytes app/src/main/res/drawable/icon_math.png | Bin 0 -> 9954 bytes app/src/main/res/drawable/icon_physics.png | Bin 0 -> 11698 bytes app/src/main/res/drawable/icon_politics.png | Bin 0 -> 5070 bytes app/src/main/res/layout/activity_details.xml | 179 ++++++ .../res/layout/activity_download_manager.xml | 49 ++ .../main/res/layout/activity_kind_detail.xml | 53 ++ .../res/layout/activity_local_manager.xml | 57 ++ app/src/main/res/layout/fragment_manage.xml | 12 +- app/src/main/res/layout/item_app.xml | 22 +- .../main/res/layout/item_download_manager.xml | 123 ++++ app/src/main/res/layout/item_image.xml | 5 +- app/src/main/res/layout/item_local_app.xml | 17 +- app/src/main/res/values/attrs.xml | 80 +++ app/src/main/res/xml/file_paths.xml | 9 + 78 files changed, 5707 insertions(+), 692 deletions(-) delete mode 100644 app/src/keys/SchoolEbaiFeng.jks create mode 100644 app/src/keys/xueshibaoos.jks delete mode 100644 app/src/main/aidl/com/appstore/uiui/IMyAidlInterface.aidl create mode 100644 app/src/main/aidl/com/appstore/uiui/KeepAliveConnection.aidl create mode 100644 app/src/main/java/com/appstore/uiui/TextCode/MessageWhat.java create mode 100644 app/src/main/java/com/appstore/uiui/activity/DetailsActivity.java create mode 100644 app/src/main/java/com/appstore/uiui/activity/DownloadManagerActivity.java create mode 100644 app/src/main/java/com/appstore/uiui/activity/KindDetailActivity.java create mode 100644 app/src/main/java/com/appstore/uiui/activity/LocalManagerActivity.java create mode 100644 app/src/main/java/com/appstore/uiui/adapter/DownloadManagerAdapter.java create mode 100644 app/src/main/java/com/appstore/uiui/adapter/ImageAdapter.java create mode 100644 app/src/main/java/com/appstore/uiui/adapter/ShowImageAdapter.java create mode 100644 app/src/main/java/com/appstore/uiui/adapter/UpdateAppAdapter.java create mode 100644 app/src/main/java/com/appstore/uiui/base/UserInfo.java create mode 100644 app/src/main/java/com/appstore/uiui/bean/UpdateAppInfo.java create mode 100644 app/src/main/java/com/appstore/uiui/helper/CustomSnapHelper.java create mode 100644 app/src/main/java/com/appstore/uiui/jpush/ExampleUtil.java create mode 100644 app/src/main/java/com/appstore/uiui/jpush/Invalid/ExampleApplication.java create mode 100644 app/src/main/java/com/appstore/uiui/jpush/Invalid/MainActivity.java create mode 100644 app/src/main/java/com/appstore/uiui/jpush/Invalid/PushSetActivity.java create mode 100644 app/src/main/java/com/appstore/uiui/jpush/Invalid/SettingActivity.java create mode 100644 app/src/main/java/com/appstore/uiui/jpush/Invalid/TestActivity.java create mode 100644 app/src/main/java/com/appstore/uiui/jpush/LocalBroadcastManager.java create mode 100644 app/src/main/java/com/appstore/uiui/jpush/Logger.java create mode 100644 app/src/main/java/com/appstore/uiui/jpush/MyJPushMessageReceiver.java create mode 100644 app/src/main/java/com/appstore/uiui/jpush/MyReceiver.java create mode 100644 app/src/main/java/com/appstore/uiui/jpush/PushService.java create mode 100644 app/src/main/java/com/appstore/uiui/jpush/TagAliasOperatorHelper.java create mode 100644 app/src/main/java/com/appstore/uiui/listener/LogDownloadListener.java create mode 100644 app/src/main/java/com/appstore/uiui/receiver/AppManagerReceiver.java create mode 100644 app/src/main/java/com/appstore/uiui/receiver/BootReceiver.java create mode 100644 app/src/main/java/com/appstore/uiui/service/InitJpushServer.java create mode 100644 app/src/main/java/com/appstore/uiui/utils/SPUtils.java create mode 100644 app/src/main/java/com/appstore/uiui/view/NumberProgressBar.java create mode 100644 app/src/main/res/drawable/bt_delete.png create mode 100644 app/src/main/res/drawable/icon_biology.png create mode 100644 app/src/main/res/drawable/icon_chemistry.png create mode 100644 app/src/main/res/drawable/icon_english.png create mode 100644 app/src/main/res/drawable/icon_game.png create mode 100644 app/src/main/res/drawable/icon_geography.png create mode 100644 app/src/main/res/drawable/icon_history.png create mode 100644 app/src/main/res/drawable/icon_language.png create mode 100644 app/src/main/res/drawable/icon_math.png create mode 100644 app/src/main/res/drawable/icon_physics.png create mode 100644 app/src/main/res/drawable/icon_politics.png create mode 100644 app/src/main/res/layout/activity_details.xml create mode 100644 app/src/main/res/layout/activity_download_manager.xml create mode 100644 app/src/main/res/layout/activity_kind_detail.xml create mode 100644 app/src/main/res/layout/activity_local_manager.xml create mode 100644 app/src/main/res/layout/item_download_manager.xml create mode 100644 app/src/main/res/values/attrs.xml create mode 100644 app/src/main/res/xml/file_paths.xml diff --git a/app/build.gradle b/app/build.gradle index 3cb11e5..443bbfc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,7 +1,7 @@ apply plugin: 'com.android.application' def appName() { - return "应用市场" + return "UIUIAPPStore" } def releaseTime() { @@ -18,6 +18,18 @@ android { versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + //极光 + ndk { + //选择要添加的对应 cpu 类型的 .so 库。 + abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a' + // 还可以添加 'x86', 'x86_64', 'mips', 'mips64' + } + + manifestPlaceholders = [ + JPUSH_PKGNAME: applicationId, + JPUSH_APPKEY : "d7ad4da7d65687b32cacbdb1", //JPush 上注册的包名对应的 Appkey. + JPUSH_CHANNEL: "developer-default", //暂时填写默认值即可. + ] } lintOptions { checkReleaseBuilds false @@ -25,16 +37,16 @@ android { //签名 signingConfigs { debug { - storeFile file("src/keys/SchoolEbaiFeng.jks") + storeFile file("src/keys/xueshibaoos.jks") storePassword "123456" - keyAlias "key0" + keyAlias "xueshibaoos" keyPassword "123456" v2SigningEnabled false } release {// 签名文件 - storeFile file("src/keys/SchoolEbaiFeng.jks") + storeFile file("src/keys/xueshibaoos.jks") storePassword "123456" - keyAlias "key0" + keyAlias "xueshibaoos" keyPassword "123456" v2SigningEnabled false } @@ -98,13 +110,16 @@ dependencies { implementation 'com.github.bumptech.glide:glide:4.10.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0' - +//aria implementation 'com.arialyy.aria:core:3.7.7' annotationProcessor 'com.arialyy.aria:compiler:3.7.7' //OKGO - implementation 'com.lzy.net:okgo:2.1.4' - implementation 'com.lzy.net:okrx:0.1.2' - implementation 'com.lzy.net:okserver:1.1.3' + //必须使用 + implementation 'com.lzy.net:okgo:3.0.4' + //以下三个选择添加,okrx和okrx2不能同时使用 + implementation 'com.lzy.net:okrx:1.0.2' +// compile 'com.lzy.net:okrx2:2.0.2' + implementation 'com.lzy.net:okserver:2.0.5' implementation 'com.alibaba:fastjson:1.2.21' @@ -123,4 +138,8 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit:1.1.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' implementation 'androidx.cardview:cardview:1.0.0' + //极光推送 + implementation 'cn.jiguang.sdk:jpush:3.4.1' // 此处以JPush 3.4.1 版本为例。 + implementation 'cn.jiguang.sdk:jcore:2.2.4' // 此处以JCore 2.2.4 版本为例。 + } diff --git a/app/src/keys/SchoolEbaiFeng.jks b/app/src/keys/SchoolEbaiFeng.jks deleted file mode 100644 index 367b25e9e78904cc52faa1d0402a00ebfb1fe3d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2193 zcmcK4`8U*!8UXO`cV>)bEV;-|k`|hL$2tm&h6tNi+tNLTIds>LsGG zXOJu*VzNfElx>tHOZM$LoqNxD`vdNI&hx|Pr{_G&d3NV^=KuhJ?I-v<5gsnV&_2lZ zhxHW!fDMEwh#iO*$)$h<5uiMb9|YL|1O@S3g5)|o8Oa^E-I+LrTpS9mrB)p2d!NUx zdZ*#zcwz}mDrMMSsi7}@Dfn7-n_JK^DQ$1f(vypOOluVr)epC6b&;5)TD|h;MOClv z=N$i}o|CBF)da6qKshEAblf=|&t1nYJP2(9AUHY+Id~=lFxXmTI|Laj8|u>{L=?yoWptV z%(jU~g5;*LQ~jqq&7X2yj$aVA*m~qU%)y~-V~T$X5Q;Rw!u4tNGPl~S^E4Nyj_Rn1 zNlVWM#*zq-YgI3}j8Gp=EmhOkmanMTFuS&~7d#tH$KK;4nf1i#_j#uI=n2ihh+pS= zm%4#ct(oR*+;a;rR(e6lfy}>sglRDlMIviKQpSt-GYJP1Q`FKR0N< z4ETq1>y|K-cO?mNd$`8YW3e%PO$_+I7ZcE8454%*6_GUiVbLp+ZNx-tA>zFtZ{hW$F;u6G*7H zU>O&G>nO|8NjvxWfWgsrFC!#cLy3>Zv zDF~NfsZ=O;n?|$3`a3(vP9!s-@`((K8zb@^FDLNGN%Y#wzLnRm^w9@D-|e`|G@KOV zta29=r#?b%Mv(}lqUu^8?wpKaS%TMWLM3x&OLr^5HdHZDHydxFt*?N@5u5UIJPn16 z^NJnSi%{f^jMn^zC#H4DK9^F)X7|n*ysF+dzy`3V#}$L-HS^!QEGx2R2g|}n+tf6! z%(UOHY&{(`ET+o8ZM6-lI{O9OhvlwT#}uRTt6?MJPYse4X>v82w70GeXWA2WF<;)V z2aMWVi)fwP86bFf?|$|K>yJqRq3_R-5X2`!RE)Cv!=Rr|J-%_a{RdC%O11Nh+?S+X zxm~bO*WITeC-P!d0PlizzA}EaY?f%?V^k(->|<6cy{6fPI&ZonG*fi}yHw+z^-WM^GKRhQ!snD8iDBqz;=5=rb05l@r0+!Q+M!%nr*BrfEeUNd26+*-f#i{#vKS#5l3!f$5crjnf_ zy4T#X$^!`f{#7z4A;s52S7}Atx<1_D1N}-eY51|r0dB3)Ib(q`6Ac$fE0a_37-#J- zjN=1gn{v}{a#|8eSIzx39c7wfB!$7sP$#8%v7xBB8BZ+!_giEmo>GN00054IC~!lF z0#nR}!$24ep{d`2hj@^j@=-=nYdj!`Vgn#c3^&AugcA@*0XRlU`G4a0L+pQu>kpy+ z5d60wDPbOrI)vR14U9TQ#a5U{MQI;O|KA&ah|0?< zC_PKLVIRxV{q;&;s-)~J8I$=_^z%o*UC>Dk7kWUk?%T616fHW@G!ndEZo9K%vBPTaR=yP*a&L52r=_b;-<-AD3b& zt@8f>YegTX@x+}%F?za}Ew1Oi=Rbg7Jz$h-d(}N^b&sw+X=@*)l*1PfQ12S4EpJ6a|vWtkhDyM>5*1^*o4{*CMp5d=Jig&YV3dOY<C zV3}2~_+xyM2(dDtsq{g7gjn4hhfp^~0omqD5)KMctn?en13}V{0=IOde>nJRHRhMx ztuDW9L@;wh#vM}ZBS*M@^B94dbX@>gz4@`5431wC@9w;N>yMplG@knV;(2>JlAJv} zFoOuDdaN`QdOmX+SdCWahRDX&)kjwx2ZgT-Zmav_DvW*LcP9yfQ4)zk&uCGalgpNK zR$;>T%H-Ejkj)Muqu;0{E#*u5zowagJz=}yo^a>Kk(ygnK4~N`dLuxb-VpF+XN%}6 zP(k$#W<7|>l!&zzw_B$*ifr3U=@5rWSn6m2Z>AbBjZLz)<0VlQ*^<`^bKS0re~pD^ GjQ#_6+s8Ek diff --git a/app/src/keys/xueshibaoos.jks b/app/src/keys/xueshibaoos.jks new file mode 100644 index 0000000000000000000000000000000000000000..b94a626a1e59b41e0d2b0e1725c1d164b6a8f5ab GIT binary patch literal 2552 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 h=~IOHotm-=tG={6OY~?wV#1*=AWE - - - - - + xmlns:tools="http://schemas.android.com/tools" + package="com.appstore.uiui" + android:sharedUserId="android.uid.system"> + + + + + + + + + + + + + + + + + + + + + android:theme="@style/AppTheme" + tools:ignore="GoogleAppIndexingWarning"> + + + + + + @@ -23,6 +51,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/aidl/com/appstore/uiui/IMyAidlInterface.aidl b/app/src/main/aidl/com/appstore/uiui/IMyAidlInterface.aidl deleted file mode 100644 index 7cd4693..0000000 --- a/app/src/main/aidl/com/appstore/uiui/IMyAidlInterface.aidl +++ /dev/null @@ -1,13 +0,0 @@ -// IMyAidlInterface.aidl -package com.appstore.uiui; - -// Declare any non-default types here with import statements - -interface IMyAidlInterface { - /** - * Demonstrates some basic types that you can use as parameters - * and return values in AIDL. - */ - void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, - double aDouble, String aString); -} diff --git a/app/src/main/aidl/com/appstore/uiui/KeepAliveConnection.aidl b/app/src/main/aidl/com/appstore/uiui/KeepAliveConnection.aidl new file mode 100644 index 0000000..06d1fa9 --- /dev/null +++ b/app/src/main/aidl/com/appstore/uiui/KeepAliveConnection.aidl @@ -0,0 +1,7 @@ +// KeepAliveConnection.aidl +package com.appstore.uiui; + +// Declare any non-default types here with import statements + +interface KeepAliveConnection { +} diff --git a/app/src/main/java/com/appstore/uiui/MyApplication.java b/app/src/main/java/com/appstore/uiui/MyApplication.java index 1357d49..17de47a 100644 --- a/app/src/main/java/com/appstore/uiui/MyApplication.java +++ b/app/src/main/java/com/appstore/uiui/MyApplication.java @@ -3,15 +3,22 @@ package com.appstore.uiui; import android.app.Application; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.os.Handler; import android.os.Looper; +import android.provider.Settings; import android.util.Log; import androidx.annotation.NonNull; +import com.appstore.uiui.jpush.Logger; +import com.appstore.uiui.utils.ApkUtils; +import com.appstore.uiui.utils.LogUtils; import com.appstore.uiui.utils.ToastUtil; -import com.arialyy.aria.core.Aria; import com.lzy.okgo.OkGo; +import com.lzy.okgo.callback.FileCallback; +import com.lzy.okgo.model.Progress; +import com.lzy.okgo.model.Response; import com.scwang.smartrefresh.layout.SmartRefreshLayout; import com.scwang.smartrefresh.layout.api.DefaultRefreshFooterCreator; import com.scwang.smartrefresh.layout.api.DefaultRefreshHeaderCreator; @@ -21,11 +28,22 @@ import com.scwang.smartrefresh.layout.api.RefreshLayout; import com.scwang.smartrefresh.layout.footer.ClassicsFooter; import com.scwang.smartrefresh.layout.header.ClassicsHeader; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; + +import cn.jpush.android.api.CustomMessage; +import cn.jpush.android.api.JPushInterface; + /** * Created by asus on 2017/10/27. */ public class MyApplication extends Application { + private static final String TAG = "JIGUANG-Example"; + + private static MyApplication app; public static MyApplication getInstance() { @@ -56,12 +74,18 @@ public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); + Logger.d(TAG, "[ExampleApplication] onCreate"); + JPushInterface.setDebugMode(true); // 设置开启日志,发布时请关闭日志 + JPushInterface.init(this); // 初始化 JPush app = this; ToastUtil.init(this); - OkGo.getInstance().init(this); + OkGo.getInstance().init(this) + .setRetryCount(10)//重试次数 + ; + // AppUtil.getInstalledApp(this); - Aria.init(this); - Aria.download(this).register(); +// Aria.init(this); +// Aria.download(this).register(); if (!isDebug(this)) { catchException(); } @@ -77,7 +101,7 @@ public class MyApplication extends Application { Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) { - Log.d("捕获异常子线程:", Thread.currentThread().getName() + + LogUtils.d("捕获异常子线程:", Thread.currentThread().getName() + "在:" + e.getStackTrace()[0].getClassName()); } } @@ -90,14 +114,274 @@ public class MyApplication extends Application { try { Looper.loop(); //会先执行这个方法,然后在执行下面的异常捕获方法! } catch (Exception e) { - Log.d("捕获异常主线程:", Thread.currentThread().getName() + "在:" + e.getStackTrace()[0].getClassName()); + LogUtils.d("捕获异常主线程:", Thread.currentThread().getName() + "在:" + e.getStackTrace()[0].getClassName()); e.printStackTrace(); } } } }); } - public static Context getAppContext() { - return getAppContext(); + + public Context getAppContext() { + return getApplicationContext(); } + + //定义接收极光推送消息的类型 + // 3.数据线传输管控 + // 4.TF卡管控 + // 5.蓝牙管控 + // 6.浏览器上网管控 + // 7.应用联网管控 + // 8.应用锁管控 + // 9.强制安装应用 + // 10.强制卸载应用 + + private static final String JIGUANG_USB_STATE = "3"; + private static final String JIGUANG_TFCARD_STATE = "4"; + private static final String JIGUANG_BLUETOOTH_STATE = "5"; + private static final String JIGUANG_BROWSER_URLPATH = "6"; + private static final String JIGUANG_APP_NETWORKSTATE = "7"; + private static final String JIGUANG_APP_LOCKEDSTATE = "8"; + private static final String JIGUANG_FORCE_INSTALLAPK = "9"; + private static final String JIGUANG_FORCE_UNINSTALLAPK = "10"; + + synchronized public void manageCustomMessage(CustomMessage customMessage) { + if (customMessage == null) { + LogUtils.e("jiguang", "customMessage is NULL"); + } else { + String MESSAGE = customMessage.message; + //MESSAGE用作判断 + String TITLE = customMessage.title; + String CONTENT_TYPE = customMessage.contentType; + String EXTRA = customMessage.extra; + LogUtils.e("EXTRA", EXTRA); + switch (MESSAGE) { + case JIGUANG_USB_STATE: + setUsbState(EXTRA); + break; + case JIGUANG_TFCARD_STATE: + setTfcardState(EXTRA); + break; + case JIGUANG_BLUETOOTH_STATE: + setBluetoothState(EXTRA); + break; + case JIGUANG_BROWSER_URLPATH: + setBrowserUrlpath(EXTRA); + break; + case JIGUANG_APP_NETWORKSTATE: + setAppNetworkstate(EXTRA); + break; + case JIGUANG_APP_LOCKEDSTATE: + setAppLockedstate(EXTRA); + break; + case JIGUANG_FORCE_INSTALLAPK: + intallApk(EXTRA); + break; + case JIGUANG_FORCE_UNINSTALLAPK: + ToastUtil.debugShow("收到应用卸载消息"); + unintallApk(EXTRA); + break; + } + + + } + } + + synchronized private void defaults(String jsonArray) { + if (jsonArray.length() > 0) { + try { + JSONObject extra = new JSONObject(jsonArray); + String packageName = extra.getString("package"); + int is_network = extra.getInt("is_network"); + } catch (JSONException e) { + e.printStackTrace(); + LogUtils.e("defaults", e.getMessage()); + } + } else { + ToastUtil.debugShow("defaults jsonArray is NULL"); + } + } + + //USB数据功能管控 + //仅充电:usb_charge + //MTP模式:usb_mtp + //Midi模式:usb_midi + synchronized private void setUsbState(String jsonArray) { + if (jsonArray.length() > 0) { + try { + JSONObject extra = new JSONObject(jsonArray); + int is_dataline = extra.getInt("is_dataline"); + if (is_dataline == 1) { + boolean qch_usb_choose = Settings.System.putString(getContentResolver(), "qch_usb_choose", "usb_charge"); + LogUtils.e("setUsbState:", Settings.System.getString(getContentResolver(), "qch_usb_choose")); + } else { + boolean qch_usb_choose = Settings.System.putString(getContentResolver(), "qch_usb_choose", "usb_mtp"); + LogUtils.e("setUsbState:", Settings.System.getString(getContentResolver(), "qch_usb_choose")); + } + } catch (JSONException e) { + e.printStackTrace(); + LogUtils.e("setUsbState", e.getMessage()); + } + } else { + ToastUtil.debugShow("setUsbState jsonArray is NULL"); + } + } + + synchronized private void setTfcardState(String jsonArray) { + if (jsonArray.length() > 0) { + try { + JSONObject extra = new JSONObject(jsonArray); + int is_tf = extra.getInt("is_tf"); + boolean qch_sdcard_forbid_on = Settings.System.putInt(getContentResolver(), "qch_sdcard_forbid_on", is_tf); + if (qch_sdcard_forbid_on) { + LogUtils.e("setTfcardState:", Settings.System.getString(getContentResolver(), "qch_sdcard_forbid_on")); + } else { + ToastUtil.debugShow("setTfcardState failed,state:" + is_tf); + } + } catch (JSONException e) { + e.printStackTrace(); + LogUtils.e("setTfcardState", e.getMessage()); + } + } else { + ToastUtil.debugShow("setTfcardState jsonArray is NULL"); + } + } + + synchronized private void setBluetoothState(String jsonArray) { + if (jsonArray.length() > 0) { + try { + JSONObject extra = new JSONObject(jsonArray); + int is_bluetooth = extra.getInt("is_bluetooth"); + boolean qch_bt_forbid_on = Settings.System.putInt(getContentResolver(), "qch_bt_forbid_on", is_bluetooth); + if (qch_bt_forbid_on) { + LogUtils.e("setBluetoothState:", Settings.System.getString(getContentResolver(), "qch_bt_forbid_on")); + } else { + ToastUtil.debugShow("setBluetoothState failed,state:" + is_bluetooth); + } + } catch (JSONException e) { + e.printStackTrace(); + LogUtils.e("setBluetoothState", e.getMessage()); + } + } else { + ToastUtil.debugShow("setBluetoothState jsonArray is NULL"); + } + } + + synchronized private void setBrowserUrlpath(String jsonArray) { + if (jsonArray.length() > 0) { + try { + JSONObject extra = new JSONObject(jsonArray); + String browser = extra.getString("browser"); + boolean setBrowserUrlpath = Settings.System.putString(getContentResolver(), "DeselectBrowserArray", browser); + LogUtils.e("setBrowserUrlpath:", String.valueOf(setBrowserUrlpath)); + if (setBrowserUrlpath) { + LogUtils.e("getBrowserUrlpath:", Settings.System.getString(getContentResolver(), "DeselectBrowserArray")); + } else { + ToastUtil.debugShow("setBrowserUrlpath failed,url:" + browser); + } + } catch (JSONException e) { + e.printStackTrace(); + LogUtils.e("setBrowserUrlpath", e.getMessage()); + } + } else { + boolean setBrowserUrlpath = Settings.System.putString(getContentResolver(), "DeselectBrowserArray", "invalid"); + + ToastUtil.debugShow("setBrowserUrlpath jsonArray is NULL,set default: " + setBrowserUrlpath); + } + } + + synchronized private void setAppNetworkstate(String jsonArray) { + if (jsonArray.length() > 0) { + try { + JSONObject extra = new JSONObject(jsonArray); + String packageName = extra.getString("package"); + int is_network = extra.getInt("is_network"); + if (is_network == 1) { +// Settings.System.putString(getContentResolver(), "qch_jgy_network_disallow", net_not); + + } else { + + } + } catch (JSONException e) { + e.printStackTrace(); + LogUtils.e("setAppNetworkstate", e.getMessage()); + } + } else { + ToastUtil.debugShow("setAppNetworkstate jsonArray is NULL"); + } + } + + synchronized private void setAppLockedstate(String jsonArray) { + if (jsonArray.length() > 0) { + try { + JSONObject extra = new JSONObject(jsonArray); + String packageName = extra.getString("package"); + int is_lock = extra.getInt("is_lock"); + ToastUtil.debugShow("收到应用锁管控消息:包名" + packageName + "is_lock_state:" + is_lock); + PackageManager pm = getPackageManager(); + //后台为0可能传过来null + if (is_lock == 1) { + pm.setApplicationEnabledSetting(packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0); + } else { + pm.setApplicationEnabledSetting(packageName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0); + } + } catch (JSONException e) { + e.printStackTrace(); + LogUtils.e("setAppLockedstate", e.getMessage()); + } + } else { + ToastUtil.debugShow("setAppLockedstate jsonArray is NULL"); + } + + } + + //静默安装应用,使用okgo,断网会出现问题,等待修改使用aria + synchronized private void intallApk(String jsondata) { + try { + JSONObject extra = new JSONObject(jsondata); + final String packages = extra.getString("package"); + ToastUtil.debugShow("收到应用安装消息:包名" + packages); + String url = extra.getString("url"); + OkGo.get(url) + .execute(new FileCallback() { + @Override + public void onSuccess(Response response) { +// Settings.System.putString(getApplicationContext().getContentResolver(), "qch_app_forbid", "com.baidu.video"); + ApkUtils.installApkInSilence(response.body().getAbsolutePath(), packages); + LogUtils.e("onSuccess", "download file successful,now installing"); + } + + @Override + public void onError(Response response) { + super.onError(response); + LogUtils.e("manageCustomMessage", "File download Failure"); + } + + @Override + public void downloadProgress(Progress progress) { + super.downloadProgress(progress); + LogUtils.e("downloadProgress", "已下载:" + progress.currentSize + ",总大小:" + progress.totalSize + ",进度:" + progress.fraction + ",当前网速:" + progress.speed); + } + }); + + } catch (JSONException e) { + e.printStackTrace(); + LogUtils.e("intallApk", e.getMessage()); + } + } + + synchronized private void unintallApk(String json) { + try { + JSONObject object = new JSONObject(json); + String packageName = object.getString("package"); + ToastUtil.debugShow("收到应用卸载消息:包名" + packageName); + if (!packageName.equals("")) { + ApkUtils.deleteApkInSilence(packageName); + } + } catch (JSONException e) { + e.printStackTrace(); + LogUtils.e("unintallApk", e.getMessage()); + } + } + } diff --git a/app/src/main/java/com/appstore/uiui/TextCode/MessageWhat.java b/app/src/main/java/com/appstore/uiui/TextCode/MessageWhat.java new file mode 100644 index 0000000..e2d2427 --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/TextCode/MessageWhat.java @@ -0,0 +1,6 @@ +package com.appstore.uiui.TextCode; + +public class MessageWhat { + public static int CODE_SUCCESSFUL = 200; + +} diff --git a/app/src/main/java/com/appstore/uiui/activity/DetailsActivity.java b/app/src/main/java/com/appstore/uiui/activity/DetailsActivity.java new file mode 100644 index 0000000..8445f9d --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/activity/DetailsActivity.java @@ -0,0 +1,116 @@ +package com.appstore.uiui.activity; + +import android.view.View; +import android.widget.ImageView; +import android.widget.RatingBar; +import android.widget.TextView; + +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.appstore.uiui.R; +import com.appstore.uiui.adapter.ImageAdapter; +import com.appstore.uiui.base.BaseActivity; +import com.appstore.uiui.bean.AppInfo; +import com.appstore.uiui.helper.CustomSnapHelper; +import com.appstore.uiui.listener.LogDownloadListener; +import com.appstore.uiui.utils.ApkUtils; +import com.appstore.uiui.utils.ToastUtil; +import com.bumptech.glide.Glide; +import com.lzy.okgo.OkGo; +import com.lzy.okgo.request.GetRequest; +import com.lzy.okserver.OkDownload; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class DetailsActivity extends BaseActivity { + private TextView tv_title, tv_name, tv_company, tv_update_time, tv_version, tv_content, tv_download; + private RatingBar rating_bar; + private ImageView iv_icon; + private RecyclerView rv_image; + private AppInfo appInfo; + private ImageAdapter imageAdapter; + + @Override + protected int setLayoutResourceID() { + return R.layout.activity_details; + } + + @Override + protected void initView() { + tv_title = findViewById(R.id.detail_tv_title); + tv_name = findViewById(R.id.detail_tv_name); + tv_company = findViewById(R.id.detail_tv_company); + tv_update_time = findViewById(R.id.detail_tv_update_time); + tv_version = findViewById(R.id.detail_tv_version); + tv_content = findViewById(R.id.detali_tv_content); + tv_download = findViewById(R.id.detail_tv_download); + rating_bar = findViewById(R.id.detail_rating_bar); + iv_icon = findViewById(R.id.detail_iv_icon); + rv_image = findViewById(R.id.detail_rv_image); + } + + @Override + protected void initData() { + appInfo = (AppInfo) getIntent().getSerializableExtra("appinfo"); + if (appInfo == null) { + ToastUtil.show("获取引用信息失败,返回重试"); + return; + } + tv_title.setText(appInfo.getApp_name()); + tv_name.setText(appInfo.getApp_name()); + tv_company.setText(appInfo.getApp_developer()); + tv_update_time.setText("更新时间:" + appInfo.getCreatetime()); + tv_version.setText("最新版本:" + appInfo.getApp_version_name()); + tv_content.setText(appInfo.getApp_desc()); + rating_bar.setRating((float) appInfo.getApp_score()); + Glide.with(this).asBitmap().load(appInfo.getApp_img()).into(iv_icon); + List list = new ArrayList() {{ + this.add(appInfo.getApp_preview1()); + this.add(appInfo.getApp_preview2()); + this.add(appInfo.getApp_preview3()); + }}; + imageAdapter = new ImageAdapter(list); + rv_image.setAdapter(imageAdapter); + rv_image.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)); + CustomSnapHelper snapHelper = new CustomSnapHelper(); + snapHelper.attachToRecyclerView(rv_image); + if (appInfo.isInstall()) { + if (appInfo.isUpdate()) { + tv_download.setText("更新"); + } else { + tv_download.setText("打开"); + } + } else { + tv_download.setText("安装"); + } + tv_download.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (appInfo.isInstall() && !appInfo.isUpdate()) { + ApkUtils.openApp(DetailsActivity.this, appInfo.getApp_package()); + } else { + GetRequest request = OkGo.get(appInfo.getApp_url()); + //这里第一个参数是tag,代表下载任务的唯一标识,传任意字符串都行,需要保证唯一,我这里用url作为了tag + OkDownload.request(appInfo.getApp_url(), request)// +// .priority(apk.priority)// + .extra1(appInfo)// + .save()// + .register(new LogDownloadListener())// + .start(); + } + } + }); + } + + @Override + protected void setListener() { + + } + + public void finish(View view) { + finish(); + } +} diff --git a/app/src/main/java/com/appstore/uiui/activity/DownloadManagerActivity.java b/app/src/main/java/com/appstore/uiui/activity/DownloadManagerActivity.java new file mode 100644 index 0000000..8436dc6 --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/activity/DownloadManagerActivity.java @@ -0,0 +1,68 @@ +package com.appstore.uiui.activity; + +import android.view.View; +import android.widget.Adapter; + +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.appstore.uiui.R; +import com.appstore.uiui.adapter.DownloadManagerAdapter; +import com.appstore.uiui.base.BaseActivity; +import com.appstore.uiui.utils.ToastUtil; +import com.lzy.okserver.OkDownload; +import com.lzy.okserver.task.XExecutor; + +public class DownloadManagerActivity extends BaseActivity implements XExecutor.OnAllTaskEndListener { + private RecyclerView recyclerView; + private DownloadManagerAdapter adapter; + private OkDownload okDownload; + + @Override + protected int setLayoutResourceID() { + return R.layout.activity_download_manager; + } + + @Override + protected void initView() { + recyclerView = findViewById(R.id.recyclerView); + } + + @Override + protected void initData() { + okDownload = OkDownload.getInstance(); + adapter = new DownloadManagerAdapter(this); + adapter.updateData(DownloadManagerAdapter.TYPE_ALL); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + recyclerView.setAdapter(adapter); + okDownload.addOnAllTaskEndListener(this); + } + + @Override + protected void setListener() { + + } + + + @Override + public void onAllTaskEnd() { + ToastUtil.show("所有任务已完成"); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + okDownload.removeOnAllTaskEndListener(this); + adapter.unRegister(); + } + + @Override + protected void onResume() { + super.onResume(); + adapter.notifyDataSetChanged(); + } + + public void finish(View view) { + this.finish(); + } +} diff --git a/app/src/main/java/com/appstore/uiui/activity/KindDetailActivity.java b/app/src/main/java/com/appstore/uiui/activity/KindDetailActivity.java new file mode 100644 index 0000000..2a2c8e0 --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/activity/KindDetailActivity.java @@ -0,0 +1,58 @@ +package com.appstore.uiui.activity; + +import android.os.Handler; +import android.os.Message; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import com.appstore.uiui.R; +import com.appstore.uiui.base.BaseActivity; + + +public class KindDetailActivity extends BaseActivity { + private TextView title; + private String name; + private int subject; + + @Override + + protected int setLayoutResourceID() { + return R.layout.activity_kind_detail; + } + + @Override + protected void initView() { + title = findViewById(R.id.detail_tv_title); + } + + @Override + protected void initData() { + subject = getIntent().getIntExtra("kind", 0); + name = getIntent().getStringExtra("name"); + title.setText(name); + + } + + @Override + protected void setListener() { + + } + + public void finish(View view) { + finish(); + } + + Handler handler = new Handler() { + @Override + public void handleMessage(@NonNull Message msg) { + super.handleMessage(msg); + switch (msg.what) { + case 1: + + break; + } + } + }; +} diff --git a/app/src/main/java/com/appstore/uiui/activity/LocalManagerActivity.java b/app/src/main/java/com/appstore/uiui/activity/LocalManagerActivity.java new file mode 100644 index 0000000..5fb58cb --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/activity/LocalManagerActivity.java @@ -0,0 +1,132 @@ +package com.appstore.uiui.activity; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.View; + +import com.appstore.uiui.R; +import com.appstore.uiui.adapter.LocalAppAdapter; +import com.appstore.uiui.base.BaseActivity; +import com.appstore.uiui.base.RefreshManager; +import com.appstore.uiui.bean.LocalApp; +import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smartrefresh.layout.api.RefreshLayout; +import com.scwang.smartrefresh.layout.listener.OnRefreshListener; + +import java.util.ArrayList; +import java.util.List; + +public class LocalManagerActivity extends BaseActivity implements RefreshManager.RefreshInterface { + private RecyclerView mRvApp; + private SmartRefreshLayout mRefreshLayout; + private List localAppList; + private LocalAppAdapter adapter; + + @Override + protected int setLayoutResourceID() { + return R.layout.activity_local_manager; + } + + @Override + protected void initView() { + mRvApp = findViewById(R.id.local_app_rv_app); + mRefreshLayout = findViewById(R.id.local_app_refresh_layout); + RefreshManager.getInstance().register(this); + localAppList = new ArrayList<>(); + + mRefreshLayout.setEnableLoadMore(false); + mRefreshLayout.setEnableRefresh(true); + mRefreshLayout.setOnRefreshListener(new OnRefreshListener() { + @Override + public void onRefresh(RefreshLayout refreshlayout) { + initDatas(); + } + }); + mRefreshLayout.autoRefresh(); + + adapter = new LocalAppAdapter(localAppList, this); + adapter.setHasStableIds(true); + mRvApp.setAdapter(adapter); + mRvApp.setLayoutManager(new LinearLayoutManager(getApplicationContext())); + } + + @Override + protected void initData() { + initDatas(); + + } + + @Override + protected void onResume() { + super.onResume(); + initDatas(); + + } + + private void initDatas() { + localAppList.clear(); + Intent intent = new Intent(Intent.ACTION_MAIN, null); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + List resolveInfoList = getApplication().getPackageManager().queryIntentActivities(intent, 0); + + for (int i = 0; i < resolveInfoList.size(); i++) { + LocalApp bean = new LocalApp(); + bean.setAppName(resolveInfoList.get(i).loadLabel(getApplicationContext().getPackageManager()).toString()); + String packageName = resolveInfoList.get(i).activityInfo.packageName; + bean.setPackageName(packageName); + Drawable icon = resolveInfoList.get(i).loadIcon(getApplicationContext().getPackageManager()); + bean.setIcon(icon); + + try { + PackageInfo packageInfo = getApplicationContext().getPackageManager().getPackageInfo(packageName, 0); + String versionCode = getApplicationContext().getPackageManager() + .getPackageInfo(packageName, 0).versionName; + bean.setVersion(versionCode); + + if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) <= 0) { + //第三方应用 + localAppList.add(bean); + //判断是否需要进行更新 + + } else { + //系统应用 + } + + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + } + adapter.notifyDataSetChanged(); + mRefreshLayout.finishRefresh(); + + } + + @Override + protected void setListener() { + + } + + @Override + public void onRefresh() { + mRefreshLayout.autoRefresh(); + + } + + @Override + public void onLoadBitMap(boolean isLoad) { + + } + + public void finish(View view) { + this.finish(); + } +} diff --git a/app/src/main/java/com/appstore/uiui/activity/MainActivity.java b/app/src/main/java/com/appstore/uiui/activity/MainActivity.java index 20842cc..9c75ee6 100644 --- a/app/src/main/java/com/appstore/uiui/activity/MainActivity.java +++ b/app/src/main/java/com/appstore/uiui/activity/MainActivity.java @@ -1,36 +1,42 @@ package com.appstore.uiui.activity; -import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentPagerAdapter; -import androidx.viewpager.widget.ViewPager; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Environment; +import android.os.Handler; +import android.os.Message; +import android.text.TextUtils; import android.util.Log; -import android.view.MenuItem; +import android.view.KeyEvent; import android.view.View; import android.widget.ImageView; import android.widget.RelativeLayout; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.ViewPager; + import com.appstore.uiui.R; import com.appstore.uiui.base.BaseActivity; +import com.appstore.uiui.base.UserInfo; import com.appstore.uiui.fragment.FeaturedFragment; import com.appstore.uiui.fragment.KindFragment; import com.appstore.uiui.fragment.ManageFragment; import com.appstore.uiui.fragment.RankFragment; -import com.appstore.uiui.utils.ApkUtils; +import com.appstore.uiui.jpush.ExampleUtil; +import com.appstore.uiui.jpush.LocalBroadcastManager; +import com.appstore.uiui.jpush.TagAliasOperatorHelper; +import com.appstore.uiui.network.OKGOPost; +import com.appstore.uiui.service.MyDownloadService; +import com.appstore.uiui.utils.LogUtils; +import com.appstore.uiui.utils.SPUtils; import com.appstore.uiui.utils.ToastUtil; -import com.arialyy.annotations.Download; -import com.arialyy.aria.core.Aria; -import com.arialyy.aria.core.listener.ISchedulers; -import com.arialyy.aria.core.task.DownloadTask; -import com.arialyy.aria.util.ALog; -import com.blankj.utilcode.util.ToastUtils; +import com.appstore.uiui.utils.Utils; import com.flyco.tablayout.SlidingTabLayout; import com.hjq.permissions.OnPermission; import com.hjq.permissions.Permission; @@ -39,18 +45,48 @@ import com.hjq.permissions.XXPermissions; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.Set; + +import cn.jpush.android.api.JPushInterface; +import cn.jpush.android.api.TagAliasCallback; + +import static com.appstore.uiui.jpush.TagAliasOperatorHelper.ACTION_ADD; +import static com.appstore.uiui.jpush.TagAliasOperatorHelper.ACTION_CHECK; +import static com.appstore.uiui.jpush.TagAliasOperatorHelper.ACTION_CLEAN; +import static com.appstore.uiui.jpush.TagAliasOperatorHelper.ACTION_DELETE; +import static com.appstore.uiui.jpush.TagAliasOperatorHelper.ACTION_GET; +import static com.appstore.uiui.jpush.TagAliasOperatorHelper.ACTION_SET; +import static com.appstore.uiui.jpush.TagAliasOperatorHelper.TagAliasBean; +import static com.appstore.uiui.jpush.TagAliasOperatorHelper.sequence; public class MainActivity extends BaseActivity { private RelativeLayout search_layout; private ImageView iv_download; private SlidingTabLayout mSlidingTabLayout; private ViewPager mViewPager; + private long exitTime = 0; + public static boolean isForeground = false; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Aria.download(this).register(); requestPermission(); + registerMessageReceiver(); // used for receive msg + String rid = JPushInterface.getRegistrationID(getApplicationContext()); + if (!rid.isEmpty()) { + ToastUtil.debugShow("RegId:" + rid); + LogUtils.e("RegId", rid); + onTagAliasAction(7); + } else { +// ToastUtil.show("Get registration fail, JPush init failed!"); +// Toast.makeText(this, "Get registration fail, JPush init failed!", Toast.LENGTH_SHORT).show(); + } + OKGOPost.getUserInfo(handler); +// PackageManager pm = getPackageManager(); +// pm.setApplicationEnabledSetting("com.tencent.qqmusic", PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0); + startService(new Intent(MainActivity.this, MyDownloadService.class)); + } @Override @@ -64,7 +100,7 @@ public class MainActivity extends BaseActivity { iv_download.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - + startActivity(new Intent(MainActivity.this, DownloadManagerActivity.class)); } }); search_layout = findViewById(R.id.search_layout); @@ -111,12 +147,138 @@ public class MainActivity extends BaseActivity { } + // 初始化 JPush。如果已经初始化,但没有登录成功,则执行重新登录。 + private void init() { + JPushInterface.init(getApplicationContext()); + } + + public void onTagAliasAction(int i) { + Set tags = null; + String alias = null; + int action = -1; + boolean isAliasAction = false; + switch (i) { + //设置手机号码: + case 0: +// handleSetMobileNumber(); + return; + //增加tag + case 1: +// tags = getInPutTags(); + if (tags == null) { + return; + } + action = ACTION_ADD; + break; + //设置tag + case 2: +// tags = getInPutTags(); + if (tags == null) { + return; + } + action = ACTION_SET; + break; + //删除tag + case 3: +// tags = getInPutTags(); + if (tags == null) { + return; + } + action = ACTION_DELETE; + break; + //获取所有tag + case 4: + action = ACTION_GET; + break; + //清除所有tag + case 5: + action = ACTION_CLEAN; + break; + case 6: +// tags = getInPutTags(); + if (tags == null) { + return; + } + action = ACTION_CHECK; + break; + //设置alias + case 7: +// alias = getInPutAlias(); + alias = Utils.getSerial(); + if (TextUtils.isEmpty(alias)) { + return; + } + isAliasAction = true; + action = ACTION_SET; + break; + //获取alias + case 8: + isAliasAction = true; + action = ACTION_GET; + break; + //删除alias + case 9: + isAliasAction = true; + action = ACTION_DELETE; + break; + default: + return; + } + TagAliasBean tagAliasBean = new TagAliasBean(); + tagAliasBean.action = action; + sequence++; + if (isAliasAction) { + tagAliasBean.alias = alias; + } else { + tagAliasBean.tags = tags; + } + tagAliasBean.isAliasAction = isAliasAction; + TagAliasOperatorHelper.getInstance().handleAction(getApplicationContext(), sequence, tagAliasBean); + + } + + //for receive customer msg from jpush server + private MessageReceiver mMessageReceiver; + public static final String MESSAGE_RECEIVED_ACTION = "com.example.jpushdemo.MESSAGE_RECEIVED_ACTION"; + public static final String KEY_TITLE = "title"; + public static final String KEY_MESSAGE = "message"; + public static final String KEY_EXTRAS = "extras"; + + public void registerMessageReceiver() { + mMessageReceiver = new MessageReceiver(); + IntentFilter filter = new IntentFilter(); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + filter.addAction(MESSAGE_RECEIVED_ACTION); + LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, filter); + } + + + public class MessageReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + try { + if (MESSAGE_RECEIVED_ACTION.equals(intent.getAction())) { + String messge = intent.getStringExtra(KEY_MESSAGE); + String extras = intent.getStringExtra(KEY_EXTRAS); + StringBuilder showMsg = new StringBuilder(); + showMsg.append(KEY_MESSAGE + " : " + messge + "\n"); + if (!ExampleUtil.isEmpty(extras)) { + showMsg.append(KEY_EXTRAS + " : " + extras + "\n"); + } + } + } catch (Exception e) { + } + } + } + private String[] permission = new String[]{ // Permission.SYSTEM_ALERT_WINDOW, // Permission.CAMERA, // Permission.READ_SMS, // Permission.RECEIVE_SMS, // Permission.SEND_SMS, + Permission.REQUEST_INSTALL_PACKAGES, Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE, // Permission.READ_PHONE_STATE @@ -138,7 +300,7 @@ public class MainActivity extends BaseActivity { String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "POStemp"; File file = new File(path); file.mkdirs(); - } else { + } else { ToastUtil.show("需要授予所有权限才能正常使用本程序!"); } } @@ -156,53 +318,47 @@ public class MainActivity extends BaseActivity { }); } - //在这里处理任务执行中的状态,如进度进度条的刷新 - @Download.onTaskRunning - protected void running(DownloadTask task) { - Log.e("fanhuitong", "正在下载:" + "State:" + task.getState() + "\t进度:" + task.getPercent() + "%" + "-" + task.getExtendField()); - ToastUtils.showShort("正在下载:" + task.getExtendField() + "\t" + task.getPercent() + "%"); - } - @Download.onTaskComplete - void taskComplete(DownloadTask task) { - //在这里处理任务完成的状态 - String downloadPath = task.getFilePath(); - String packageName = task.getExtendField(); - Log.e("fanhuitong", "downloadPath::" + downloadPath); - Log.e("fanhuitong", "extendField::" + packageName); - ApkUtils.installApkInSilence(downloadPath, packageName); - } private static final String TAG = "fanhuitong"; - BroadcastReceiver receiver = new BroadcastReceiver() { - @Override public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(ISchedulers.ARIA_TASK_INFO_ACTION)){ - // 获取任务状态 - ALog.d(TAG, "state = " + intent.getIntExtra(ISchedulers.TASK_STATE, -1)); - // 获取任务类型 - ALog.d(TAG, "type = " + intent.getIntExtra(ISchedulers.TASK_TYPE, -1)); - // 获取任务状态速度,单位为byte/s - ALog.d(TAG, "speed = " + intent.getLongExtra(ISchedulers.TASK_SPEED, -1)); - // 获取任务进度,0-100 - ALog.d(TAG, "percent = " + intent.getIntExtra(ISchedulers.TASK_PERCENT, -1)); - // 获取任务实体 - ALog.d(TAG, "entity = " + intent.getParcelableExtra(ISchedulers.TASK_ENTITY).toString()); + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + exit(); + return false; + } + return super.onKeyDown(keyCode, event); + + } + + public void exit() { + if ((System.currentTimeMillis() - exitTime) > 2000) { + ToastUtil.show("再按一次退出程序"); + exitTime = System.currentTimeMillis(); + } else { + finish(); + } + } + + private Handler handler = new Handler() { + @Override + public void handleMessage(@NonNull Message msg) { + super.handleMessage(msg); + switch (msg.what) { + case 0: + SPUtils.put(MainActivity.this, "isLogined", 0); + break; + case 1: + SPUtils.put(MainActivity.this, "isLogined", 1); + UserInfo userInfo = (UserInfo) msg.obj; + SPUtils.put(MainActivity.this, "member_id", userInfo.getMember_id()); + SPUtils.put(MainActivity.this, "sn_id", userInfo.getId()); + break; + case 2: + SPUtils.put(MainActivity.this, "isLogined", 2); + break; } } }; - - @Override protected void onResume() { - super.onResume(); - registerReceiver(receiver, new IntentFilter(ISchedulers.ARIA_TASK_INFO_ACTION)); - } - - @Override protected void onDestroy() { - super.onDestroy(); - unregisterReceiver(receiver); - //Aria.download(this).unRegister(); - } - - - - } diff --git a/app/src/main/java/com/appstore/uiui/adapter/AppAdapter.java b/app/src/main/java/com/appstore/uiui/adapter/AppAdapter.java index e7fad68..1a65d9f 100644 --- a/app/src/main/java/com/appstore/uiui/adapter/AppAdapter.java +++ b/app/src/main/java/com/appstore/uiui/adapter/AppAdapter.java @@ -1,9 +1,8 @@ package com.appstore.uiui.adapter; -import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; - +import android.content.Intent; +import android.telecom.Call; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -11,31 +10,32 @@ import android.widget.Button; import android.widget.ImageView; import android.widget.RatingBar; import android.widget.TextView; -import android.widget.Toast; import androidx.cardview.widget.CardView; import androidx.recyclerview.widget.RecyclerView; -import com.appstore.uiui.BuildConfig; import com.appstore.uiui.R; +import com.appstore.uiui.activity.DetailsActivity; import com.appstore.uiui.bean.AppInfo; - -import com.arialyy.annotations.AriaConstance; -import com.arialyy.aria.core.Aria; -import com.arialyy.aria.core.AriaConfig; -import com.arialyy.aria.core.AriaManager; -import com.blankj.utilcode.util.EncryptUtils; -import com.blankj.utilcode.util.PathUtils; +import com.appstore.uiui.listener.LogDownloadListener; +import com.appstore.uiui.utils.ApkUtils; +import com.appstore.uiui.utils.ToastUtil; import com.bumptech.glide.Glide; +import com.lzy.okgo.OkGo; +import com.lzy.okgo.model.Progress; +import com.lzy.okgo.request.GetRequest; +import com.lzy.okserver.OkDownload; +import com.lzy.okserver.download.DownloadListener; +import com.lzy.okserver.download.DownloadTask; - +import java.io.File; import java.util.List; /** * Created by asus on 2017/10/23. */ -public class AppAdapter extends RecyclerView.Adapter { +public class AppAdapter extends RecyclerView.Adapter { private List mAppInfoList; @@ -46,21 +46,30 @@ public class AppAdapter extends RecyclerView.Adapter appInfoList, boolean isShowOrder, boolean isNotLoadBitmap) { + public AppAdapter(List appInfoList, boolean isShowOrder, Context context) { + this.mContext = context; this.mAppInfoList = appInfoList; this.isShowOrder = isShowOrder; - this.isNotLoadBitmap = isNotLoadBitmap; } @Override - public FeaturedViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - mContext = parent.getContext(); - return new FeaturedViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_app, parent, false)); + public Holder onCreateViewHolder(ViewGroup parent, int viewType) { + Context context = parent.getContext(); + return new Holder(LayoutInflater.from(context).inflate(R.layout.item_app, parent, false)); } @Override - public void onBindViewHolder(FeaturedViewHolder holder, final int position) { + public void onBindViewHolder(Holder holder, final int position) { final AppInfo appInfo = mAppInfoList.get(position); + final DownloadTask downloadTask = OkDownload.getInstance().getTask(appInfo.getApp_url()); + holder.setTag(appInfo.getApp_url()); + if (downloadTask != null) { + downloadTask.register(new ListDownloadListener(appInfo.getApp_url(), holder)); + holder.setTask(downloadTask); +// holder.bind(); + holder.refresh(downloadTask.progress); + } + if (!isNotLoadBitmap) { Glide.with(mContext).asBitmap().load(appInfo.getApp_img()).placeholder(R.drawable.ic_place_holder).into(holder.ivIcon); } else { @@ -73,80 +82,122 @@ public class AppAdapter extends RecyclerView.Adapter request = OkGo.get(appInfo.getApp_url()); + //这里第一个参数是tag,代表下载任务的唯一标识,传任意字符串都行,需要保证唯一,我这里用url作为了tag + OkDownload.request(appInfo.getApp_url(), request)// +// .priority(apk.priority)// + .extra1(appInfo)// + .save()// + .register(new LogDownloadListener())// + .start(); + notifyDataSetChanged(); } @Override public int getItemCount() { - return mAppInfoList.size(); + return mAppInfoList == null ? 0 : mAppInfoList.size(); } - public static class FeaturedViewHolder extends RecyclerView.ViewHolder { + @Override + public long getItemId(int position) { + return position; + } + + + public class Holder extends RecyclerView.ViewHolder { ImageView ivIcon; TextView tvCompany, tvName; Button btnDownLoad; RatingBar ratingBar; - TextView tvInstalled; + // TextView tvInstalled; CardView cardView; - public FeaturedViewHolder(View itemView) { + private DownloadTask task; + private String tag; + + public Holder(View itemView) { super(itemView); ivIcon = itemView.findViewById(R.id.app_iv_icon); tvCompany = itemView.findViewById(R.id.app_tv_company); tvName = itemView.findViewById(R.id.app_tv_name); btnDownLoad = itemView.findViewById(R.id.app_btn_download); ratingBar = itemView.findViewById(R.id.app_rating_bar); - tvInstalled = itemView.findViewById(R.id.app_tv_installed); +// tvInstalled = itemView.findViewById(R.id.app_tv_installed); cardView = itemView.findViewById(R.id.app_card_view); } + + public void bind() { + + } + + void refresh(Progress progress) { + switch (progress.status) { + case Progress.NONE: + btnDownLoad.setText("下载"); + break; + case Progress.PAUSE: + btnDownLoad.setText("继续"); + break; + case Progress.ERROR: + btnDownLoad.setText("出错"); + break; + case Progress.WAITING: + btnDownLoad.setText("等待"); + break; + case Progress.FINISH: + btnDownLoad.setText("完成"); + + break; + case Progress.LOADING: + btnDownLoad.setText((int) (progress.fraction * 100) + "%"); + break; + } + + } + + void setTask(DownloadTask task) { + this.task = task; + } + + void setTag(String tag) { + this.tag = tag; + } + + String getTag() { + return tag; + } } public void setNotLoadBitmap(boolean isNotLoadBitmap) { @@ -158,4 +209,45 @@ public class AppAdapter extends RecyclerView.Adapter { + + public static final int TYPE_ALL = 0; + public static final int TYPE_FINISH = 1; + public static final int TYPE_ING = 2; + + private Context mContext; + private List values; + private int type; + + private final LayoutInflater mInflater; + + public DownloadManagerAdapter(Context context) { + this.mContext = context; + mInflater = LayoutInflater.from(mContext); + } + + public void updateData(int type) { + //这里是将数据库的数据恢复 + this.type = type; + if (type == TYPE_ALL) values = OkDownload.restore(DownloadManager.getInstance().getAll()); + if (type == TYPE_FINISH) + values = OkDownload.restore(DownloadManager.getInstance().getFinished()); + if (type == TYPE_ING) + values = OkDownload.restore(DownloadManager.getInstance().getDownloading()); + notifyDataSetChanged(); + } + + @NonNull + @Override + public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new Holder(mInflater.inflate(R.layout.item_download_manager, parent, false)); + + } + + @Override + public void onBindViewHolder(@NonNull Holder holder, int position) { + DownloadTask task = values.get(position); + String tag = createTag(task); + task.register(new ListDownloadListener(tag, holder))// + .register(new LogDownloadListener()); + holder.setTag(tag); + holder.setTask(task); + holder.bind(); + holder.refresh(task.progress); + + } + + public void unRegister() { + Map taskMap = OkDownload.getInstance().getTaskMap(); + for (DownloadTask task : taskMap.values()) { + task.unRegister(createTag(task)); + } + } + + private String createTag(DownloadTask task) { + return type + "_" + task.progress.tag; + } + + @Override + public int getItemCount() { + return values == null ? 0 : values.size(); + } + + class Holder extends RecyclerView.ViewHolder { + ImageView icon, remove; + TextView name, state, downloadSize; + Button start; + NumberProgressBar pbProgress; + + private DownloadTask task; + private String tag; + + Holder(@NonNull View itemView) { + super(itemView); + icon = itemView.findViewById(R.id.icon); + remove = itemView.findViewById(R.id.remove); + name = itemView.findViewById(R.id.name); + state = itemView.findViewById(R.id.state); + downloadSize = itemView.findViewById(R.id.downloadSize); + start = itemView.findViewById(R.id.start); + pbProgress = itemView.findViewById(R.id.pbProgress); + } + + public void setTask(DownloadTask task) { + this.task = task; + } + + public void bind() { + + Progress progress = task.progress; + AppInfo apk = (AppInfo) progress.extra1; + if (apk != null) { + Glide.with(mContext).load(apk.getApp_img()).error(R.mipmap.ic_launcher).into(icon); + name.setText(apk.getApp_name()); + } else { + name.setText(progress.fileName); + } + start.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + start(); + } + }); + remove.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + remove(); + } + }); + + } + + public void refresh(Progress progress) { + String currentSize = Formatter.formatFileSize(mContext, progress.currentSize); + String totalSize = Formatter.formatFileSize(mContext, progress.totalSize); + downloadSize.setText(currentSize + "/" + totalSize); + switch (progress.status) { + case Progress.NONE: + state.setText("停止"); + start.setText("下载"); + break; + case Progress.PAUSE: + state.setText("暂停中"); + start.setText("继续"); + break; + case Progress.ERROR: + state.setText("下载出错"); + start.setText("出错"); + break; + case Progress.WAITING: + state.setText("等待中"); + start.setText("等待"); + break; + case Progress.FINISH: + state.setText("下载完成"); + start.setText("完成"); + break; + case Progress.LOADING: + String speed = Formatter.formatFileSize(mContext, progress.speed); + state.setText(String.format("%s/s", speed)); + start.setText("暂停"); + break; + } + pbProgress.setMax(10000); + pbProgress.setProgress((int) (progress.fraction * 10000)); + } + + public void start() { + Progress progress = task.progress; + switch (progress.status) { + case Progress.PAUSE: + case Progress.NONE: + case Progress.ERROR: + task.start(); + break; + case Progress.LOADING: + task.pause(); + break; + case Progress.FINISH: + + break; + } + refresh(progress); + } + + public void remove() { + task.remove(true); + updateData(type); + } + + public void restart() { + task.restart(); + } + + public void setTag(String tag) { + this.tag = tag; + } + + public String getTag() { + return tag; + } + } + + private class ListDownloadListener extends DownloadListener { + + private Holder holder; + + ListDownloadListener(Object tag, Holder holder) { + super(tag); + this.holder = holder; + } + + @Override + public void onStart(Progress progress) { + } + + @Override + public void onProgress(Progress progress) { + if (tag == holder.getTag()) { + holder.refresh(progress); + } + } + + @Override + public void onError(Progress progress) { + Throwable throwable = progress.exception; + if (throwable != null) throwable.printStackTrace(); + } + + @Override + public void onFinish(File file, Progress progress) { + + } + + @Override + public void onRemove(Progress progress) { + } + } + +} diff --git a/app/src/main/java/com/appstore/uiui/adapter/ImageAdapter.java b/app/src/main/java/com/appstore/uiui/adapter/ImageAdapter.java new file mode 100644 index 0000000..ae20c09 --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/adapter/ImageAdapter.java @@ -0,0 +1,56 @@ +package com.appstore.uiui.adapter; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import androidx.recyclerview.widget.RecyclerView; + +import com.appstore.uiui.R; +import com.bumptech.glide.Glide; + +import java.util.List; + +/** + * Created by fht on 2019/11/08. + */ + +public class ImageAdapter extends RecyclerView.Adapter { + + private List mSrcList; + private Context mContext; + + + public ImageAdapter(List mSrcList) { + this.mSrcList = mSrcList; + } + + @Override + public ImageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + mContext = parent.getContext(); + return new ImageViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image, parent, false)); + } + + @Override + public void onBindViewHolder(ImageViewHolder holder, int position) { + String src = mSrcList.get(position); + Glide.with(mContext).asBitmap().load(src).placeholder(R.drawable.ic_place_holder).into(holder.imageView); + } + + @Override + public int getItemCount() { + return mSrcList.size(); + } + + static class ImageViewHolder extends RecyclerView.ViewHolder { + ImageView imageView; + + ImageViewHolder(View itemView) { + super(itemView); + imageView = itemView.findViewById(R.id.item_iv); + } + } + +} diff --git a/app/src/main/java/com/appstore/uiui/adapter/KindAdapter.java b/app/src/main/java/com/appstore/uiui/adapter/KindAdapter.java index ca2fe37..dd64129 100644 --- a/app/src/main/java/com/appstore/uiui/adapter/KindAdapter.java +++ b/app/src/main/java/com/appstore/uiui/adapter/KindAdapter.java @@ -1,6 +1,7 @@ package com.appstore.uiui.adapter; import android.content.Context; +import android.content.Intent; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -11,6 +12,7 @@ import android.widget.TextView; import androidx.recyclerview.widget.RecyclerView; import com.appstore.uiui.R; +import com.appstore.uiui.activity.KindDetailActivity; import com.appstore.uiui.bean.Kind; import com.bumptech.glide.Glide; @@ -24,16 +26,14 @@ public class KindAdapter extends RecyclerView.Adapter mKindList; private Context mContext; - private boolean isNotLoadBitmap = false; - public KindAdapter(List mKindList, boolean isLoadBitmap) { + public KindAdapter(List mKindList, Context context) { this.mKindList = mKindList; - this.isNotLoadBitmap = isLoadBitmap; + this.mContext = context; } @Override public KindViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - mContext = parent.getContext(); return new KindViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_kind, parent, false)); } @@ -41,16 +41,16 @@ public class KindAdapter extends RecyclerView.Adapter localAppList; - public LocalAppAdapter(List localAppList) { + public LocalAppAdapter(List localAppList, Context context) { + this.context = context; this.localAppList = localAppList; } @Override public LocalAppViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - context = parent.getContext(); return new LocalAppViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_local_app, parent, false)); } @@ -46,64 +49,28 @@ public class LocalAppAdapter extends RecyclerView.Adapter" + appInfo.getNewVersionName()); + holder.btnDownLoad.setText("更新"); + holder.btnDownLoad.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + downloadApk(appInfo); + } + }); + + + } + + private void downloadApk(UpdateAppInfo appInfo) { + GetRequest request = OkGo.get(appInfo.getURL()); + //这里第一个参数是tag,代表下载任务的唯一标识,传任意字符串都行,需要保证唯一,我这里用url作为了tag + OkDownload.request(appInfo.getURL(), request)// +// .priority(apk.priority)// + .extra1(appInfo.getAppInfo())// + .save()// + .register(new LogDownloadListener())// + .start(); + notifyDataSetChanged(); + } + + @Override + public int getItemCount() { + return localAppList == null ? 0 : localAppList.size(); + } + + @Override + public long getItemId(int position) { + return position; + } + + public static class UpdateAppViewHolder extends RecyclerView.ViewHolder { + ImageView ivIcon; + TextView tvName, tvVersion; + TextView tvUpdate; + Button btnDownLoad; + CardView cardView; + + private DownloadTask task; + private String tag; + + public UpdateAppViewHolder(View itemView) { + super(itemView); + ivIcon = itemView.findViewById(R.id.local_app_iv_icon); + tvName = itemView.findViewById(R.id.local_app_tv_name); + tvVersion = itemView.findViewById(R.id.local_app_version); + tvUpdate = itemView.findViewById(R.id.local_app_update); + btnDownLoad = itemView.findViewById(R.id.local_app_btn_download); + cardView = itemView.findViewById(R.id.local_app_card_view); + } + + public void bind() { + + } + + void refresh(Progress progress) { + switch (progress.status) { + case Progress.NONE: + btnDownLoad.setText("下载"); + break; + case Progress.PAUSE: + btnDownLoad.setText("继续"); + break; + case Progress.ERROR: + btnDownLoad.setText("出错"); + break; + case Progress.WAITING: + btnDownLoad.setText("等待"); + break; + case Progress.FINISH: + btnDownLoad.setText("完成"); + + break; + case Progress.LOADING: + btnDownLoad.setText((int) (progress.fraction * 100) + "%"); + break; + } + + } + + void setTask(DownloadTask task) { + this.task = task; + } + + void setTag(String tag) { + this.tag = tag; + } + + String getTag() { + return tag; + } + } + + private class ListDownloadListener extends DownloadListener { + + private UpdateAppViewHolder holder; + + ListDownloadListener(Object tag, UpdateAppViewHolder holder) { + super(tag); + this.holder = holder; + } + + @Override + public void onStart(Progress progress) { + + } + + @Override + public void onProgress(Progress progress) { + if (tag == holder.getTag()) { + holder.refresh(progress); + } + } + + @Override + public void onError(Progress progress) { + Throwable throwable = progress.exception; + if (throwable != null) throwable.printStackTrace(); + ToastUtil.show(((AppInfo) progress.extra1).getApp_name() + "\t下载失败,请重试"); + } + + @Override + public void onFinish(File file, Progress progress) { + ToastUtil.show(((AppInfo) progress.extra1).getApp_name() + "\t下载完成"); + ApkUtils.installApkInSilence(file.getAbsolutePath(), ((AppInfo) progress.extra1).getApp_package()); + } + + @Override + public void onRemove(Progress progress) { + } + } + +} diff --git a/app/src/main/java/com/appstore/uiui/base/BaseFragment.java b/app/src/main/java/com/appstore/uiui/base/BaseFragment.java index 8844354..15eb209 100644 --- a/app/src/main/java/com/appstore/uiui/base/BaseFragment.java +++ b/app/src/main/java/com/appstore/uiui/base/BaseFragment.java @@ -9,6 +9,8 @@ import android.view.View; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; +import com.appstore.uiui.utils.LogUtils; + /** * Created by asus on 2017/8/4. */ @@ -20,66 +22,66 @@ public class BaseFragment extends Fragment { @Override public void onAttach(Context context) { super.onAttach(context); - Log.d(TAG, "onAttach: "); + LogUtils.d(TAG, "onAttach: "); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Log.d(TAG, "onCreate: "); + LogUtils.d(TAG, "onCreate: "); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - Log.d(TAG, "onViewCreated: "); + LogUtils.d(TAG, "onViewCreated: "); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - Log.d(TAG, "onActivityCreated: "); + LogUtils.d(TAG, "onActivityCreated: "); } @Override public void onStart() { super.onStart(); - Log.d(TAG, "onStart: "); + LogUtils.d(TAG, "onStart: "); } @Override public void onResume() { super.onResume(); - Log.d(TAG, "onResume: "); + LogUtils.d(TAG, "onResume: "); } @Override public void onPause() { super.onPause(); - Log.d(TAG, "onPause: "); + LogUtils.d(TAG, "onPause: "); } @Override public void onStop() { super.onStop(); - Log.d(TAG, "onStop: "); + LogUtils.d(TAG, "onStop: "); } @Override public void onDestroyView() { super.onDestroyView(); - Log.d(TAG, "onDestroyView: "); + LogUtils.d(TAG, "onDestroyView: "); } @Override public void onDestroy() { super.onDestroy(); - Log.d(TAG, "onDestroy: "); + LogUtils.d(TAG, "onDestroy: "); } @Override public void onDetach() { super.onDetach(); - Log.d(TAG, "onDetach: "); + LogUtils.d(TAG, "onDetach: "); } } diff --git a/app/src/main/java/com/appstore/uiui/base/UserInfo.java b/app/src/main/java/com/appstore/uiui/base/UserInfo.java new file mode 100644 index 0000000..4adf333 --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/base/UserInfo.java @@ -0,0 +1,123 @@ +package com.appstore.uiui.base; + +import java.io.Serializable; + +public class UserInfo implements Serializable { + private String id; + private String sn_value; + private String sn_name; + private String sn_phone; + private String sn_school; + private int sn_grade; + private String sn_app; + private String sn_area; + private int member_id; + private int is_delete; + private int is_reset; + private int is_lock; + private String createtime; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getSn_value() { + return sn_value; + } + + public void setSn_value(String sn_value) { + this.sn_value = sn_value; + } + + public String getSn_name() { + return sn_name; + } + + public void setSn_name(String sn_name) { + this.sn_name = sn_name; + } + + public String getSn_phone() { + return sn_phone; + } + + public void setSn_phone(String sn_phone) { + this.sn_phone = sn_phone; + } + + public String getSn_school() { + return sn_school; + } + + public void setSn_school(String sn_school) { + this.sn_school = sn_school; + } + + public int getSn_grade() { + return sn_grade; + } + + public void setSn_grade(int sn_grade) { + this.sn_grade = sn_grade; + } + + public String getSn_app() { + return sn_app; + } + + public void setSn_app(String sn_app) { + this.sn_app = sn_app; + } + + public String getSn_area() { + return sn_area; + } + + public void setSn_area(String sn_area) { + this.sn_area = sn_area; + } + + public int getMember_id() { + return member_id; + } + + public void setMember_id(int member_id) { + this.member_id = member_id; + } + + public int getIs_delete() { + return is_delete; + } + + public void setIs_delete(int is_delete) { + this.is_delete = is_delete; + } + + public int getIs_reset() { + return is_reset; + } + + public void setIs_reset(int is_reset) { + this.is_reset = is_reset; + } + + public int getIs_lock() { + return is_lock; + } + + public void setIs_lock(int is_lock) { + this.is_lock = is_lock; + } + + public String getCreatetime() { + return createtime; + } + + public void setCreatetime(String createtime) { + this.createtime = createtime; + } +} diff --git a/app/src/main/java/com/appstore/uiui/bean/AppInfo.java b/app/src/main/java/com/appstore/uiui/bean/AppInfo.java index 059adc6..7f8bccc 100644 --- a/app/src/main/java/com/appstore/uiui/bean/AppInfo.java +++ b/app/src/main/java/com/appstore/uiui/bean/AppInfo.java @@ -27,9 +27,8 @@ public class AppInfo implements Serializable { private int is_silent; private String createtime; - public static boolean isInstall; - - public static boolean isUpdate; + private boolean isInstall = false; + private boolean isUpdate = false; public int getApp_id() { @@ -216,6 +215,9 @@ public class AppInfo implements Serializable { this.createtime = createtime; } + public void setInstall(boolean b) { + this.isInstall = b; + } public boolean isInstall() { return isInstall; @@ -224,4 +226,8 @@ public class AppInfo implements Serializable { public boolean isUpdate() { return isUpdate; } + + public void setUpdate(boolean b) { + this.isUpdate = b; + } } diff --git a/app/src/main/java/com/appstore/uiui/bean/LocalApp.java b/app/src/main/java/com/appstore/uiui/bean/LocalApp.java index d319629..666f668 100644 --- a/app/src/main/java/com/appstore/uiui/bean/LocalApp.java +++ b/app/src/main/java/com/appstore/uiui/bean/LocalApp.java @@ -14,8 +14,8 @@ public class LocalApp { String size;//软件的大小 String version;//软件的版本号 String packageName;//软件的包名 - - boolean isNeedUpdate=false; + int versionCode; + boolean isNeedUpdate = false; public LocalApp() { } @@ -69,6 +69,14 @@ public class LocalApp { this.version = version; } + public int getVersionCode() { + return versionCode; + } + + public void setVersionCode(int versionCode) { + this.versionCode = versionCode; + } + public String getPackageName() { return packageName; } diff --git a/app/src/main/java/com/appstore/uiui/bean/UpdateAppInfo.java b/app/src/main/java/com/appstore/uiui/bean/UpdateAppInfo.java new file mode 100644 index 0000000..ab1e29f --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/bean/UpdateAppInfo.java @@ -0,0 +1,90 @@ +package com.appstore.uiui.bean; + +import android.graphics.drawable.Drawable; + +import java.io.Serializable; + +public class UpdateAppInfo implements Serializable { + + private String appName; + private String packageName; + private int versionCode; + private int newVersionCode; + private String newVersionName; + private String versionName; + private String URL; + private Drawable icon; + private AppInfo appInfo; + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public String getPackageName() { + return packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + public int getVersionCode() { + return versionCode; + } + + public void setVersionCode(int versionCode) { + this.versionCode = versionCode; + } + + public int getNewVersionCode() { + return newVersionCode; + } + + public void setNewVersionCode(int newVersionCode) { + this.newVersionCode = newVersionCode; + } + + public String getVersionName() { + return versionName; + } + + public void setVersionName(String versionName) { + this.versionName = versionName; + } + + public String getNewVersionName() { + return newVersionName; + } + + public void setNewVersionName(String newVersionName) { + this.newVersionName = newVersionName; + } + + public String getURL() { + return URL; + } + + public void setURL(String URL) { + this.URL = URL; + } + + public Drawable getIcon() { + return icon; + } + + public void setIcon(Drawable icon) { + this.icon = icon; + } + + public AppInfo getAppInfo() { + return appInfo; + } + + public void setAppInfo(AppInfo appInfo) { + this.appInfo = appInfo; + } +} diff --git a/app/src/main/java/com/appstore/uiui/fragment/FeaturedFragment.java b/app/src/main/java/com/appstore/uiui/fragment/FeaturedFragment.java index 4574dd2..1631206 100644 --- a/app/src/main/java/com/appstore/uiui/fragment/FeaturedFragment.java +++ b/app/src/main/java/com/appstore/uiui/fragment/FeaturedFragment.java @@ -19,6 +19,7 @@ import com.appstore.uiui.bean.AppInfo; import com.appstore.uiui.bean.AppInfos; import com.appstore.uiui.network.OKGOPost; import com.appstore.uiui.network.URLs.Url; +import com.appstore.uiui.utils.ApkUtils; import com.appstore.uiui.utils.LogUtils; import com.appstore.uiui.utils.ToastUtil; import com.lzy.okgo.OkGo; @@ -62,7 +63,8 @@ public class FeaturedFragment extends LazyLoadFragment implements RefreshManager isNotLoadBitmap = false; newAppInfoList = new ArrayList<>(); - adapter = new AppAdapter(newAppInfoList, false, isNotLoadBitmap); + adapter = new AppAdapter(newAppInfoList, false, getContext()); + adapter.setHasStableIds(true); mRvResult.setAdapter(adapter); mRvResult.setLayoutManager(new LinearLayoutManager(getActivity())); @@ -86,8 +88,6 @@ public class FeaturedFragment extends LazyLoadFragment implements RefreshManager synchronized private void initAPPData(final Handler handler) { OKGOPost.getAllAppInfo(handler); - mRefreshLayout.finishRefresh(); - } @@ -106,13 +106,24 @@ public class FeaturedFragment extends LazyLoadFragment implements RefreshManager public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { - case 0: + case 1: newAppInfoList = (List) msg.obj; - adapter.setData(newAppInfoList); + adapter.setData(checkUpdateOrInstalled(newAppInfoList)); + adapter.notifyDataSetChanged(); break; + } + mRefreshLayout.finishRefresh(); + } }; + public List checkUpdateOrInstalled(List list) { + for (AppInfo appInfo : list) { + appInfo.setInstall(ApkUtils.isAvailable(getContext(), appInfo.getApp_package())); + appInfo.setUpdate(ApkUtils.checkIsUpdate(getContext(), appInfo.getApp_package(), Integer.parseInt(appInfo.getApp_version_code()))); + } + return list; + } } diff --git a/app/src/main/java/com/appstore/uiui/fragment/KindFragment.java b/app/src/main/java/com/appstore/uiui/fragment/KindFragment.java index 359e3d6..371522f 100644 --- a/app/src/main/java/com/appstore/uiui/fragment/KindFragment.java +++ b/app/src/main/java/com/appstore/uiui/fragment/KindFragment.java @@ -26,7 +26,7 @@ import java.util.List; * 分类的页面 */ -public class KindFragment extends BaseFragment implements RefreshManager.RefreshInterface { +public class KindFragment extends BaseFragment { private RecyclerView mRvKind; private KindAdapter adapter; @@ -37,7 +37,6 @@ public class KindFragment extends BaseFragment implements RefreshManager.Refresh public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_kind, container, false); mRvKind = view.findViewById(R.id.kind_rv_kind); - RefreshManager.getInstance().register(this); initView(); return view; @@ -45,39 +44,22 @@ public class KindFragment extends BaseFragment implements RefreshManager.Refresh private void initView() { kindList = new ArrayList<>(); - kindList.add(new Kind("游戏", 15, R.mipmap.ic_launcher_round)); - kindList.add(new Kind("实用工具", 5, R.mipmap.ic_launcher_round)); - kindList.add(new Kind("影音视听", 27, R.mipmap.ic_launcher_round)); - kindList.add(new Kind("聊天社交", 2, R.mipmap.ic_launcher_round)); - kindList.add(new Kind("图书阅读", 7, R.mipmap.ic_launcher_round)); - kindList.add(new Kind("学习教育", 12, R.mipmap.ic_launcher_round)); - kindList.add(new Kind("效率办公", 10, R.mipmap.ic_launcher_round)); - kindList.add(new Kind("时尚购物", 9, R.mipmap.ic_launcher_round)); - kindList.add(new Kind("居家生活", 4, R.mipmap.ic_launcher_round)); - kindList.add(new Kind("旅行交通", 3, R.mipmap.ic_launcher_round)); - kindList.add(new Kind("摄影摄像", 6, R.mipmap.ic_launcher_round)); - kindList.add(new Kind("医疗健康", 14, R.mipmap.ic_launcher_round)); - kindList.add(new Kind("体育运动", 8, R.mipmap.ic_launcher_round)); - kindList.add(new Kind("新闻资讯", 11, R.mipmap.ic_launcher_round)); - kindList.add(new Kind("娱乐消遣", 13, R.mipmap.ic_launcher_round)); - kindList.add(new Kind("金融理财", 1, R.mipmap.ic_launcher_round)); + kindList.add(new Kind("语文", 1, R.drawable.icon_language)); + kindList.add(new Kind("数学", 2, R.drawable.icon_math)); + kindList.add(new Kind("英语", 3, R.drawable.icon_english)); + kindList.add(new Kind("物理", 4, R.drawable.icon_physics)); + kindList.add(new Kind("化学", 5, R.drawable.icon_chemistry)); + kindList.add(new Kind("生物", 6, R.drawable.icon_biology)); + kindList.add(new Kind("政治", 7, R.drawable.icon_politics)); + kindList.add(new Kind("历史", 8, R.drawable.icon_history)); + kindList.add(new Kind("地理", 9, R.drawable.icon_geography)); - adapter = new KindAdapter(kindList, false); + kindList.add(new Kind("娱乐", 0, R.drawable.icon_game)); + + adapter = new KindAdapter(kindList, getContext()); mRvKind.setAdapter(adapter); mRvKind.setLayoutManager(new LinearLayoutManager(getActivity())); } - @Override - public void onRefresh() { - } - - @Override - public void onLoadBitMap(boolean isLoad) { - adapter.setNotLoadBitmap(isLoad); -// isNotLoadBitmap=isLoad; -// adapter = new KindAdapter(kindList,isNotLoadBitmap); -// mRvKind.setAdapter(adapter); -// mRvKind.setLayoutManager(new LinearLayoutManager(getActivity())); - } } diff --git a/app/src/main/java/com/appstore/uiui/fragment/ManageFragment.java b/app/src/main/java/com/appstore/uiui/fragment/ManageFragment.java index 57fe400..0663961 100644 --- a/app/src/main/java/com/appstore/uiui/fragment/ManageFragment.java +++ b/app/src/main/java/com/appstore/uiui/fragment/ManageFragment.java @@ -7,16 +7,26 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.Handler; +import android.os.Message; import android.view.View; +import android.widget.LinearLayout; import android.widget.TextView; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.appstore.uiui.R; +import com.appstore.uiui.activity.LocalManagerActivity; import com.appstore.uiui.adapter.LocalAppAdapter; +import com.appstore.uiui.adapter.UpdateAppAdapter; import com.appstore.uiui.base.LazyLoadFragment; +import com.appstore.uiui.bean.AppInfo; import com.appstore.uiui.bean.LocalApp; +import com.appstore.uiui.bean.UpdateAppInfo; +import com.appstore.uiui.network.OKGOPost; +import com.appstore.uiui.utils.ApkUtils; +import com.appstore.uiui.utils.ToastUtil; import java.util.ArrayList; import java.util.List; @@ -27,14 +37,16 @@ import java.util.List; * 管理的页面 */ -public class ManageFragment extends LazyLoadFragment implements View.OnClickListener { +public class ManageFragment extends LazyLoadFragment { private RecyclerView mRvLocal; - private TextView mTvUpdateNum; - + private TextView manage_tv_updateNum; private List localAppList; - private LocalAppAdapter adapter; - + private List updateAppInfoList; + private List applist; + private List packageNameList = new ArrayList<>(); + private UpdateAppAdapter adapter; + private LinearLayout manage_ll_localapp; @Override public int getLayoutId() { @@ -43,9 +55,21 @@ public class ManageFragment extends LazyLoadFragment implements View.OnClickList @Override public void initViews(View view) { + manage_tv_updateNum = view.findViewById(R.id.manage_tv_updateNum); + manage_ll_localapp = view.findViewById(R.id.manage_ll_localapp); + manage_ll_localapp.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startActivity(new Intent(getActivity(), LocalManagerActivity.class)); + } + }); mRvLocal = view.findViewById(R.id.manage_rv_local); - mTvUpdateNum = view.findViewById(R.id.manage_tv_updateNum); - initView(); + updateAppInfoList = new ArrayList<>(); + adapter = new UpdateAppAdapter(updateAppInfoList, getContext()); + mRvLocal.setAdapter(adapter); + mRvLocal.setLayoutManager(new LinearLayoutManager(getActivity())); + + initAPPData(handler); } @Override @@ -53,9 +77,12 @@ public class ManageFragment extends LazyLoadFragment implements View.OnClickList } - private void initView() { - localAppList = new ArrayList<>(); + synchronized private void initAPPData(final Handler handler) { + OKGOPost.getAllAppInfo(handler); + } + synchronized private List getLocalApp() { + List appList = new ArrayList<>(); Intent intent = new Intent(Intent.ACTION_MAIN, null); intent.addCategory(Intent.CATEGORY_LAUNCHER); List resolveInfoList = getActivity().getPackageManager().queryIntentActivities(intent, 0); @@ -63,8 +90,6 @@ public class ManageFragment extends LazyLoadFragment implements View.OnClickList for (int i = 0; i < resolveInfoList.size(); i++) { LocalApp bean = new LocalApp(); bean.setAppName(resolveInfoList.get(i).loadLabel(getActivity().getPackageManager()).toString()); - - String packageName = resolveInfoList.get(i).activityInfo.packageName; bean.setPackageName(packageName); Drawable icon = resolveInfoList.get(i).loadIcon(getActivity().getPackageManager()); @@ -75,67 +100,72 @@ public class ManageFragment extends LazyLoadFragment implements View.OnClickList String versionCode = getActivity().getPackageManager() .getPackageInfo(packageName, 0).versionName; bean.setVersion(versionCode); - + bean.setVersionCode(packageInfo.versionCode); if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) <= 0) { //第三方应用 - //判断是否需要进行更新 // isNeedUpdate(bean, i); - + appList.add(bean); } else { //系统应用 } - } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } - - adapter = new LocalAppAdapter(localAppList); - mRvLocal.setAdapter(adapter); - mRvLocal.setLayoutManager(new LinearLayoutManager(getActivity())); - + return appList; } - public void onClick(View view) { - switch (view.getId()) { - case R.id.manage_ll_localapp: -// LocalAppActivity.startActivity(getActivity()); - break; - case R.id.manage_btn_update: - break; + synchronized private void updateDta(List list) { + packageNameList.clear(); + for (AppInfo appInfo : list) { + packageNameList.add(appInfo.getApp_package()); + } + localAppList = getLocalApp(); + if (packageNameList != null && packageNameList.size() > 0) { + if (localAppList != null && localAppList.size() > 0) { + for (LocalApp app : localAppList) { + if (packageNameList.contains(app.getPackageName())) { + AppInfo info = list.get(packageNameList.indexOf(app.getPackageName())); + if (ApkUtils.checkIsUpdate(getContext(), info.getApp_package(), Integer.parseInt(info.getApp_version_code()))) { + UpdateAppInfo updateAppInfo = new UpdateAppInfo(); + updateAppInfo.setVersionCode(app.getVersionCode()); + updateAppInfo.setVersionName(app.getVersion()); + updateAppInfo.setAppName(app.getAppName()); + updateAppInfo.setIcon(app.getIcon()); + updateAppInfo.setNewVersionCode(Integer.parseInt(info.getApp_version_code())); + updateAppInfo.setNewVersionName(info.getApp_version_name()); + updateAppInfo.setURL(info.getApp_url()); + updateAppInfo.setPackageName(info.getApp_package()); + updateAppInfo.setAppInfo(info); + updateAppInfoList.add(updateAppInfo); + } + } + } + } else { + ToastUtil.show("未安装本地应用"); + } + } else { + ToastUtil.show("没有可以下载的在线应用"); } } int updateNum = 0; - //判断某个应用是否需要升级 -// private void isNeedUpdate(final LocalApp localApp, final int position) { -// RetrofitManager.getInstance(getActivity()).getDetailInfo(localApp) -// .subscribeOn(Schedulers.io()) -// .observeOn(AndroidSchedulers.mainThread()) -// .subscribe(new Subscriber() { -// @Override -// public void onCompleted() { -// adapter.notifyDataSetChanged(); -// } -// -// @Override -// public void onError(Throwable e) { -// -// } -// -// @Override -// public void onNext(Boolean aBoolean) { -// localApp.setNeedUpdate(aBoolean); -// if (aBoolean) { -// localAppList.add(localApp); -// ++updateNum; -// mTvUpdateNum.setText(updateNum + "个应用可以升级"); -// } -// } -// }); -// } - + Handler handler = new Handler() { + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + switch (msg.what) { + case 1: + applist = (List) msg.obj; + updateDta(applist); + updateNum = updateAppInfoList.size(); + manage_tv_updateNum.setText(updateNum + "个应用可以升级"); + adapter.notifyDataSetChanged(); + break; + } + } + }; } diff --git a/app/src/main/java/com/appstore/uiui/fragment/RankFragment.java b/app/src/main/java/com/appstore/uiui/fragment/RankFragment.java index f992ff2..7149f56 100644 --- a/app/src/main/java/com/appstore/uiui/fragment/RankFragment.java +++ b/app/src/main/java/com/appstore/uiui/fragment/RankFragment.java @@ -64,7 +64,7 @@ public class RankFragment extends LazyLoadFragment implements RefreshManager.Ref isNotLoadBitmap = false; mAppInfoList = new ArrayList<>(); - mAppAdapter = new AppAdapter(mAppInfoList, true, isNotLoadBitmap); + mAppAdapter = new AppAdapter(mAppInfoList, true, getContext()); mRvResult.setLayoutManager(new LinearLayoutManager(getActivity())); mRvResult.setAdapter(mAppAdapter); diff --git a/app/src/main/java/com/appstore/uiui/helper/CustomSnapHelper.java b/app/src/main/java/com/appstore/uiui/helper/CustomSnapHelper.java new file mode 100644 index 0000000..347d6c8 --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/helper/CustomSnapHelper.java @@ -0,0 +1,82 @@ +package com.appstore.uiui.helper; + +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.LinearSnapHelper; +import androidx.recyclerview.widget.OrientationHelper; +import androidx.recyclerview.widget.RecyclerView; + +/** + * Created by asus on 2017/10/26. + * 自定义snapHelper和recyclerView,实现类似ViewPager滑动 + * 图片轮播 + */ + +public class CustomSnapHelper extends LinearSnapHelper { + + private OrientationHelper mHorizontalHelper; + + @Nullable + @Override + public int[] calculateDistanceToFinalSnap(RecyclerView.LayoutManager layoutManager, View targetView) { + int[] out = new int[2]; + if (layoutManager.canScrollHorizontally()) { + out[0] = distanceToStart(targetView, getHorizontalHelper(layoutManager)); + } else { + out[0] = 0; + } + + return out; + } + + private int distanceToStart(View targetView, OrientationHelper helper) { + return helper.getDecoratedStart(targetView) - helper.getStartAfterPadding(); + } + + @Nullable + @Override + public View findSnapView(RecyclerView.LayoutManager layoutManager) { + return findStartView(layoutManager, getHorizontalHelper(layoutManager)); + } + + private View findStartView(RecyclerView.LayoutManager layoutManager, + OrientationHelper helper) { + + if (layoutManager instanceof LinearLayoutManager) { + int firstChild = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); + int lastChild = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); + if (firstChild == RecyclerView.NO_POSITION) { + return null; + } + if (lastChild == layoutManager.getItemCount() - 1) { + return layoutManager.findViewByPosition(lastChild); + } + + View child = layoutManager.findViewByPosition(firstChild); + + if (helper.getDecoratedEnd(child) >= helper.getDecoratedMeasurement(child) / 2 + && helper.getDecoratedEnd(child) > 0) { + return child; + } else { + return layoutManager.findViewByPosition(firstChild + 1); + } + } + + return super.findSnapView(layoutManager); + } + + + private OrientationHelper getHorizontalHelper( + @NonNull RecyclerView.LayoutManager layoutManager) { + if (mHorizontalHelper == null) { + mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager); + } + return mHorizontalHelper; + } + + + +} diff --git a/app/src/main/java/com/appstore/uiui/jpush/ExampleUtil.java b/app/src/main/java/com/appstore/uiui/jpush/ExampleUtil.java new file mode 100644 index 0000000..f27da36 --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/jpush/ExampleUtil.java @@ -0,0 +1,133 @@ +package com.appstore.uiui.jpush; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Bundle; +import android.os.Looper; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.widget.Toast; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import cn.jpush.android.api.JPushInterface; + +public class ExampleUtil { + public static final String PREFS_NAME = "JPUSH_EXAMPLE"; + public static final String PREFS_DAYS = "JPUSH_EXAMPLE_DAYS"; + public static final String PREFS_START_TIME = "PREFS_START_TIME"; + public static final String PREFS_END_TIME = "PREFS_END_TIME"; + public static final String KEY_APP_KEY = "JPUSH_APPKEY"; + + public static boolean isEmpty(String s) { + if (null == s) + return true; + if (s.length() == 0) + return true; + if (s.trim().length() == 0) + return true; + return false; + } + /** + * 只能以 “+” 或者 数字开头;后面的内容只能包含 “-” 和 数字。 + * */ + private final static String MOBILE_NUMBER_CHARS = "^[+0-9][-0-9]{1,}$"; + public static boolean isValidMobileNumber(String s) { + if(TextUtils.isEmpty(s)) return true; + Pattern p = Pattern.compile(MOBILE_NUMBER_CHARS); + Matcher m = p.matcher(s); + return m.matches(); + } + // 校验Tag Alias 只能是数字,英文字母和中文 + public static boolean isValidTagAndAlias(String s) { + Pattern p = Pattern.compile("^[\u4E00-\u9FA50-9a-zA-Z_!@#$&*+=.|]+$"); + Matcher m = p.matcher(s); + return m.matches(); + } + + // 取得AppKey + public static String getAppKey(Context context) { + Bundle metaData = null; + String appKey = null; + try { + ApplicationInfo ai = context.getPackageManager().getApplicationInfo( + context.getPackageName(), PackageManager.GET_META_DATA); + if (null != ai) + metaData = ai.metaData; + if (null != metaData) { + appKey = metaData.getString(KEY_APP_KEY); + if ((null == appKey) || appKey.length() != 24) { + appKey = null; + } + } + } catch (NameNotFoundException e) { + + } + return appKey; + } + + // 取得版本号 + public static String GetVersion(Context context) { + try { + PackageInfo manager = context.getPackageManager().getPackageInfo( + context.getPackageName(), 0); + return manager.versionName; + } catch (NameNotFoundException e) { + return "Unknown"; + } + } + + public static void showToast(final String toast, final Context context) + { + new Thread(new Runnable() { + + @Override + public void run() { + Looper.prepare(); +// Toast.makeText(context, toast, Toast.LENGTH_SHORT).show(); + Looper.loop(); + } + }).start(); + } + + public static boolean isConnected(Context context) { + ConnectivityManager conn = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo info = conn.getActiveNetworkInfo(); + return (info != null && info.isConnected()); + } + + public static String getImei(Context context, String imei) { + String ret = null; + try { + TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + ret = telephonyManager.getDeviceId(); + } catch (Exception e) { + Logger.e(ExampleUtil.class.getSimpleName(), e.getMessage()); + } + if (isReadableASCII(ret)){ + return ret; + } else { + return imei; + } + } + + private static boolean isReadableASCII(CharSequence string){ + if (TextUtils.isEmpty(string)) return false; + try { + Pattern p = Pattern.compile("[\\x20-\\x7E]+"); + return p.matcher(string).matches(); + } catch (Throwable e){ + return true; + } + } + + public static String getDeviceId(Context context) { + return JPushInterface.getUdid(context); + } +} diff --git a/app/src/main/java/com/appstore/uiui/jpush/Invalid/ExampleApplication.java b/app/src/main/java/com/appstore/uiui/jpush/Invalid/ExampleApplication.java new file mode 100644 index 0000000..cd004f2 --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/jpush/Invalid/ExampleApplication.java @@ -0,0 +1,25 @@ +package com.appstore.uiui.jpush.Invalid; + +import android.app.Application; + +import com.appstore.uiui.jpush.Logger; + +import cn.jpush.android.api.JPushInterface; + +/** + * For developer startup JPush SDK + * + * 一般建议在自定义 Application 类里初始化。也可以在主 Activity 里。 + */ +public class ExampleApplication extends Application { + private static final String TAG = "JIGUANG-Example"; + + @Override + public void onCreate() { + Logger.d(TAG, "[ExampleApplication] onCreate"); + super.onCreate(); + + JPushInterface.setDebugMode(true); // 设置开启日志,发布时请关闭日志 + JPushInterface.init(this); // 初始化 JPush + } +} diff --git a/app/src/main/java/com/appstore/uiui/jpush/Invalid/MainActivity.java b/app/src/main/java/com/appstore/uiui/jpush/Invalid/MainActivity.java new file mode 100644 index 0000000..fec9c74 --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/jpush/Invalid/MainActivity.java @@ -0,0 +1,181 @@ +package com.appstore.uiui.jpush.Invalid; + + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import cn.jpush.android.api.InstrumentedActivity; +import cn.jpush.android.api.JPushInterface; + import com.appstore.uiui.R; + + + +//public class MainActivity extends InstrumentedActivity implements OnClickListener{ +// +// private Button mInit; +// private Button mSetting; +// private Button mStopPush; +// private Button mResumePush; +// private Button mGetRid; +// private TextView mRegId; +// private EditText msgText; +// +// public static boolean isForeground = false; +// @Override +// public void onCreate(Bundle savedInstanceState) { +// super.onCreate(savedInstanceState); +// setContentView(R.layout.main); +// initView(); +// registerMessageReceiver(); // used for receive msg +// } +// +// private void initView(){ +// TextView mImei = (TextView) findViewById(R.id.tv_imei); +// String udid = ExampleUtil.getImei(getApplicationContext(), ""); +// if (null != udid) mImei.setText("IMEI: " + udid); +// +// TextView mAppKey = (TextView) findViewById(R.id.tv_appkey); +// String appKey = ExampleUtil.getAppKey(getApplicationContext()); +// if (null == appKey) appKey = "AppKey异常"; +// mAppKey.setText("AppKey: " + appKey); +// +// mRegId = (TextView) findViewById(R.id.tv_regId); +// mRegId.setText("RegId:"); +// +// String packageName = getPackageName(); +// TextView mPackage = (TextView) findViewById(R.id.tv_package); +// mPackage.setText("PackageName: " + packageName); +// +// String deviceId = ExampleUtil.getDeviceId(getApplicationContext()); +// TextView mDeviceId = (TextView) findViewById(R.id.tv_device_id); +// mDeviceId.setText("deviceId:" + deviceId); +// +// String versionName = ExampleUtil.GetVersion(getApplicationContext()); +// TextView mVersion = (TextView) findViewById(R.id.tv_version); +// mVersion.setText("Version: " + versionName); +// +// mInit = (Button)findViewById(R.id.init); +// mInit.setOnClickListener(this); +// +// mStopPush = (Button)findViewById(R.id.stopPush); +// mStopPush.setOnClickListener(this); +// +// mResumePush = (Button)findViewById(R.id.resumePush); +// mResumePush.setOnClickListener(this); +// +// mGetRid = (Button) findViewById(R.id.getRegistrationId); +// mGetRid.setOnClickListener(this); +// +// mSetting = (Button)findViewById(R.id.setting); +// mSetting.setOnClickListener(this); +// +// msgText = (EditText)findViewById(R.id.msg_rec); +// } +// +// +// @Override +// public void onClick(View v) { +// switch (v.getId()) { +// case R.id.init: +// init(); +// break; +// case R.id.setting: +// Intent intent = new Intent(MainActivity.this, PushSetActivity.class); +// startActivity(intent); +// break; +// case R.id.stopPush: +// JPushInterface.stopPush(getApplicationContext()); +// break; +// case R.id.resumePush: +// JPushInterface.resumePush(getApplicationContext()); +// break; +// case R.id.getRegistrationId: +// String rid = JPushInterface.getRegistrationID(getApplicationContext()); +// if (!rid.isEmpty()) { +// mRegId.setText("RegId:" + rid); +// } else { +// Toast.makeText(this, "Get registration fail, JPush init failed!", Toast.LENGTH_SHORT).show(); +// } +// break; +// } +// } +// +// // 初始化 JPush。如果已经初始化,但没有登录成功,则执行重新登录。 +// private void init(){ +// JPushInterface.init(getApplicationContext()); +// } +// +// +// @Override +// protected void onResume() { +// isForeground = true; +// super.onResume(); +// } +// +// +// @Override +// protected void onPause() { +// isForeground = false; +// super.onPause(); +// } +// +// +// @Override +// protected void onDestroy() { +// LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver); +// super.onDestroy(); +// } +// +// +// //for receive customer msg from jpush server +// private MessageReceiver mMessageReceiver; +// public static final String MESSAGE_RECEIVED_ACTION = "com.example.jpushdemo.MESSAGE_RECEIVED_ACTION"; +// public static final String KEY_TITLE = "title"; +// public static final String KEY_MESSAGE = "message"; +// public static final String KEY_EXTRAS = "extras"; +// +// public void registerMessageReceiver() { +// mMessageReceiver = new MessageReceiver(); +// IntentFilter filter = new IntentFilter(); +// filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); +// filter.addAction(MESSAGE_RECEIVED_ACTION); +// LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, filter); +// } +// +// public class MessageReceiver extends BroadcastReceiver { +// +// @Override +// public void onReceive(Context context, Intent intent) { +// try { +// if (MESSAGE_RECEIVED_ACTION.equals(intent.getAction())) { +// String messge = intent.getStringExtra(KEY_MESSAGE); +// String extras = intent.getStringExtra(KEY_EXTRAS); +// StringBuilder showMsg = new StringBuilder(); +// showMsg.append(KEY_MESSAGE + " : " + messge + "\n"); +// if (!ExampleUtil.isEmpty(extras)) { +// showMsg.append(KEY_EXTRAS + " : " + extras + "\n"); +// } +// setCostomMsg(showMsg.toString()); +// } +// } catch (Exception e){ +// } +// } +// } +// +// private void setCostomMsg(String msg){ +// if (null != msgText) { +// msgText.setText(msg); +// msgText.setVisibility(View.VISIBLE); +// } +// } + +//} \ No newline at end of file diff --git a/app/src/main/java/com/appstore/uiui/jpush/Invalid/PushSetActivity.java b/app/src/main/java/com/appstore/uiui/jpush/Invalid/PushSetActivity.java new file mode 100644 index 0000000..c5709b8 --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/jpush/Invalid/PushSetActivity.java @@ -0,0 +1,265 @@ +package com.appstore.uiui.jpush.Invalid; + +//public class PushSetActivity extends InstrumentedActivity implements OnClickListener { +// private static final String TAG = "JIGUANG-Example"; +// +// @Override +// public void onCreate(Bundle icicle) { +// super.onCreate(icicle); +// setContentView(R.layout.push_set_dialog); +// initListener(); +// } +// +// private void initListener() { +// //增加tag +// findViewById(R.id.bt_addtag).setOnClickListener(this); +// //设置tag +// findViewById(R.id.bt_settag).setOnClickListener(this); +// //删除tag +// findViewById(R.id.bt_deletetag).setOnClickListener(this); +// //获取所有tag +// findViewById(R.id.bt_getalltag).setOnClickListener(this); +// //清除所有tag +// findViewById(R.id.bt_cleantag).setOnClickListener(this); +// //查询tag绑定状态 +// findViewById(R.id.bt_checktag).setOnClickListener(this); +// +// //设置alias +// findViewById(R.id.bt_setalias).setOnClickListener(this); +// //获取alias +// findViewById(R.id.bt_getalias).setOnClickListener(this); +// //删除alias +// findViewById(R.id.bt_deletealias).setOnClickListener(this); +// //设置手机号码 +// findViewById(R.id.bt_setmobileNumber).setOnClickListener(this); +// //StyleAddActions +// findViewById(R.id.setStyle0).setOnClickListener(this); +// //StyleBasic +// findViewById(R.id.setStyle1).setOnClickListener(this); +// //StyleCustom +// findViewById(R.id.setStyle2).setOnClickListener(this); +// //SetPushTime +// findViewById(R.id.bu_setTime).setOnClickListener(this); +// } +// +// @Override +// public void onClick(View view) { +// switch (view.getId()) { +// case R.id.setStyle0: +// setAddActionsStyle(); +// break; +// case R.id.setStyle1: +// setStyleBasic(); +// break; +// case R.id.setStyle2: +// setStyleCustom(); +// break; +// case R.id.bu_setTime: +// Intent intent = new Intent(PushSetActivity.this, SettingActivity.class); +// startActivity(intent); +// break; +// default: +// onTagAliasAction(view); +// break; +// } +// } +// +// TagAliasCallback tagAlias = new TagAliasCallback() { +// @Override +// public void gotResult(int responseCode, String alias, Set tags) { +// Log.e(TAG,"responseCode:"+responseCode+",alias:"+alias+",tags:"+tags); +// } +// }; +// +// +// /** +// * 设置通知提示方式 - 基础属性 +// */ +// private void setStyleBasic() { +// BasicPushNotificationBuilder builder = new BasicPushNotificationBuilder(PushSetActivity.this); +// builder.statusBarDrawable = R.drawable.ic_launcher; +// builder.notificationFlags = Notification.FLAG_AUTO_CANCEL; //设置为点击后自动消失 +// builder.notificationDefaults = Notification.DEFAULT_SOUND; //设置为铃声( Notification.DEFAULT_SOUND)或者震动( Notification.DEFAULT_VIBRATE) +// JPushInterface.setPushNotificationBuilder(1, builder); +// Toast.makeText(PushSetActivity.this, "Basic Builder - 1", Toast.LENGTH_SHORT).show(); +// } +// +// +// /** +// * 设置通知栏样式 - 定义通知栏Layout +// */ +// private void setStyleCustom() { +// CustomPushNotificationBuilder builder = new CustomPushNotificationBuilder(PushSetActivity.this, R.layout.customer_notitfication_layout, R.id.icon, R.id.title, R.id.text); +// builder.layoutIconDrawable = R.drawable.ic_launcher; +// builder.developerArg0 = "developerArg2"; +// JPushInterface.setPushNotificationBuilder(2, builder); +// Toast.makeText(PushSetActivity.this, "Custom Builder - 2", Toast.LENGTH_SHORT).show(); +// } +// +// @Override +// public boolean onKeyDown(int keyCode, KeyEvent event) { +// if (keyCode == KeyEvent.KEYCODE_BACK) { +// finish(); +// } +// return super.onKeyDown(keyCode, event); +// } +// +// private void setAddActionsStyle() { +// MultiActionsNotificationBuilder builder = new MultiActionsNotificationBuilder(PushSetActivity.this); +// builder.addJPushAction(R.drawable.jpush_ic_richpush_actionbar_back, "first", "my_extra1"); +// builder.addJPushAction(R.drawable.jpush_ic_richpush_actionbar_back, "second", "my_extra2"); +// builder.addJPushAction(R.drawable.jpush_ic_richpush_actionbar_back, "third", "my_extra3"); +// JPushInterface.setPushNotificationBuilder(10, builder); +// +// Toast.makeText(PushSetActivity.this, "AddActions Builder - 10", Toast.LENGTH_SHORT).show(); +// } +// +// +// /**===========================================================================**/ +// /**=========================TAG/ALIAS 相关=====================================**/ +// /**===========================================================================**/ +// +// /** +// * 处理tag/alias相关操作的点击 +// * */ +// public void onTagAliasAction(View view) { +// Set tags = null; +// String alias = null; +// int action = -1; +// boolean isAliasAction = false; +// switch (view.getId()){ +// //设置手机号码: +// case R.id.bt_setmobileNumber: +// handleSetMobileNumber(); +// return; +// //增加tag +// case R.id.bt_addtag: +// tags = getInPutTags(); +// if(tags == null){ +// return; +// } +// action = ACTION_ADD; +// break; +// //设置tag +// case R.id.bt_settag: +// tags = getInPutTags(); +// if(tags == null){ +// return; +// } +// action = ACTION_SET; +// break; +// //删除tag +// case R.id.bt_deletetag: +// tags = getInPutTags(); +// if(tags == null){ +// return; +// } +// action = ACTION_DELETE; +// break; +// //获取所有tag +// case R.id.bt_getalltag: +// action = ACTION_GET; +// break; +// //清除所有tag +// case R.id.bt_cleantag: +// action = ACTION_CLEAN; +// break; +// case R.id.bt_checktag: +// tags = getInPutTags(); +// if(tags == null){ +// return; +// } +// action = ACTION_CHECK; +// break; +// //设置alias +// case R.id.bt_setalias: +// alias = getInPutAlias(); +// if(TextUtils.isEmpty(alias)){ +// return; +// } +// isAliasAction = true; +// action = ACTION_SET; +// break; +// //获取alias +// case R.id.bt_getalias: +// isAliasAction = true; +// action = ACTION_GET; +// break; +// //删除alias +// case R.id.bt_deletealias: +// isAliasAction = true; +// action = ACTION_DELETE; +// break; +// default: +// return; +// } +// TagAliasBean tagAliasBean = new TagAliasBean(); +// tagAliasBean.action = action; +// sequence++; +// if(isAliasAction){ +// tagAliasBean.alias = alias; +// }else{ +// tagAliasBean.tags = tags; +// } +// tagAliasBean.isAliasAction = isAliasAction; +// TagAliasOperatorHelper.getInstance().handleAction(getApplicationContext(),sequence,tagAliasBean); +// } +// +// private void handleSetMobileNumber(){ +// EditText mobileEdit = (EditText) findViewById(R.id.et_mobilenumber); +// String mobileNumber = mobileEdit.getText().toString().trim(); +// if (TextUtils.isEmpty(mobileNumber)) { +// Toast.makeText(getApplicationContext(), R.string.mobilenumber_empty_guide, Toast.LENGTH_SHORT).show(); +// } +// if (!ExampleUtil.isValidMobileNumber(mobileNumber)) { +// Toast.makeText(getApplicationContext(), R.string.error_tag_gs_empty, Toast.LENGTH_SHORT).show(); +// return; +// } +// sequence++; +// TagAliasOperatorHelper.getInstance().handleAction(getApplicationContext(),sequence,mobileNumber); +// } +// /** +// * 获取输入的alias +// * */ +// private String getInPutAlias(){ +// EditText aliasEdit = (EditText) findViewById(R.id.et_alias); +// String alias = aliasEdit.getText().toString().trim(); +// if (TextUtils.isEmpty(alias)) { +// Toast.makeText(getApplicationContext(), R.string.error_alias_empty, Toast.LENGTH_SHORT).show(); +// return null; +// } +// if (!ExampleUtil.isValidTagAndAlias(alias)) { +// Toast.makeText(getApplicationContext(), R.string.error_tag_gs_empty, Toast.LENGTH_SHORT).show(); +// return null; +// } +// return alias; +// } +// /** +// * 获取输入的tags +// * */ +// private Set getInPutTags(){ +// EditText tagEdit = (EditText) findViewById(R.id.et_tag); +// String tag = tagEdit.getText().toString().trim(); +// // 检查 tag 的有效性 +// if (TextUtils.isEmpty(tag)) { +// Toast.makeText(getApplicationContext(), R.string.error_tag_empty, Toast.LENGTH_SHORT).show(); +// return null; +// } +// +// // ","隔开的多个 转换成 Set +// String[] sArray = tag.split(","); +// Set tagSet = new LinkedHashSet(); +// for (String sTagItme : sArray) { +// if (!ExampleUtil.isValidTagAndAlias(sTagItme)) { +// Toast.makeText(getApplicationContext(), R.string.error_tag_gs_empty, Toast.LENGTH_SHORT).show(); +// return null; +// } +// tagSet.add(sTagItme); +// } +// if(tagSet.isEmpty()){ +// Toast.makeText(getApplicationContext(), R.string.error_tag_empty, Toast.LENGTH_SHORT).show(); +// return null; +// } +// return tagSet; +// } +//} \ No newline at end of file diff --git a/app/src/main/java/com/appstore/uiui/jpush/Invalid/SettingActivity.java b/app/src/main/java/com/appstore/uiui/jpush/Invalid/SettingActivity.java new file mode 100644 index 0000000..1348751 --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/jpush/Invalid/SettingActivity.java @@ -0,0 +1,197 @@ +package com.appstore.uiui.jpush.Invalid; + +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.os.Bundle; +import android.text.TextUtils; +import android.text.format.DateFormat; +import android.view.KeyEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.TimePicker; +import android.widget.Toast; + +import java.util.HashSet; +import java.util.Set; + +import cn.jpush.android.api.InstrumentedActivity; +import cn.jpush.android.api.JPushInterface; + import com.appstore.uiui.R; + +//public class SettingActivity extends InstrumentedActivity implements OnClickListener { +// TimePicker startTime; +// TimePicker endTime; +// CheckBox mMonday ; +// CheckBox mTuesday ; +// CheckBox mWednesday; +// CheckBox mThursday; +// CheckBox mFriday ; +// CheckBox mSaturday; +// CheckBox mSunday ; +// Button mSetTime; +// SharedPreferences mSettings; +// Editor mEditor; +// +// @Override +// public void onCreate(Bundle icicle) { +// super.onCreate(icicle); +// setContentView(R.layout.set_push_time); +// init(); +// initListener(); +// } +// +// @Override +// public void onStart() { +// super.onStart(); +// initData(); +// } +// +// private void init(){ +// startTime = (TimePicker) findViewById(R.id.start_time); +// endTime = (TimePicker) findViewById(R.id.end_time); +// startTime.setIs24HourView(DateFormat.is24HourFormat(this)); +// endTime.setIs24HourView(DateFormat.is24HourFormat(this)); +// mSetTime = (Button)findViewById(R.id.bu_setTime); +// mMonday = (CheckBox)findViewById(R.id.cb_monday); +// mTuesday = (CheckBox)findViewById(R.id.cb_tuesday); +// mWednesday = (CheckBox)findViewById(R.id.cb_wednesday); +// mThursday = (CheckBox)findViewById(R.id.cb_thursday); +// mFriday = (CheckBox)findViewById(R.id.cb_friday); +// mSaturday = (CheckBox)findViewById(R.id.cb_saturday); +// mSunday = (CheckBox)findViewById(R.id.cb_sunday); +// } +// +// private void initListener(){ +// mSetTime.setOnClickListener(this); +// } +// +// private void initData(){ +// mSettings = getSharedPreferences(ExampleUtil.PREFS_NAME, MODE_PRIVATE); +// String days = mSettings.getString(ExampleUtil.PREFS_DAYS, ""); +// if (!TextUtils.isEmpty(days)) { +// initAllWeek(false); +// String[] sArray = days.split(","); +// for (String day : sArray) { +// setWeek(day); +// } +// } else { +// initAllWeek(true); +// } +// +// int startTimeStr = mSettings.getInt(ExampleUtil.PREFS_START_TIME, 0); +// startTime.setCurrentHour(startTimeStr); +// int endTimeStr = mSettings.getInt(ExampleUtil.PREFS_END_TIME, 23); +// endTime.setCurrentHour(endTimeStr); +// } +// +// @Override +// public void onClick(View v) { +// switch (v.getId()) { +// case R.id.bu_setTime: +// v.requestFocus(); +// v.requestFocusFromTouch(); +// setPushTime(); +// break; +// } +// } +// +// /** +// *设置允许接收通知时间 +// */ +// private void setPushTime(){ +// int startime = startTime.getCurrentHour(); +// int endtime = endTime.getCurrentHour(); +// if (startime > endtime) { +// Toast.makeText(SettingActivity.this, "开始时间不能大于结束时间", Toast.LENGTH_SHORT).show(); +// return; +// } +// StringBuffer daysSB = new StringBuffer(); +// Set days = new HashSet(); +// if (mSunday.isChecked()) { +// days.add(0); +// daysSB.append("0,"); +// } +// if (mMonday.isChecked()) { +// days.add(1); +// daysSB.append("1,"); +// } +// if (mTuesday.isChecked()) { +// days.add(2); +// daysSB.append("2,"); +// } +// if (mWednesday.isChecked()) { +// days.add(3); +// daysSB.append("3,"); +// } +// if (mThursday.isChecked()) { +// days.add(4); +// daysSB.append("4,"); +// } +// if (mFriday.isChecked()) { +// days.add(5); +// daysSB.append("5,"); +// } +// if (mSaturday.isChecked()) { +// days.add(6); +// daysSB.append("6,"); +// } +// +// +// //调用JPush api设置Push时间 +// JPushInterface.setPushTime(getApplicationContext(), days, startime, endtime); +// +// mEditor = mSettings.edit(); +// mEditor.putString(ExampleUtil.PREFS_DAYS, daysSB.toString()); +// mEditor.putInt(ExampleUtil.PREFS_START_TIME, startime); +// mEditor.putInt(ExampleUtil.PREFS_END_TIME, endtime); +// mEditor.commit(); +// Toast.makeText(SettingActivity.this, R.string.setting_su, Toast.LENGTH_SHORT).show(); +// } +// +// @Override +// public boolean onKeyDown(int keyCode, KeyEvent event) { +// if (keyCode == KeyEvent.KEYCODE_BACK){ +// finish(); +// } +// return super.onKeyDown(keyCode, event); +// } +// +// private void setWeek(String day){ +// int dayId = Integer.valueOf(day); +// switch (dayId) { +// case 0: +// mSunday.setChecked(true); +// break; +// case 1: +// mMonday.setChecked(true); +// break; +// case 2: +// mTuesday.setChecked(true); +// break; +// case 3: +// mWednesday.setChecked(true); +// break; +// case 4: +// mThursday.setChecked(true); +// break; +// case 5: +// mFriday.setChecked(true); +// break; +// case 6: +// mSaturday.setChecked(true); +// break; +// } +// } +// +// private void initAllWeek(boolean isChecked) { +// mSunday.setChecked(isChecked); +// mMonday.setChecked(isChecked); +// mTuesday.setChecked(isChecked); +// mWednesday.setChecked(isChecked); +// mThursday.setChecked(isChecked); +// mFriday.setChecked(isChecked); +// mSaturday.setChecked(isChecked); +// } +//} \ No newline at end of file diff --git a/app/src/main/java/com/appstore/uiui/jpush/Invalid/TestActivity.java b/app/src/main/java/com/appstore/uiui/jpush/Invalid/TestActivity.java new file mode 100644 index 0000000..1fed3ad --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/jpush/Invalid/TestActivity.java @@ -0,0 +1,32 @@ +package com.appstore.uiui.jpush.Invalid; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.view.ViewGroup.LayoutParams; +import android.widget.TextView; + +import cn.jpush.android.api.JPushInterface; + +public class TestActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + TextView tv = new TextView(this); + tv.setText("用户自定义打开的Activity"); + Intent intent = getIntent(); + if (null != intent) { + Bundle bundle = getIntent().getExtras(); + String title = null; + String content = null; + if(bundle!=null){ + title = bundle.getString(JPushInterface.EXTRA_NOTIFICATION_TITLE); + content = bundle.getString(JPushInterface.EXTRA_ALERT); + } + tv.setText("Title : " + title + " " + "Content : " + content); + } + addContentView(tv, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); + } + +} diff --git a/app/src/main/java/com/appstore/uiui/jpush/LocalBroadcastManager.java b/app/src/main/java/com/appstore/uiui/jpush/LocalBroadcastManager.java new file mode 100644 index 0000000..a3bee04 --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/jpush/LocalBroadcastManager.java @@ -0,0 +1,263 @@ +package com.appstore.uiui.jpush; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.Uri; +import android.os.Handler; +import android.os.Message; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Set; + +/** + * Created by efan on 2017/4/14. + */ + +public final class LocalBroadcastManager { + private static final String TAG = "JIGUANG-Example"; + private static final boolean DEBUG = false; + private final Context mAppContext; + private final HashMap> mReceivers = new HashMap>(); + private final HashMap> mActions = new HashMap> (); + private final ArrayList mPendingBroadcasts = new ArrayList(); + static final int MSG_EXEC_PENDING_BROADCASTS = 1; + private final Handler mHandler; + private static final Object mLock = new Object(); + private static LocalBroadcastManager mInstance; + + public static LocalBroadcastManager getInstance(Context context) { + Object var1 = mLock; + synchronized (mLock) { + if (mInstance == null) { + mInstance = new LocalBroadcastManager(context.getApplicationContext()); + } + + return mInstance; + } + } + + private LocalBroadcastManager(Context context) { + this.mAppContext = context; + this.mHandler = new Handler(context.getMainLooper()) { + public void handleMessage(Message msg) { + switch (msg.what) { + case 1: + LocalBroadcastManager.this.executePendingBroadcasts(); + break; + default: + super.handleMessage(msg); + } + + } + }; + } + + public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { + HashMap var3 = this.mReceivers; + synchronized (this.mReceivers) { + ReceiverRecord entry = new ReceiverRecord(filter, receiver); + ArrayList filters = (ArrayList) this.mReceivers.get(receiver); + if (filters == null) { + filters = new ArrayList(1); + this.mReceivers.put(receiver, filters); + } + + filters.add(filter); + + for (int i = 0; i < filter.countActions(); ++i) { + String action = filter.getAction(i); + ArrayList entries = (ArrayList) this.mActions.get(action); + if (entries == null) { + entries = new ArrayList(1); + this.mActions.put(action, entries); + } + + entries.add(entry); + } + + } + } + + public void unregisterReceiver(BroadcastReceiver receiver) { + HashMap var2 = this.mReceivers; + synchronized (this.mReceivers) { + ArrayList filters = (ArrayList) this.mReceivers.remove(receiver); + if (filters != null) { + for (int i = 0; i < filters.size(); ++i) { + IntentFilter filter = (IntentFilter) filters.get(i); + + for (int j = 0; j < filter.countActions(); ++j) { + String action = filter.getAction(j); + ArrayList receivers = (ArrayList) this.mActions.get(action); + if (receivers != null) { + for (int k = 0; k < receivers.size(); ++k) { + if (((ReceiverRecord) receivers.get(k)).receiver == receiver) { + receivers.remove(k); + --k; + } + } + + if (receivers.size() <= 0) { + this.mActions.remove(action); + } + } + } + } + + } + } + } + + public boolean sendBroadcast(Intent intent) { + HashMap var2 = this.mReceivers; + synchronized (this.mReceivers) { + String action = intent.getAction(); + String type = intent.resolveTypeIfNeeded(this.mAppContext.getContentResolver()); + Uri data = intent.getData(); + String scheme = intent.getScheme(); + Set categories = intent.getCategories(); + boolean debug = (intent.getFlags() & 8) != 0; + if (debug) { + Logger.v("LocalBroadcastManager", "Resolving type " + type + " scheme " + scheme + " of intent " + intent); + } + + ArrayList entries = (ArrayList) this.mActions.get(intent.getAction()); + if (entries != null) { + if (debug) { + Logger.v("LocalBroadcastManager", "Action list: " + entries); + } + + ArrayList receivers = null; + + int i; + for (i = 0; i < entries.size(); ++i) { + ReceiverRecord receiver = (ReceiverRecord) entries.get(i); + if (debug) { + Logger.v("LocalBroadcastManager", "Matching against filter " + receiver.filter); + } + + if (receiver.broadcasting) { + if (debug) { + Logger.v("LocalBroadcastManager", " Filter\'s target already added"); + } + } else { + int match = receiver.filter.match(action, type, scheme, data, categories, "LocalBroadcastManager"); + if (match >= 0) { + if (debug) { + Logger.v("LocalBroadcastManager", " Filter matched! match=0x" + Integer.toHexString(match)); + } + + if (receivers == null) { + receivers = new ArrayList(); + } + + receivers.add(receiver); + receiver.broadcasting = true; + } else if (debug) { + String reason; + switch (match) { + case -4: + reason = "category"; + break; + case -3: + reason = "action"; + break; + case -2: + reason = "data"; + break; + case -1: + reason = "type"; + break; + default: + reason = "unknown reason"; + } + + Logger.v("LocalBroadcastManager", " Filter did not match: " + reason); + } + } + } + + if (receivers != null) { + for (i = 0; i < receivers.size(); ++i) { + ((ReceiverRecord) receivers.get(i)).broadcasting = false; + } + + this.mPendingBroadcasts.add(new BroadcastRecord(intent, receivers)); + if (!this.mHandler.hasMessages(1)) { + this.mHandler.sendEmptyMessage(1); + } + + return true; + } + } + + return false; + } + } + + public void sendBroadcastSync(Intent intent) { + if (this.sendBroadcast(intent)) { + this.executePendingBroadcasts(); + } + + } + + private void executePendingBroadcasts() { + while (true) { + BroadcastRecord[] brs = null; + HashMap i = this.mReceivers; + synchronized (this.mReceivers) { + int br = this.mPendingBroadcasts.size(); + if (br <= 0) { + return; + } + + brs = new BroadcastRecord[br]; + this.mPendingBroadcasts.toArray(brs); + this.mPendingBroadcasts.clear(); + } + + for (int var6 = 0; var6 < brs.length; ++var6) { + BroadcastRecord var7 = brs[var6]; + + for (int j = 0; j < var7.receivers.size(); ++j) { + ((ReceiverRecord) var7.receivers.get(j)).receiver.onReceive(this.mAppContext, var7.intent); + } + } + } + } + + private static class BroadcastRecord { + final Intent intent; + final ArrayList receivers; + + BroadcastRecord(Intent _intent, ArrayList _receivers) { + this.intent = _intent; + this.receivers = _receivers; + } + } + + private static class ReceiverRecord { + final IntentFilter filter; + final BroadcastReceiver receiver; + boolean broadcasting; + + ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) { + this.filter = _filter; + this.receiver = _receiver; + } + + public String toString() { + StringBuilder builder = new StringBuilder(128); + builder.append("Receiver{"); + builder.append(this.receiver); + builder.append(" filter="); + builder.append(this.filter); + builder.append("}"); + return builder.toString(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/appstore/uiui/jpush/Logger.java b/app/src/main/java/com/appstore/uiui/jpush/Logger.java new file mode 100644 index 0000000..9c65cae --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/jpush/Logger.java @@ -0,0 +1,40 @@ +package com.appstore.uiui.jpush; + +import android.util.Log; + +/** + * Created by efan on 2017/4/13. + */ + +public class Logger { + + //设为false关闭日志 + private static final boolean LOG_ENABLE = true; + + public static void i(String tag, String msg){ + if (LOG_ENABLE){ + Log.i(tag, msg); + } + } + public static void v(String tag, String msg){ + if (LOG_ENABLE){ + Log.v(tag, msg); + } + } + public static void d(String tag, String msg){ + if (LOG_ENABLE){ + Log.d(tag, msg); + } + } + public static void w(String tag, String msg){ + if (LOG_ENABLE){ + Log.w(tag, msg); + } + } + public static void e(String tag, String msg){ + if (LOG_ENABLE){ + Log.e(tag, msg); + } + } + +} diff --git a/app/src/main/java/com/appstore/uiui/jpush/MyJPushMessageReceiver.java b/app/src/main/java/com/appstore/uiui/jpush/MyJPushMessageReceiver.java new file mode 100644 index 0000000..dcc513d --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/jpush/MyJPushMessageReceiver.java @@ -0,0 +1,44 @@ +package com.appstore.uiui.jpush; + +import android.content.Context; + +import com.appstore.uiui.MyApplication; + +import cn.jpush.android.api.CustomMessage; +import cn.jpush.android.api.JPushMessage; +import cn.jpush.android.service.JPushMessageReceiver; + +/** + * 自定义JPush message 接收器,包括操作tag/alias的结果返回(仅仅包含tag/alias新接口部分) + * */ +public class MyJPushMessageReceiver extends JPushMessageReceiver { + + @Override + public void onTagOperatorResult(Context context,JPushMessage jPushMessage) { + TagAliasOperatorHelper.getInstance().onTagOperatorResult(context,jPushMessage); + super.onTagOperatorResult(context, jPushMessage); + } + @Override + public void onCheckTagOperatorResult(Context context,JPushMessage jPushMessage){ + TagAliasOperatorHelper.getInstance().onCheckTagOperatorResult(context,jPushMessage); + super.onCheckTagOperatorResult(context, jPushMessage); + } + @Override + public void onAliasOperatorResult(Context context, JPushMessage jPushMessage) { + TagAliasOperatorHelper.getInstance().onAliasOperatorResult(context,jPushMessage); + super.onAliasOperatorResult(context, jPushMessage); + + } + + @Override + public void onMobileNumberOperatorResult(Context context, JPushMessage jPushMessage) { + TagAliasOperatorHelper.getInstance().onMobileNumberOperatorResult(context,jPushMessage); + super.onMobileNumberOperatorResult(context, jPushMessage); + } + + @Override + public void onMessage(Context context, CustomMessage customMessage) { + super.onMessage(context, customMessage); + MyApplication.getInstance().manageCustomMessage(customMessage); + } +} diff --git a/app/src/main/java/com/appstore/uiui/jpush/MyReceiver.java b/app/src/main/java/com/appstore/uiui/jpush/MyReceiver.java new file mode 100644 index 0000000..6290f7f --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/jpush/MyReceiver.java @@ -0,0 +1,129 @@ +package com.appstore.uiui.jpush; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.text.TextUtils; + +import com.appstore.uiui.activity.MainActivity; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Iterator; + +import cn.jpush.android.api.JPushInterface; + +/** + * 自定义接收器 + * + * 如果不定义这个 Receiver,则: + * 1) 默认用户会打开主界面 + * 2) 接收不到自定义消息 + */ +public class MyReceiver extends BroadcastReceiver { + private static final String TAG = "JIGUANG-Example"; + + @Override + public void onReceive(Context context, Intent intent) { + try { + Bundle bundle = intent.getExtras(); + Logger.d(TAG, "[MyReceiver] onReceive - " + intent.getAction() + ", extras: " + printBundle(bundle)); + + if (JPushInterface.ACTION_REGISTRATION_ID.equals(intent.getAction())) { + String regId = bundle.getString(JPushInterface.EXTRA_REGISTRATION_ID); + Logger.d(TAG, "[MyReceiver] 接收Registration Id : " + regId); + //send the Registration Id to your server... + + } else if (JPushInterface.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) { + Logger.d(TAG, "[MyReceiver] 接收到推送下来的自定义消息: " + bundle.getString(JPushInterface.EXTRA_MESSAGE)); + processCustomMessage(context, bundle); + + } else if (JPushInterface.ACTION_NOTIFICATION_RECEIVED.equals(intent.getAction())) { + Logger.d(TAG, "[MyReceiver] 接收到推送下来的通知"); + int notifactionId = bundle.getInt(JPushInterface.EXTRA_NOTIFICATION_ID); + Logger.d(TAG, "[MyReceiver] 接收到推送下来的通知的ID: " + notifactionId); + + } else if (JPushInterface.ACTION_NOTIFICATION_OPENED.equals(intent.getAction())) { + Logger.d(TAG, "[MyReceiver] 用户点击打开了通知"); + + //打开自定义的Activity +// Intent i = new Intent(context, TestActivity.class); +// i.putExtras(bundle); + //i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); +// i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP ); +// context.startActivity(i); + + } else if (JPushInterface.ACTION_RICHPUSH_CALLBACK.equals(intent.getAction())) { + Logger.d(TAG, "[MyReceiver] 用户收到到RICH PUSH CALLBACK: " + bundle.getString(JPushInterface.EXTRA_EXTRA)); + //在这里根据 JPushInterface.EXTRA_EXTRA 的内容处理代码,比如打开新的Activity, 打开一个网页等.. + + } else if(JPushInterface.ACTION_CONNECTION_CHANGE.equals(intent.getAction())) { + boolean connected = intent.getBooleanExtra(JPushInterface.EXTRA_CONNECTION_CHANGE, false); + Logger.w(TAG, "[MyReceiver]" + intent.getAction() +" connected state change to "+connected); + } else { + Logger.d(TAG, "[MyReceiver] Unhandled intent - " + intent.getAction()); + } + } catch (Exception e){ + + } + + } + + // 打印所有的 intent extra 数据 + private static String printBundle(Bundle bundle) { + StringBuilder sb = new StringBuilder(); + for (String key : bundle.keySet()) { + if (key.equals(JPushInterface.EXTRA_NOTIFICATION_ID)) { + sb.append("\nkey:" + key + ", value:" + bundle.getInt(key)); + }else if(key.equals(JPushInterface.EXTRA_CONNECTION_CHANGE)){ + sb.append("\nkey:" + key + ", value:" + bundle.getBoolean(key)); + } else if (key.equals(JPushInterface.EXTRA_EXTRA)) { + if (TextUtils.isEmpty(bundle.getString(JPushInterface.EXTRA_EXTRA))) { + Logger.i(TAG, "This message has no Extra data"); + continue; + } + + try { + JSONObject json = new JSONObject(bundle.getString(JPushInterface.EXTRA_EXTRA)); + Iterator it = json.keys(); + + while (it.hasNext()) { + String myKey = it.next(); + sb.append("\nkey:" + key + ", value: [" + + myKey + " - " +json.optString(myKey) + "]"); + } + } catch (JSONException e) { + Logger.e(TAG, "Get message extra JSON error!"); + } + + } else { + sb.append("\nkey:" + key + ", value:" + bundle.get(key)); + } + } + return sb.toString(); + } + + //send msg to MainActivity + private void processCustomMessage(Context context, Bundle bundle) { + if (MainActivity.isForeground) { + String message = bundle.getString(JPushInterface.EXTRA_MESSAGE); + String extras = bundle.getString(JPushInterface.EXTRA_EXTRA); + Intent msgIntent = new Intent(MainActivity.MESSAGE_RECEIVED_ACTION); + msgIntent.putExtra(MainActivity.KEY_MESSAGE, message); + if (!ExampleUtil.isEmpty(extras)) { + try { + JSONObject extraJson = new JSONObject(extras); + if (extraJson.length() > 0) { + msgIntent.putExtra(MainActivity.KEY_EXTRAS, extras); + } + } catch (JSONException e) { + + } + + } + LocalBroadcastManager.getInstance(context).sendBroadcast(msgIntent); + } + } +} diff --git a/app/src/main/java/com/appstore/uiui/jpush/PushService.java b/app/src/main/java/com/appstore/uiui/jpush/PushService.java new file mode 100644 index 0000000..b5d61fb --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/jpush/PushService.java @@ -0,0 +1,7 @@ +package com.appstore.uiui.jpush; + +import cn.jpush.android.service.JCommonService; + +public class PushService extends JCommonService { + +} diff --git a/app/src/main/java/com/appstore/uiui/jpush/TagAliasOperatorHelper.java b/app/src/main/java/com/appstore/uiui/jpush/TagAliasOperatorHelper.java new file mode 100644 index 0000000..4feca3e --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/jpush/TagAliasOperatorHelper.java @@ -0,0 +1,338 @@ +package com.appstore.uiui.jpush; + +import android.content.Context; +import android.os.Handler; +import android.os.Message; +import android.util.SparseArray; + +import java.util.Locale; +import java.util.Set; + +import cn.jpush.android.api.JPushInterface; +import cn.jpush.android.api.JPushMessage; + +/** + * 处理tagalias相关的逻辑 + * */ +public class TagAliasOperatorHelper { + private static final String TAG = "JIGUANG-TagAliasHelper"; + public static int sequence = 1; + /**增加*/ + public static final int ACTION_ADD = 1; + /**覆盖*/ + public static final int ACTION_SET = 2; + /**删除部分*/ + public static final int ACTION_DELETE = 3; + /**删除所有*/ + public static final int ACTION_CLEAN = 4; + /**查询*/ + public static final int ACTION_GET = 5; + + public static final int ACTION_CHECK = 6; + + public static final int DELAY_SEND_ACTION = 1; + + public static final int DELAY_SET_MOBILE_NUMBER_ACTION = 2; + + private Context context; + + private static TagAliasOperatorHelper mInstance; + private TagAliasOperatorHelper(){ + } + public static TagAliasOperatorHelper getInstance(){ + if(mInstance == null){ + synchronized (TagAliasOperatorHelper.class){ + if(mInstance == null){ + mInstance = new TagAliasOperatorHelper(); + } + } + } + return mInstance; + } + public void init(Context context){ + if(context != null) { + this.context = context.getApplicationContext(); + } + } + private SparseArray setActionCache = new SparseArray(); + + public Object get(int sequence){ + return setActionCache.get(sequence); + } + public Object remove(int sequence){ + return setActionCache.get(sequence); + } + public void put(int sequence,Object tagAliasBean){ + setActionCache.put(sequence,tagAliasBean); + } + private Handler delaySendHandler = new Handler(){ + @Override + public void handleMessage(Message msg) { + switch (msg.what){ + case DELAY_SEND_ACTION: + if(msg.obj !=null && msg.obj instanceof TagAliasBean){ + Logger.i(TAG,"on delay time"); + sequence++; + TagAliasBean tagAliasBean = (TagAliasBean) msg.obj; + setActionCache.put(sequence, tagAliasBean); + if(context!=null) { + handleAction(context, sequence, tagAliasBean); + }else{ + Logger.e(TAG,"#unexcepted - context was null"); + } + }else{ + Logger.w(TAG,"#unexcepted - msg obj was incorrect"); + } + break; + case DELAY_SET_MOBILE_NUMBER_ACTION: + if(msg.obj !=null && msg.obj instanceof String) { + Logger.i(TAG, "retry set mobile number"); + sequence++; + String mobileNumber = (String) msg.obj; + setActionCache.put(sequence, mobileNumber); + if(context !=null) { + handleAction(context, sequence, mobileNumber); + }else { + Logger.e(TAG, "#unexcepted - context was null"); + } + }else{ + Logger.w(TAG,"#unexcepted - msg obj was incorrect"); + } + break; + } + } + }; + public void handleAction(Context context,int sequence,String mobileNumber){ + put(sequence,mobileNumber); + Logger.d(TAG,"sequence:"+sequence+",mobileNumber:"+mobileNumber); + JPushInterface.setMobileNumber(context,sequence,mobileNumber); + } + /** + * 处理设置tag + * */ + public void handleAction(Context context,int sequence, TagAliasBean tagAliasBean){ + init(context); + if(tagAliasBean == null){ + Logger.w(TAG,"tagAliasBean was null"); + return; + } + put(sequence,tagAliasBean); + if(tagAliasBean.isAliasAction){ + switch (tagAliasBean.action){ + case ACTION_GET: + JPushInterface.getAlias(context,sequence); + break; + case ACTION_DELETE: + JPushInterface.deleteAlias(context,sequence); + break; + case ACTION_SET: + JPushInterface.setAlias(context,sequence,tagAliasBean.alias); + break; + default: + Logger.w(TAG,"unsupport alias action type"); + return; + } + }else { + switch (tagAliasBean.action) { + case ACTION_ADD: + JPushInterface.addTags(context, sequence, tagAliasBean.tags); + break; + case ACTION_SET: + JPushInterface.setTags(context, sequence, tagAliasBean.tags); + break; + case ACTION_DELETE: + JPushInterface.deleteTags(context, sequence, tagAliasBean.tags); + break; + case ACTION_CHECK: + //一次只能check一个tag + String tag = (String)tagAliasBean.tags.toArray()[0]; + JPushInterface.checkTagBindState(context,sequence,tag); + break; + case ACTION_GET: + JPushInterface.getAllTags(context, sequence); + break; + case ACTION_CLEAN: + JPushInterface.cleanTags(context, sequence); + break; + default: + Logger.w(TAG,"unsupport tag action type"); + return; + } + } + } + private boolean RetryActionIfNeeded(int errorCode,TagAliasBean tagAliasBean){ + if(!ExampleUtil.isConnected(context)){ + Logger.w(TAG,"no network"); + return false; + } + //返回的错误码为6002 超时,6014 服务器繁忙,都建议延迟重试 + if(errorCode == 6002 || errorCode == 6014){ + Logger.d(TAG,"need retry"); + if(tagAliasBean!=null){ + Message message = new Message(); + message.what = DELAY_SEND_ACTION; + message.obj = tagAliasBean; + delaySendHandler.sendMessageDelayed(message,1000*60); + String logs =getRetryStr(tagAliasBean.isAliasAction, tagAliasBean.action,errorCode); + ExampleUtil.showToast(logs, context); + return true; + } + } + return false; + } + private boolean RetrySetMObileNumberActionIfNeeded(int errorCode,String mobileNumber){ + if(!ExampleUtil.isConnected(context)){ + Logger.w(TAG,"no network"); + return false; + } + //返回的错误码为6002 超时,6024 服务器内部错误,建议稍后重试 + if(errorCode == 6002 || errorCode == 6024){ + Logger.d(TAG,"need retry"); + Message message = new Message(); + message.what = DELAY_SET_MOBILE_NUMBER_ACTION; + message.obj = mobileNumber; + delaySendHandler.sendMessageDelayed(message,1000*60); + String str = "Failed to set mobile number due to %s. Try again after 60s."; + str = String.format(Locale.ENGLISH,str,(errorCode == 6002 ? "timeout" : "server internal error”")); + ExampleUtil.showToast(str, context); + return true; + } + return false; + + } + private String getRetryStr(boolean isAliasAction,int actionType,int errorCode){ + String str = "Failed to %s %s due to %s. Try again after 60s."; + str = String.format(Locale.ENGLISH,str,getActionStr(actionType),(isAliasAction? "alias" : " tags") ,(errorCode == 6002 ? "timeout" : "server too busy")); + return str; + } + + private String getActionStr(int actionType){ + switch (actionType){ + case ACTION_ADD: + return "add"; + case ACTION_SET: + return "set"; + case ACTION_DELETE: + return "delete"; + case ACTION_GET: + return "get"; + case ACTION_CLEAN: + return "clean"; + case ACTION_CHECK: + return "check"; + } + return "unkonw operation"; + } + public void onTagOperatorResult(Context context, JPushMessage jPushMessage) { + int sequence = jPushMessage.getSequence(); + Logger.i(TAG,"action - onTagOperatorResult, sequence:"+sequence+",tags:"+jPushMessage.getTags()); + Logger.i(TAG,"tags size:"+jPushMessage.getTags().size()); + init(context); + //根据sequence从之前操作缓存中获取缓存记录 + TagAliasBean tagAliasBean = (TagAliasBean)setActionCache.get(sequence); + if(tagAliasBean == null){ + ExampleUtil.showToast("获取缓存记录失败", context); + return; + } + if(jPushMessage.getErrorCode() == 0){ + Logger.i(TAG,"action - modify tag Success,sequence:"+sequence); + setActionCache.remove(sequence); + String logs = getActionStr(tagAliasBean.action)+" tags success"; + Logger.i(TAG,logs); + ExampleUtil.showToast(logs, context); + }else{ + String logs = "Failed to " + getActionStr(tagAliasBean.action)+" tags"; + if(jPushMessage.getErrorCode() == 6018){ + //tag数量超过限制,需要先清除一部分再add + logs += ", tags is exceed limit need to clean"; + } + logs += ", errorCode:" + jPushMessage.getErrorCode(); + Logger.e(TAG, logs); + if(!RetryActionIfNeeded(jPushMessage.getErrorCode(),tagAliasBean)) { + ExampleUtil.showToast(logs, context); + } + } + } + public void onCheckTagOperatorResult(Context context, JPushMessage jPushMessage){ + int sequence = jPushMessage.getSequence(); + Logger.i(TAG,"action - onCheckTagOperatorResult, sequence:"+sequence+",checktag:"+jPushMessage.getCheckTag()); + init(context); + //根据sequence从之前操作缓存中获取缓存记录 + TagAliasBean tagAliasBean = (TagAliasBean)setActionCache.get(sequence); + if(tagAliasBean == null){ + ExampleUtil.showToast("获取缓存记录失败", context); + return; + } + if(jPushMessage.getErrorCode() == 0){ + Logger.i(TAG,"tagBean:"+tagAliasBean); + setActionCache.remove(sequence); + String logs = getActionStr(tagAliasBean.action)+" tag "+jPushMessage.getCheckTag() + " bind state success,state:"+jPushMessage.getTagCheckStateResult(); + Logger.i(TAG,logs); + ExampleUtil.showToast(logs, context); + }else{ + String logs = "Failed to " + getActionStr(tagAliasBean.action)+" tags, errorCode:" + jPushMessage.getErrorCode(); + Logger.e(TAG, logs); + if(!RetryActionIfNeeded(jPushMessage.getErrorCode(),tagAliasBean)) { + ExampleUtil.showToast(logs, context); + } + } + } + public void onAliasOperatorResult(Context context, JPushMessage jPushMessage) { + int sequence = jPushMessage.getSequence(); + Logger.i(TAG,"action - onAliasOperatorResult, sequence:"+sequence+",alias:"+jPushMessage.getAlias()); + init(context); + //根据sequence从之前操作缓存中获取缓存记录 + TagAliasBean tagAliasBean = (TagAliasBean)setActionCache.get(sequence); + if(tagAliasBean == null){ + ExampleUtil.showToast("获取缓存记录失败", context); + return; + } + if(jPushMessage.getErrorCode() == 0){ + Logger.i(TAG,"action - modify alias Success,sequence:"+sequence); + setActionCache.remove(sequence); + String logs = getActionStr(tagAliasBean.action)+" alias success"; + Logger.i(TAG,logs); + ExampleUtil.showToast(logs, context); + }else{ + String logs = "Failed to " + getActionStr(tagAliasBean.action)+" alias, errorCode:" + jPushMessage.getErrorCode(); + Logger.e(TAG, logs); + if(!RetryActionIfNeeded(jPushMessage.getErrorCode(),tagAliasBean)) { + ExampleUtil.showToast(logs, context); + } + } + } + //设置手机号码回调 + public void onMobileNumberOperatorResult(Context context, JPushMessage jPushMessage) { + int sequence = jPushMessage.getSequence(); + Logger.i(TAG,"action - onMobileNumberOperatorResult, sequence:"+sequence+",mobileNumber:"+jPushMessage.getMobileNumber()); + init(context); + if(jPushMessage.getErrorCode() == 0){ + Logger.i(TAG,"action - set mobile number Success,sequence:"+sequence); + setActionCache.remove(sequence); + }else{ + String logs = "Failed to set mobile number, errorCode:" + jPushMessage.getErrorCode(); + Logger.e(TAG, logs); + if(!RetrySetMObileNumberActionIfNeeded(jPushMessage.getErrorCode(),jPushMessage.getMobileNumber())){ + ExampleUtil.showToast(logs, context); + } + } + } + public static class TagAliasBean{ + public int action; + public Set tags; + public String alias; + public boolean isAliasAction; + + @Override + public String toString() { + return "TagAliasBean{" + + "action=" + action + + ", tags=" + tags + + ", alias='" + alias + '\'' + + ", isAliasAction=" + isAliasAction + + '}'; + } + } + + +} diff --git a/app/src/main/java/com/appstore/uiui/listener/LogDownloadListener.java b/app/src/main/java/com/appstore/uiui/listener/LogDownloadListener.java new file mode 100644 index 0000000..b43a9c0 --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/listener/LogDownloadListener.java @@ -0,0 +1,63 @@ +/* + * Copyright 2016 jeasonlzy(廖子尧) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.appstore.uiui.listener; + +import com.lzy.okgo.model.Progress; +import com.lzy.okserver.download.DownloadListener; + +import java.io.File; + +/** + * ================================================ + * 作 者:jeasonlzy(廖子尧)Github地址:https://github.com/jeasonlzy + * 版 本:1.0 + * 创建日期:2017/6/7 + * 描 述: + * 修订历史: + * ================================================ + */ +public class LogDownloadListener extends DownloadListener { + + public LogDownloadListener() { + super("LogDownloadListener"); + } + + @Override + public void onStart(Progress progress) { + System.out.println("onStart: " + progress); + } + + @Override + public void onProgress(Progress progress) { + System.out.println("onProgress: " + progress); + } + + @Override + public void onError(Progress progress) { + System.out.println("onError: " + progress); + progress.exception.printStackTrace(); + } + + @Override + public void onFinish(File file, Progress progress) { + System.out.println("onFinish: " + progress); + } + + @Override + public void onRemove(Progress progress) { + System.out.println("onRemove: " + progress); + } +} diff --git a/app/src/main/java/com/appstore/uiui/network/OKGOPost.java b/app/src/main/java/com/appstore/uiui/network/OKGOPost.java index 39fe6fb..b299002 100644 --- a/app/src/main/java/com/appstore/uiui/network/OKGOPost.java +++ b/app/src/main/java/com/appstore/uiui/network/OKGOPost.java @@ -1,59 +1,248 @@ package com.appstore.uiui.network; +import android.content.Context; import android.os.Handler; import android.os.Message; import android.util.Log; +import androidx.annotation.NonNull; + import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.appstore.uiui.TextCode.MessageWhat; +import com.appstore.uiui.base.UserInfo; import com.appstore.uiui.bean.AppInfo; import com.appstore.uiui.network.URLs.Url; import com.appstore.uiui.utils.LogUtils; +import com.appstore.uiui.utils.SPUtils; import com.appstore.uiui.utils.ToastUtil; +import com.appstore.uiui.utils.Utils; import com.lzy.okgo.OkGo; import com.lzy.okgo.callback.StringCallback; import com.lzy.okgo.model.Response; - import java.util.List; -import okhttp3.Call; - public class OKGOPost { + private static final int GET_ALL_APPINFO = 0; + private static final int GET_USERINFO = 1; + private static final int GET_SORTAPP = 2; + + private Handler mhandler = new Handler() { + @Override + public void handleMessage(@NonNull Message msg) { + super.handleMessage(msg); + switch (msg.what) { + case GET_ALL_APPINFO: + break; + case GET_USERINFO: + + break; + case GET_SORTAPP: + + break; + } + } + }; + + //msg.what=0 synchronized public static void getAllAppInfo(final Handler handler) { - OkGo.post(Url.GET_ALL_APPINFO).execute(new StringCallback() { + OkGo.post(Url.GET_ALL_APPINFO).execute(new StringCallback() { @Override - public void onSuccess(String s, Call call, okhttp3.Response response) { + public void onSuccess(Response response) { try { - JSONObject body = JSON.parseObject(s); -// Log.e("onSuccess", body.toString()); + JSONObject body = JSON.parseObject(response.body()); +// LogUtils.e("onSuccess", body.toString()); + LogUtils.e("fht", "getAllAppInfo onSuccess"); int code = body.getInteger("code"); String msg = body.getString("msg"); String data = body.getString("data"); List applist = JSON.parseArray(data, AppInfo.class); - if (code == 200) { + if (code == MessageWhat.CODE_SUCCESSFUL) { Message message = new Message(); - message.what = 0; + message.what = 1; message.obj = applist; handler.sendMessage(message); } else { ToastUtil.show(msg); + handler.sendEmptyMessage(0); } } catch (Exception e) { - Log.e("Exception", e.getMessage()); + LogUtils.e("Exception", e.getMessage()); ToastUtil.show("服务器错误,请稍后重试"); - + handler.sendEmptyMessage(0); } } + @Override - public void onError(Call call, okhttp3.Response response, Exception e) { - super.onError(call, response, e); - LogUtils.e("Exception", e.getMessage()); + public void onError(Response response) { + super.onError(response); + LogUtils.e("onError", response.getException().toString()); ToastUtil.show("网络连接失败,检查网络连接"); + handler.sendEmptyMessage(0); + } }); + } + + //msg.what=1 + synchronized public static void getUserInfo(final Handler handler) { + String sn = Utils.getSerial(); + OkGo.post(Url.GET_INFO_FROMESN) + .params("sn", sn) + .execute(new StringCallback() { + @Override + public void onSuccess(Response response) { + LogUtils.e("fht", "getUserInfo onSuccess"); + JSONObject jsonObject = JSON.parseObject(response.body()); + int code = jsonObject.getInteger("code"); + String msg = jsonObject.getString("msg"); + if (code == MessageWhat.CODE_SUCCESSFUL) { + String data = jsonObject.getString("data"); + UserInfo userInfo = JSON.parseObject(data, UserInfo.class); + Message message = new Message(); + message.what = 1; + message.obj = userInfo; + handler.sendMessage(message); + } else { + ToastUtil.show(msg); + } + } + + @Override + public void onError(Response response) { + super.onError(response); + LogUtils.e("onError", response.getException().toString()); + ToastUtil.show("网络连接失败,检查网络连接"); + } + }); + } + + //msg.what=2 + synchronized public static void getUserInfo(final Context context) { + OkGo.post(Url.GET_INFO_FROMESN) + .params("sn", Utils.getSerial()) + .execute(new StringCallback() { + @Override + public void onSuccess(Response response) { + LogUtils.e("fht", "getUserInfo onSuccess"); + JSONObject jsonObject = JSON.parseObject(response.body()); + int code = jsonObject.getInteger("code"); + String msg = jsonObject.getString("msg"); + if (code == MessageWhat.CODE_SUCCESSFUL) { + String data = jsonObject.getString("data"); + UserInfo userInfo = JSON.parseObject(data, UserInfo.class); + SPUtils.put(context, "member_id", userInfo.getMember_id()); + SPUtils.put(context, "sn_id", userInfo.getId()); + } else { + ToastUtil.show(msg); + } + } + + @Override + public void onError(Response response) { + super.onError(response); + LogUtils.e("onError", response.getException().toString()); + ToastUtil.show("网络连接失败,检查网络连接"); + } + }); + } + + //msg.what=3 + synchronized public static void getSortApp(final Handler handler, int type, int grade, int subject) { + OkGo.post(Url.GET_SORT_APP) + .params("app_type", type)//应用分类 0学习 1娱乐 + .params("grade", grade)//年级 以数字代表 + .params("subject", subject) //科目 以数字代表 + .execute(new StringCallback() { + @Override + public void onSuccess(Response response) { + LogUtils.e("fht", "getSortApp onSuccess"); + JSONObject jsonObject = JSON.parseObject(response.body()); + int code = jsonObject.getInteger("code"); + String msg = jsonObject.getString("msg"); + if (code == MessageWhat.CODE_SUCCESSFUL) { + String data = jsonObject.getString("data"); + List list = JSON.parseArray(data, AppInfo.class); + Message message = new Message(); + message.what = 1; + message.obj = list; + handler.sendMessage(message); + } else { + ToastUtil.show(msg); + } + } + + @Override + public void onError(Response response) { + super.onError(response); + LogUtils.e("onError", response.getException().toString()); + ToastUtil.show("网络连接失败,检查网络连接"); + } + }); + } + + //msg.what=4 + synchronized public static void setAppinstallInfo(int member_id, String sn_id, String app_name, String packageName) { + OkGo.post(Url.SET_APP_INSTALL_INFO) + .params("member_id", member_id) + .params("sn_id", sn_id) + .params("app_name", app_name) + .params("package", packageName) + .execute(new StringCallback() { + @Override + public void onSuccess(Response response) { + JSONObject object = JSON.parseObject(response.body()); + int code = object.getInteger("code"); + String msg = object.getString("msg"); + if (code == MessageWhat.CODE_SUCCESSFUL) { + LogUtils.e("setAppinstallInfo", msg); + } else { + ToastUtil.show(msg); + } + } + + @Override + public void onError(Response response) { + super.onError(response); + LogUtils.e("setAppinstallInfo", "onError:" + response.getException()); + + } + }); + + } + + //msg.what=5 + synchronized public static void setAppuninstallInfo(String sn_id, String packageName) { + OkGo.post(Url.SET_APP_UNINSTALL_INFO) + .params("sn_id", sn_id) + .params("package", packageName) + .execute(new StringCallback() { + @Override + public void onSuccess(Response response) { + JSONObject object = JSON.parseObject(response.body()); + int code = object.getInteger("code"); + String msg = object.getString("msg"); + if (code == MessageWhat.CODE_SUCCESSFUL) { + LogUtils.e("setAppinstallInfo", msg); + } else { + ToastUtil.show(msg); + } + } + + @Override + public void onError(Response response) { + super.onError(response); + LogUtils.e("setAppinstallInfo", "onError:" + response.getException()); + + } + }); + + } + } + diff --git a/app/src/main/java/com/appstore/uiui/network/URLs/Url.java b/app/src/main/java/com/appstore/uiui/network/URLs/Url.java index 7bb12fa..b82b0ee 100644 --- a/app/src/main/java/com/appstore/uiui/network/URLs/Url.java +++ b/app/src/main/java/com/appstore/uiui/network/URLs/Url.java @@ -5,5 +5,13 @@ public class Url { public final static String GET_ALL_APPINFO = NETWORK_HOME_ADDRESS + "/App/allInfo"; //获取所有应用 + public final static String GET_INFO_FROMESN = NETWORK_HOME_ADDRESS + "/Member/snInfo"; + //通过SN获取绑定信息 + public final static String GET_SORT_APP = NETWORK_HOME_ADDRESS + "/App/sortApp"; + //app分类查询 + public final static String SET_APP_INSTALL_INFO = NETWORK_HOME_ADDRESS + "/App/appInstall"; + //发送app安装信息 + public final static String SET_APP_UNINSTALL_INFO = NETWORK_HOME_ADDRESS + "/App/appUnload"; + //发送app卸载信息 } diff --git a/app/src/main/java/com/appstore/uiui/receiver/AppManagerReceiver.java b/app/src/main/java/com/appstore/uiui/receiver/AppManagerReceiver.java new file mode 100644 index 0000000..433be7b --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/receiver/AppManagerReceiver.java @@ -0,0 +1,53 @@ +package com.appstore.uiui.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.util.Log; + +import com.appstore.uiui.network.OKGOPost; +import com.appstore.uiui.utils.ApkUtils; +import com.appstore.uiui.utils.LogUtils; +import com.appstore.uiui.utils.SPUtils; +import com.appstore.uiui.utils.ToastUtil; + +public class AppManagerReceiver extends BroadcastReceiver { + + private static final String TAG = "--AppManagerReceiver--"; + private String action = null; + + @Override + public void onReceive(Context context, Intent intent) { + int member_id = (int) SPUtils.get(context, "member_id", -1); + String sn_id = (String) SPUtils.get(context, "sn_id", "-1"); + + action = intent.getAction(); + //接收安装广播 + if (action.equals(Intent.ACTION_PACKAGE_ADDED) || action.equals(Intent.ACTION_PACKAGE_REPLACED)) { + String packageName = intent.getDataString().replace("package:", ""); + String name = ApkUtils.getApplicationName(context, packageName); + ToastUtil.show(name + ":安装成功"); + LogUtils.e(TAG, "安装了:" + packageName + "包名的程序"); +// ToastTool.show("安装成功"); + if (member_id == -1 || sn_id.equals("-1")) { + OKGOPost.getUserInfo(context); + } else { + OKGOPost.setAppinstallInfo(member_id, sn_id, name, packageName); + } + } + + //接收卸载广播 + if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) { + String packageName = intent.getDataString().replace("package:", ""); + LogUtils.e(TAG, "卸载了:" + packageName + "包名的程序"); + if (member_id == -1 || sn_id.equals("-1")) { + OKGOPost.getUserInfo(context); + } else { + OKGOPost.setAppuninstallInfo(sn_id, packageName); + } + } + } + + +} diff --git a/app/src/main/java/com/appstore/uiui/receiver/BootReceiver.java b/app/src/main/java/com/appstore/uiui/receiver/BootReceiver.java new file mode 100644 index 0000000..595bab3 --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/receiver/BootReceiver.java @@ -0,0 +1,27 @@ +package com.appstore.uiui.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import com.appstore.uiui.network.OKGOPost; +import com.appstore.uiui.service.GuardService; +import com.appstore.uiui.service.InitJpushServer; +import com.appstore.uiui.service.StepService; +import com.appstore.uiui.utils.LogUtils; + +public class BootReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) { + + Intent i = new Intent(context, InitJpushServer.class); + context.startService(i); + context.startService(new Intent(context, StepService.class)); + context.startService(new Intent(context, GuardService.class)); + OKGOPost.getUserInfo(context); + LogUtils.e("fht", "booting BootReceiver"); + } + } +} diff --git a/app/src/main/java/com/appstore/uiui/service/GuardService.java b/app/src/main/java/com/appstore/uiui/service/GuardService.java index 05ffffb..5bbb621 100644 --- a/app/src/main/java/com/appstore/uiui/service/GuardService.java +++ b/app/src/main/java/com/appstore/uiui/service/GuardService.java @@ -17,12 +17,13 @@ import android.util.Log; import androidx.annotation.Nullable; +import com.appstore.uiui.KeepAliveConnection; import com.appstore.uiui.utils.ApkUtils; +import com.appstore.uiui.utils.LogUtils; import com.appstore.uiui.utils.ServiceAliveUtils; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.task.DownloadTask; -import com.blankj.utilcode.util.LogUtils; import com.blankj.utilcode.util.ToastUtils; @@ -57,9 +58,8 @@ public class GuardService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { -// return new KeepAliveConnection.Stub() {}; - return null; - + return new KeepAliveConnection.Stub() { + }; } @Override @@ -68,31 +68,27 @@ public class GuardService extends Service { Aria.init(this); Aria.get(this).getDownloadConfig().setMaxTaskNum(1); - - Aria.download(this).register(); - // startForeground(1, new Notification()); // 绑定建立链接 bindService(new Intent(this, StepService.class), mServiceConnection, Context.BIND_IMPORTANT); return START_STICKY; } - //在这里处理任务执行中的状态,如进度进度条的刷新 @Download.onTaskRunning - protected void running(DownloadTask task) { - Log.e("mjsheng", "我在下载=--------------::" + task.getState() + "-------" + task.getPercent() + "-------" + task.getExtendField()); + protected void running( DownloadTask task) { + LogUtils.e("mjsheng", "我在下载=--------------::" + task.getState() + "-------" + task.getPercent()+ "-------" + task.getExtendField()); ToastUtils.showShort("我在下载=--------------::" + task.getExtendField() + "-------" + task.getPercent()); } @Download.onTaskComplete - void taskComplete(DownloadTask task) { + void taskComplete( DownloadTask task) { //在这里处理任务完成的状态 String downloadPath = task.getFilePath(); String packageName = task.getExtendField(); - Log.e("mjsheng", "downloadPath::" + downloadPath); - Log.e("mjsheng", "extendField::" + packageName); + LogUtils.e("mjsheng", "downloadPath::" + downloadPath); + LogUtils.e("mjsheng", "extendField::" + packageName); ApkUtils.installApkInSilence(downloadPath, packageName); } } diff --git a/app/src/main/java/com/appstore/uiui/service/InitJpushServer.java b/app/src/main/java/com/appstore/uiui/service/InitJpushServer.java new file mode 100644 index 0000000..0e9a5e3 --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/service/InitJpushServer.java @@ -0,0 +1,16 @@ +package com.appstore.uiui.service; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +public class InitJpushServer extends Service { + public InitJpushServer() { + } + + @Override + public IBinder onBind(Intent intent) { + // TODO: Return the communication channel to the service. + throw new UnsupportedOperationException("Not yet implemented"); + } +} diff --git a/app/src/main/java/com/appstore/uiui/service/MyDownloadService.java b/app/src/main/java/com/appstore/uiui/service/MyDownloadService.java index abb5f73..44f6387 100644 --- a/app/src/main/java/com/appstore/uiui/service/MyDownloadService.java +++ b/app/src/main/java/com/appstore/uiui/service/MyDownloadService.java @@ -1,48 +1,14 @@ - package com.appstore.uiui.service; import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; import android.os.IBinder; -import android.provider.Settings; -import android.util.Log; import androidx.annotation.Nullable; -import com.appstore.uiui.MyApplication; -import com.appstore.uiui.bean.AppDateInfo; -import com.appstore.uiui.bean.AppDownloadInfo; -import com.appstore.uiui.utils.ApkUtils; -import com.appstore.uiui.utils.SaveListUtils; -import com.appstore.uiui.utils.StorageUtils; -import com.appstore.uiui.utils.Utils; -import com.lzy.okgo.OkGo; -import com.lzy.okgo.request.GetRequest; -import com.lzy.okserver.download.DownloadInfo; -import com.lzy.okserver.download.DownloadManager; -import com.lzy.okserver.download.DownloadService; -import com.lzy.okserver.listener.DownloadListener; - - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - // 下载管理服务 public class MyDownloadService extends Service { - private DownloadReceiver downloadReceiver; - private DownloadManager downloadManager; - public List allTask; - private List downloadURL; // 当前下载的任务 - private static final String ACTION_START = "START"; - private static final String ACTION_STOP = "STOP"; - @Override public int onStartCommand(Intent intent, int flags, int startId) { @@ -52,32 +18,7 @@ public class MyDownloadService extends Service { @Override public void onCreate() { - super.onCreate(); - downloadManager = DownloadService.getDownloadManager(); - downloadManager.getThreadPool().setCorePoolSize(1); - allTask = downloadManager.getAllTask(); - for (DownloadInfo downloadInfo : allTask) { - DownloadListener downloadListener = new MyDownloadListener(); - downloadListener.setUserTag(downloadInfo.getUrl()); - downloadInfo.setListener(downloadListener); - } - downloadReceiver = new DownloadReceiver(); - downloadURL = new ArrayList<>(); - /* 注册广播 */ - IntentFilter mScreenOnFilter = new IntentFilter(); - mScreenOnFilter.addAction(Utils.DOWNLOAD_STARTALL_ACTION); - mScreenOnFilter.addAction(Utils.DOWNLOAD_DELETE_UPDATE_ACTION); - mScreenOnFilter.addAction(Utils.DOWNLOAD_START_ACTION); - mScreenOnFilter.addAction(Utils.DOWNLOAD_STOP_ACTION); - mScreenOnFilter.addAction(Utils.DOWNLOAD_INITIALIZE_ACTION); - mScreenOnFilter.addAction(Utils.DOWNLOAD_DELETE_URL_ACTION); - mScreenOnFilter.addAction(Utils.DOWNLOAD_DELETE_PACKAGENAME_ACTION); - mScreenOnFilter.addAction(Utils.DOWNLOAD_DELETEALL_ACTION); - mScreenOnFilter.addAction(Utils.DOWNLOAD_ALLTASK_ACTION); - mScreenOnFilter.addAction(Utils.DOWNLOAD_PACKAGENAME_ACTION); - MyDownloadService.this.registerReceiver(downloadReceiver, mScreenOnFilter); - startService(new Intent(this, StepService.class)); startService(new Intent(this, GuardService.class)); } @@ -90,17 +31,6 @@ public class MyDownloadService extends Service { @Override public void onDestroy() { super.onDestroy(); - if (downloadManager != null) { - downloadManager.stopAllTask(); - } - if (allTask != null) { - allTask.clear(); - allTask = null; - } - downloadURL.clear(); - MyDownloadService.this.unregisterReceiver(downloadReceiver); - Intent sevice = new Intent(this, DownloadService.class); - this.startService(sevice); } @Nullable @@ -109,239 +39,5 @@ public class MyDownloadService extends Service { return null; } - private class MyDownloadListener extends DownloadListener { - - @Override - public void onProgress(DownloadInfo downloadInfo) { - Log.e("mjsheng", "onProgress::::::::" + downloadInfo.getProgress()); - sendDownloadState(downloadInfo); - } - - @Override - public void onFinish(DownloadInfo downloadInfo) { - sendDownloadState(downloadInfo); - AppDateInfo info = (AppDateInfo) downloadInfo.getData(); - if (info.getApp_baoming().equals(Utils.YOUNGSYSTEM_APP_TONGBU) || info.getApp_baoming().equals(MyApplication.getAppContext().getPackageName())) { -// ApkUtils.install(MyApplication.getAppContext(), new File(downloadInfo.getTargetPath())); - ApkUtils.installApkInSilence(downloadInfo.getTargetPath(), info.getApp_baoming()); - } - if (downloadURL.contains(downloadInfo.getUrl())) { -// ApkUtils.install(MyApplication.getAppContext(), new File(downloadInfo.getTargetPath())); - ApkUtils.installApkInSilence(downloadInfo.getTargetPath(), info.getApp_baoming()); - downloadURL.remove(downloadInfo.getUrl()); - } - } - - @Override - public void onError(DownloadInfo downloadInfo, String errorMsg, Exception e) { - sendDownloadState(downloadInfo); - } - } - - private void sendDownloadState(DownloadInfo downloadInfo) { - if (downloadInfo == null) return; - AppDownloadInfo info = new AppDownloadInfo(); - info.setFileName(downloadInfo.getFileName()); - info.setProgress(downloadInfo.getProgress()); - info.setTargetPath(downloadInfo.getTargetPath()); - info.setUrl(downloadInfo.getUrl()); - info.setState(downloadInfo.getState()); - info.setDownloadLength(downloadInfo.getDownloadLength()); - info.setTotalLength(downloadInfo.getTotalLength()); - AppDateInfo dateInfo = (AppDateInfo) downloadInfo.getData(); - info.setPackageName(dateInfo.getApp_baoming()); - info.setVersion(dateInfo.getApp_banben()); - info.setData(dateInfo); - Intent intent = new Intent(); - intent.setAction(Utils.DOWNLOAD_SERVICE_ACTION); - intent.putExtra("DownloadInfo", info); - sendBroadcast(intent); - } - - /** - * 是否存在该包名的下载任务 - */ - public DownloadInfo getPackageName(String packagename) { - if (packagename != null) { - for (DownloadInfo downloadInfo : allTask) { - AppDateInfo info = (AppDateInfo) downloadInfo.getData(); - if (info.getApp_baoming() != null) { - if (info.getApp_baoming().equals(packagename)) { - return downloadInfo; - } - } - } - } - return null; - } - - - public class DownloadReceiver extends BroadcastReceiver { - - @Override - public void onReceive(Context context, Intent intent) { - if (Utils.DOWNLOAD_STARTALL_ACTION.equals(intent.getAction())) { - downloadManager.startAllTask(); - } else if (Utils.DOWNLOAD_DELETE_URL_ACTION.equals(intent.getAction())) { - // 删除标识 下载地址 - String URL = intent.getStringExtra("URL"); - DownloadInfo downloadInfo = downloadManager.getDownloadInfo(URL); - if (downloadInfo != null && StorageUtils.isFileIsExists(downloadInfo.getTargetPath())) { - downloadInfo.setState(10); - sendDownloadState(downloadInfo); - downloadManager.removeTask(downloadInfo.getUrl(), true); - } - } else if (Utils.DOWNLOAD_START_ACTION.equals(intent.getAction())) { - // 下载 - String URL = intent.getStringExtra("URL"); - AppDateInfo appDateInfo = (AppDateInfo) intent.getSerializableExtra("AppDateInfo"); - if (appDateInfo != null) { - GetRequest request = OkGo.get(URL); - MyDownloadListener listener = new MyDownloadListener(); - listener.setUserTag(URL); - if (URL != null) { - if (downloadManager.getDownloadInfo(URL) == null) { - if (!StorageUtils.isFileIsExists("/Android/data/" + context.getPackageName() + "/files/Download")) { - StorageUtils.getFileRoot(context); - } - downloadManager.addTask(URL, appDateInfo, request, listener); - SaveListUtils.addDownLoadList(appDateInfo.getApp_baoming()); - DownloadInfo downloadInfo = downloadManager.getDownloadInfo(URL); - AppDownloadInfo info = new AppDownloadInfo(); - info.setFileName(downloadInfo.getFileName()); - info.setProgress(downloadInfo.getProgress()); - info.setTargetPath(downloadInfo.getTargetPath()); - info.setUrl(downloadInfo.getUrl()); - info.setState(downloadInfo.getState()); - info.setDownloadLength(downloadInfo.getDownloadLength()); - info.setTotalLength(downloadInfo.getTotalLength()); - AppDateInfo dateInfo = (AppDateInfo) downloadInfo.getData(); - info.setPackageName(dateInfo.getApp_baoming()); - String s = Settings.System.getString(getContentResolver(), "qch_app_forbid"); - Settings.System.putString(getContentResolver(), "qch_app_forbid", s + "," + dateInfo.getApp_baoming()); - Log.e("SystemSetting", "qch_app_forbid__________" + Settings.System.getString(getContentResolver(), "qch_app_forbid")); - - //2019.9.26 - info.setVersion(dateInfo.getApp_banben()); - info.setData(dateInfo); - Intent allIntent = new Intent(); - allIntent.setAction(Utils.DOWNLOAD_NEWSERVICE_ACTION); - allIntent.putExtra("addTask", info); - sendBroadcast(allIntent); - - } else { - if (!StorageUtils.isFileIsExists(downloadManager.getDownloadInfo(URL).getTargetPath())) { - downloadManager.removeTask(URL, true); - StorageUtils.getFileRoot(context); - } - downloadManager.addTask(URL, appDateInfo, request, listener); - } - if (!downloadURL.contains(URL)) { - downloadURL.add(URL); - } - } - } else { - if (downloadManager.getDownloadInfo(URL) != null) { - downloadManager.restartTask(URL); - } - } - } else if (Utils.DOWNLOAD_INITIALIZE_ACTION.equals(intent.getAction())) { - // 初始化item状态 - String URL = intent.getStringExtra("URL"); - DownloadInfo downloadInfo = downloadManager.getDownloadInfo(URL); - if (downloadInfo != null && StorageUtils.isFileIsExists(downloadInfo.getTargetPath())) { - sendDownloadState(downloadInfo); - } - } else if (Utils.DOWNLOAD_PACKAGENAME_ACTION.equals(intent.getAction())) { - // 初始化item状态 - String packageName = intent.getStringExtra("packageName"); - DownloadInfo downloadInfo = getPackageName(packageName); - if (downloadInfo != null && StorageUtils.isFileIsExists(downloadInfo.getTargetPath())) { - sendDownloadState(downloadInfo); - } - } else if (Utils.DOWNLOAD_STOP_ACTION.equals(intent.getAction())) { - // 暂停标识 - String URL = intent.getStringExtra("URL"); - downloadManager.pauseTask(URL); - DownloadInfo downloadInfo = downloadManager.getDownloadInfo(URL); - if (downloadInfo != null && StorageUtils.isFileIsExists(downloadInfo.getTargetPath())) { - sendDownloadState(downloadInfo); - } - } else if (Utils.DOWNLOAD_DELETE_PACKAGENAME_ACTION.equals(intent.getAction())) { - // 删除标识 包名 - String packageName = intent.getStringExtra("packageName"); - DownloadInfo downloadInfo = getPackageName(packageName); - if (downloadInfo != null && StorageUtils.isFileIsExists(downloadInfo.getTargetPath())) { - downloadInfo.setState(10); - sendDownloadState(downloadInfo); - downloadManager.removeTask(downloadInfo.getUrl(), true); - } - } else if (Utils.DOWNLOAD_DELETE_URL_ACTION.equals(intent.getAction())) { - // 删除标识 下载地址 - String URL = intent.getStringExtra("URL"); - DownloadInfo downloadInfo = downloadManager.getDownloadInfo(URL); - if (downloadInfo != null) { - downloadInfo.setState(10); - sendDownloadState(downloadInfo); - downloadManager.removeTask(downloadInfo.getUrl(), true); - } - } else if (Utils.DOWNLOAD_DELETE_UPDATE_ACTION.equals(intent.getAction())) { - // 删除 应用更新包 - String packageName = intent.getStringExtra("packageName"); - DownloadInfo downloadInfo = getPackageName(packageName); - if (downloadInfo != null && downloadInfo.getData() != null) { - AppDateInfo dateInfo = (AppDateInfo) downloadInfo.getData(); - PackageManager packageManager = context.getPackageManager(); - try { - PackageInfo packInfo = packageManager.getPackageInfo(context.getPackageName(), - 0); - String version = packInfo.versionName; - String oldVersion = dateInfo.getApp_banben(); - if (oldVersion == null || oldVersion.equals("")) { - downloadManager.removeTask(downloadInfo.getUrl(), true); - } else if (Utils.isUpdate(oldVersion, version) == true || oldVersion.equals(version)) { - downloadManager.removeTask(downloadInfo.getUrl(), true); - } - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - if (StorageUtils.isFileIsExists(downloadInfo.getTargetPath())) { - downloadInfo.setState(10); - sendDownloadState(downloadInfo); - downloadManager.removeTask(downloadInfo.getUrl(), true); - } - } - } else if (Utils.DOWNLOAD_ALLTASK_ACTION.equals(intent.getAction())) { - downloadManager = DownloadService.getDownloadManager(); - allTask = downloadManager.getAllTask(); - List list = new ArrayList<>(); - for (int i = 0; i < allTask.size(); i++) { - DownloadInfo downloadInfo = allTask.get(i); - if (downloadInfo != null && StorageUtils.isFileIsExists(downloadInfo.getTargetPath())) { - AppDateInfo dateInfo = (AppDateInfo) downloadInfo.getData(); - if (!dateInfo.getApp_baoming().equals(Utils.YOUNGSYSTEM_APP_TONGBU) && !dateInfo.getApp_baoming().equals(context.getPackageName())) { - AppDownloadInfo info = new AppDownloadInfo(); - info.setFileName(downloadInfo.getFileName()); - info.setProgress(downloadInfo.getProgress()); - info.setTargetPath(downloadInfo.getTargetPath()); - info.setUrl(downloadInfo.getUrl()); - info.setState(downloadInfo.getState()); - info.setDownloadLength(downloadInfo.getDownloadLength()); - info.setTotalLength(downloadInfo.getTotalLength()); - info.setPackageName(dateInfo.getApp_baoming()); - info.setVersion(dateInfo.getApp_banben()); - info.setData(dateInfo); - list.add(info); - } - } - } - Intent allIntent = new Intent(); - allIntent.setAction(Utils.DOWNLOAD_ALLSERVICE_ACTION); - allIntent.putExtra("allTask", (Serializable) list); - sendBroadcast(allIntent); - } - } - - } } diff --git a/app/src/main/java/com/appstore/uiui/service/StepService.java b/app/src/main/java/com/appstore/uiui/service/StepService.java index f3cc57a..06c2ec5 100644 --- a/app/src/main/java/com/appstore/uiui/service/StepService.java +++ b/app/src/main/java/com/appstore/uiui/service/StepService.java @@ -16,8 +16,9 @@ import android.os.IBinder; import androidx.annotation.Nullable; +import com.appstore.uiui.KeepAliveConnection; +import com.appstore.uiui.utils.LogUtils; import com.appstore.uiui.utils.ServiceAliveUtils; -import com.blankj.utilcode.util.LogUtils; /** * 主进程 双进程通讯 @@ -51,8 +52,8 @@ public class StepService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { -// return new KeepAliveConnection.Stub() { }; - return null; + return new KeepAliveConnection.Stub() { + }; } @Override diff --git a/app/src/main/java/com/appstore/uiui/utils/ApkUtils.java b/app/src/main/java/com/appstore/uiui/utils/ApkUtils.java index 18b3639..fc4af0f 100644 --- a/app/src/main/java/com/appstore/uiui/utils/ApkUtils.java +++ b/app/src/main/java/com/appstore/uiui/utils/ApkUtils.java @@ -78,6 +78,15 @@ public class ApkUtils { return; } + public static void openApp(Context context, String packageName) { + Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName); + if (intent != null) { + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + } + + /** * 安装一个apk文件 */ @@ -89,7 +98,7 @@ public class ApkUtils { if (Build.VERSION.SDK_INT >= 24) { //判读版本是否在7.0以上 //参数1 上下文, 参数2 Provider主机地址 和配置文件中保持一致 参数3 共享的文件 Uri apkUri = - FileProvider.getUriForFile(context, "com.mjsheng.myappstore.fileprovider", uriFile); + FileProvider.getUriForFile(context, "com.appstore.uiui.fileprovider", uriFile); //添加这一句表示对目标应用临时授权该Uri所代表的文件 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setDataAndType(apkUri, "application/vnd.android.package-archive"); @@ -298,6 +307,8 @@ public class ApkUtils { // Log.e("fanhuitong", "安装失败"); // } // } + + //使用系统签名 public static void installApkInSilence(String installPath, String packageName) { ToastUtil.show("正在安装应用..."); Class pmService; @@ -366,4 +377,47 @@ public class ApkUtils { return uid / PER_USER_RANGE; } + public static boolean checkIsUpdate(Context context, String packageName, int versionCode) { + PackageManager packageManager = context.getPackageManager(); + boolean update = false; + PackageInfo packageInfo = null; + try { + packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES); + int code = packageInfo.versionCode; + update = versionCode > code; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); +// LogUtils.e("NameNotFoundException", e.getMessage()); + update = false; + } + return update; + } + + synchronized public static PackageInfo getPackageInfo(Context context, String packageName) { + PackageManager pm = context.getPackageManager(); + PackageInfo packageInfo = null; + try { + packageInfo = pm.getPackageInfo(packageName, 0); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + Log.e("getPackageInfo", packageName + ":" + e.getMessage()); + } + return packageInfo; + } + + synchronized public static String getApplicationName(Context context, String packageName) { + PackageManager pm = context.getPackageManager(); + PackageInfo packageInfo = null; + String name = ""; + try { + packageInfo = pm.getPackageInfo(packageName, 0); + name = pm.getApplicationLabel(packageInfo.applicationInfo).toString(); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + Log.e("getPackageInfo", packageName + ":" + e.getMessage()); + } + return name; + } + + } diff --git a/app/src/main/java/com/appstore/uiui/utils/SPUtils.java b/app/src/main/java/com/appstore/uiui/utils/SPUtils.java new file mode 100644 index 0000000..015d23f --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/utils/SPUtils.java @@ -0,0 +1,202 @@ +package com.appstore.uiui.utils; + +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.util.Base64; +import android.widget.ImageView; + + +import com.appstore.uiui.common.CommonDatas; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Map; + +/** + * 作者 mjsheng + * 日期 2018/10/9 18:41 + * 邮箱 501802639@qq.com + * 来自: + */ + +public class SPUtils { + /** + * 保存在手机里面的文件名 + */ + public static final String FILE_NAME = "share_data"; + + /** + * 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法 + */ + public static void put(Context context, String key, Object object) { + + SharedPreferences sp = context.getSharedPreferences(FILE_NAME, + Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sp.edit(); + + if (object instanceof String) { + editor.putString(key, (String) object); + } else if (object instanceof Integer) { + editor.putInt(key, (Integer) object); + } else if (object instanceof Boolean) { + editor.putBoolean(key, (Boolean) object); + } else if (object instanceof Float) { + editor.putFloat(key, (Float) object); + } else if (object instanceof Long) { + editor.putLong(key, (Long) object); + } else { + editor.putString(key, object.toString()); + } + + SharedPreferencesCompat.apply(editor); + } + + /** + * 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值 + */ + public static Object get(Context context, String key, Object defaultObject) { + SharedPreferences sp = context.getSharedPreferences(FILE_NAME, + Context.MODE_PRIVATE); + + if (defaultObject instanceof String) { + return sp.getString(key, (String) defaultObject); + } else if (defaultObject instanceof Integer) { + return sp.getInt(key, (Integer) defaultObject); + } else if (defaultObject instanceof Boolean) { + return sp.getBoolean(key, (Boolean) defaultObject); + } else if (defaultObject instanceof Float) { + return sp.getFloat(key, (Float) defaultObject); + } else if (defaultObject instanceof Long) { + return sp.getLong(key, (Long) defaultObject); + } + + return null; + } + + /** + * 移除某个key值已经对应的值 + */ + public static void remove(Context context, String key) { + SharedPreferences sp = context.getSharedPreferences(FILE_NAME, + Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sp.edit(); + editor.remove(key); + SharedPreferencesCompat.apply(editor); + } + + /** + * 清除所有数据 + */ + public static void clear(Context context) { + SharedPreferences sp = context.getSharedPreferences(FILE_NAME, + Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sp.edit(); + editor.clear(); + SharedPreferencesCompat.apply(editor); + reductFirstEnter(context); + } + + //还原状态firstEnter信息 + private static void reductFirstEnter(Context context){ + put(context, CommonDatas.FLAG_FIRST_ENTER,CommonDatas.FLAG_FIRST_ENTER); + } + + + /** + * 查询某个key是否已经存在 + */ + public static boolean contains(Context context, String key) { + SharedPreferences sp = context.getSharedPreferences(FILE_NAME, + Context.MODE_PRIVATE); + return sp.contains(key); + } + + /** + * 返回所有的键值对 + */ + public static Map getAll(Context context) { + SharedPreferences sp = context.getSharedPreferences(FILE_NAME, + Context.MODE_PRIVATE); + return sp.getAll(); + } + + + /** + * 保存图片到SharedPreferences + * + * @param mContext + * @param imageView + */ + public static void putImage(Context mContext, String key, ImageView imageView) { + BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable(); + Bitmap bitmap = drawable.getBitmap(); + // 将Bitmap压缩成字节数组输出流 + ByteArrayOutputStream byStream = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 80, byStream); + // 利用Base64将我们的字节数组输出流转换成String + byte[] byteArray = byStream.toByteArray(); + String imgString = new String(Base64.encodeToString(byteArray, Base64.DEFAULT)); + // 将String保存shareUtils + SPUtils.put(mContext, key, imgString); + } + + /** + * 从SharedPreferences读取图片 + * + * @param mContext + * @param imageView + */ + public static Bitmap getImage(Context mContext, String key, ImageView imageView) { + String imgString = (String) SPUtils.get(mContext, key, ""); + if (!imgString.equals("")) { + // 利用Base64将我们string转换 + byte[] byteArray = Base64.decode(imgString, Base64.DEFAULT); + ByteArrayInputStream byStream = new ByteArrayInputStream(byteArray); + // 生成bitmap + return BitmapFactory.decodeStream(byStream); + } + return null; + } + + /** + * 创建一个解决SharedPreferencesCompat.apply方法的一个兼容类 + */ + private static class SharedPreferencesCompat { + private static final Method sApplyMethod = findApplyMethod(); + + /** + * 反射查找apply的方法 + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + private static Method findApplyMethod() { + try { + Class clz = SharedPreferences.Editor.class; + return clz.getMethod("apply"); + } catch (NoSuchMethodException e) { + } + + return null; + } + + /** + * 如果找到则使用apply执行,否则使用commit + */ + public static void apply(SharedPreferences.Editor editor) { + try { + if (sApplyMethod != null) { + sApplyMethod.invoke(editor); + return; + } + } catch (IllegalArgumentException e) { + } catch (IllegalAccessException e) { + } catch (InvocationTargetException e) { + } + editor.commit(); + } + } +} diff --git a/app/src/main/java/com/appstore/uiui/utils/ServiceAliveUtils.java b/app/src/main/java/com/appstore/uiui/utils/ServiceAliveUtils.java index 2fff427..729b39a 100644 --- a/app/src/main/java/com/appstore/uiui/utils/ServiceAliveUtils.java +++ b/app/src/main/java/com/appstore/uiui/utils/ServiceAliveUtils.java @@ -10,7 +10,7 @@ public class ServiceAliveUtils { public static boolean isServiceAlice() { boolean isServiceRunning = false; ActivityManager manager = - (ActivityManager) MyApplication.getAppContext().getSystemService(Context.ACTIVITY_SERVICE); + (ActivityManager) MyApplication.getInstance().getAppContext().getSystemService(Context.ACTIVITY_SERVICE); if (manager == null) { return true; } diff --git a/app/src/main/java/com/appstore/uiui/utils/ToastUtil.java b/app/src/main/java/com/appstore/uiui/utils/ToastUtil.java index c40105c..3b4a475 100644 --- a/app/src/main/java/com/appstore/uiui/utils/ToastUtil.java +++ b/app/src/main/java/com/appstore/uiui/utils/ToastUtil.java @@ -6,6 +6,8 @@ import android.os.Handler; import android.os.Looper; import android.widget.Toast; +import com.appstore.uiui.BuildConfig; + /** * Created by haoge on 2017/3/2. */ @@ -17,6 +19,8 @@ public class ToastUtil { @SuppressLint("ShowToast") public static void init(Context context) { toast = Toast.makeText(context, "", Toast.LENGTH_SHORT); + debugToast = Toast.makeText(context, "", Toast.LENGTH_SHORT); + } public static void show(final String msg) { @@ -31,7 +35,7 @@ public class ToastUtil { }); } -// public static void showInCenter(String msg) { + // public static void showInCenter(String msg) { // mainHandler.post(() -> { // if (toast != null) { // toast.setGravity(Gravity.CENTER, 0, 0); @@ -40,5 +44,21 @@ public class ToastUtil { // } // }); // } + static Handler debugHandler = new Handler(Looper.getMainLooper()); + static Toast debugToast; + + public static void debugShow(final String msg) { + mainHandler.post(new Runnable() { + @Override + public void run() { + if (BuildConfig.LOG_DEBUG) { + if (toast != null) { + toast.setText(msg); + toast.show(); + } + } + } + }); + } } diff --git a/app/src/main/java/com/appstore/uiui/view/NumberProgressBar.java b/app/src/main/java/com/appstore/uiui/view/NumberProgressBar.java new file mode 100644 index 0000000..9db734e --- /dev/null +++ b/app/src/main/java/com/appstore/uiui/view/NumberProgressBar.java @@ -0,0 +1,524 @@ +/* + * Copyright 2016 jeasonlzy(廖子尧) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.appstore.uiui.view; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.view.View; + +import com.appstore.uiui.R; + +/** + * ================================================ + * 作 者:jeasonlzy(廖子尧)Github地址:https://github.com/jeasonlzy + * 版 本:1.0 + * 创建日期:16/9/11 + * 描 述: + * 修订历史: + * ================================================ + */ +public class NumberProgressBar extends View { + + public interface OnProgressBarListener { + + void onProgressChange(int current, int max); + } + + private int mMaxProgress = 100; + + /** + * Current progress, can not exceed the max progress. + */ + private int mCurrentProgress = 0; + + /** + * The progress area bar color. + */ + private int mReachedBarColor; + + /** + * The bar unreached area color. + */ + private int mUnreachedBarColor; + + /** + * The progress text color. + */ + private int mTextColor; + + /** + * The progress text size. + */ + private float mTextSize; + + /** + * The height of the reached area. + */ + private float mReachedBarHeight; + + /** + * The height of the unreached area. + */ + private float mUnreachedBarHeight; + + /** + * The suffix of the number. + */ + private String mSuffix = "%"; + + /** + * The prefix. + */ + private String mPrefix = ""; + + private final int default_text_color = Color.rgb(66, 145, 241); + private final int default_reached_color = Color.rgb(66, 145, 241); + private final int default_unreached_color = Color.rgb(204, 204, 204); + private final float default_progress_text_offset; + private final float default_text_size; + private final float default_reached_bar_height; + private final float default_unreached_bar_height; + + /** + * For save and restore instance of progressbar. + */ + private static final String INSTANCE_STATE = "saved_instance"; + private static final String INSTANCE_TEXT_COLOR = "text_color"; + private static final String INSTANCE_TEXT_SIZE = "text_size"; + private static final String INSTANCE_REACHED_BAR_HEIGHT = "reached_bar_height"; + private static final String INSTANCE_REACHED_BAR_COLOR = "reached_bar_color"; + private static final String INSTANCE_UNREACHED_BAR_HEIGHT = "unreached_bar_height"; + private static final String INSTANCE_UNREACHED_BAR_COLOR = "unreached_bar_color"; + private static final String INSTANCE_MAX = "max"; + private static final String INSTANCE_PROGRESS = "progress"; + private static final String INSTANCE_SUFFIX = "suffix"; + private static final String INSTANCE_PREFIX = "prefix"; + private static final String INSTANCE_TEXT_VISIBILITY = "text_visibility"; + + private static final int PROGRESS_TEXT_VISIBLE = 0; + + /** + * The width of the text that to be drawn. + */ + private float mDrawTextWidth; + + /** + * The drawn text start. + */ + private float mDrawTextStart; + + /** + * The drawn text end. + */ + private float mDrawTextEnd; + + /** + * The text that to be drawn in onDraw(). + */ + private String mCurrentDrawText; + + /** + * The Paint of the reached area. + */ + private Paint mReachedBarPaint; + /** + * The Paint of the unreached area. + */ + private Paint mUnreachedBarPaint; + /** + * The Paint of the progress text. + */ + private Paint mTextPaint; + + /** + * Unreached bar area to draw rect. + */ + private RectF mUnreachedRectF = new RectF(0, 0, 0, 0); + /** + * Reached bar area rect. + */ + private RectF mReachedRectF = new RectF(0, 0, 0, 0); + + /** + * The progress text offset. + */ + private float mOffset; + + /** + * Determine if need to draw unreached area. + */ + private boolean mDrawUnreachedBar = true; + + private boolean mDrawReachedBar = true; + + private boolean mIfDrawText = true; + + /** + * Listener + */ + private OnProgressBarListener mListener; + + public enum ProgressTextVisibility { + Visible, Invisible + } + + public NumberProgressBar(Context context) { + this(context, null); + } + + public NumberProgressBar(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public NumberProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + default_reached_bar_height = dp2px(1.5f); + default_unreached_bar_height = dp2px(1.0f); + default_text_size = sp2px(10); + default_progress_text_offset = dp2px(3.0f); + + //load styled attributes. + final TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.NumberProgressBar, defStyleAttr, 0); + + mReachedBarColor = attributes.getColor(R.styleable.NumberProgressBar_progress_reached_color, default_reached_color); + mUnreachedBarColor = attributes.getColor(R.styleable.NumberProgressBar_progress_unreached_color, default_unreached_color); + mTextColor = attributes.getColor(R.styleable.NumberProgressBar_progress_text_color, default_text_color); + mTextSize = attributes.getDimension(R.styleable.NumberProgressBar_progress_text_size, default_text_size); + + mReachedBarHeight = attributes.getDimension(R.styleable.NumberProgressBar_progress_reached_bar_height, default_reached_bar_height); + mUnreachedBarHeight = attributes.getDimension(R.styleable.NumberProgressBar_progress_unreached_bar_height, default_unreached_bar_height); + mOffset = attributes.getDimension(R.styleable.NumberProgressBar_progress_text_offset, default_progress_text_offset); + + int textVisible = attributes.getInt(R.styleable.NumberProgressBar_progress_text_visibility, PROGRESS_TEXT_VISIBLE); + if (textVisible != PROGRESS_TEXT_VISIBLE) { + mIfDrawText = false; + } + + setProgress(attributes.getInt(R.styleable.NumberProgressBar_progress_current, 0)); + setMax(attributes.getInt(R.styleable.NumberProgressBar_progress_max, 100)); + + attributes.recycle(); + initializePainters(); + } + + @Override + protected int getSuggestedMinimumWidth() { + return (int) mTextSize; + } + + @Override + protected int getSuggestedMinimumHeight() { + return Math.max((int) mTextSize, Math.max((int) mReachedBarHeight, (int) mUnreachedBarHeight)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false)); + } + + private int measure(int measureSpec, boolean isWidth) { + int result; + int mode = MeasureSpec.getMode(measureSpec); + int size = MeasureSpec.getSize(measureSpec); + int padding = isWidth ? getPaddingLeft() + getPaddingRight() : getPaddingTop() + getPaddingBottom(); + if (mode == MeasureSpec.EXACTLY) { + result = size; + } else { + result = isWidth ? getSuggestedMinimumWidth() : getSuggestedMinimumHeight(); + result += padding; + if (mode == MeasureSpec.AT_MOST) { + if (isWidth) { + result = Math.max(result, size); + } else { + result = Math.min(result, size); + } + } + } + return result; + } + + @Override + protected void onDraw(Canvas canvas) { + if (mIfDrawText) { + calculateDrawRectF(); + } else { + calculateDrawRectFWithoutProgressText(); + } + + if (mDrawReachedBar) { + canvas.drawRect(mReachedRectF, mReachedBarPaint); + } + + if (mDrawUnreachedBar) { + canvas.drawRect(mUnreachedRectF, mUnreachedBarPaint); + } + + if (mIfDrawText) canvas.drawText(mCurrentDrawText, mDrawTextStart, mDrawTextEnd, mTextPaint); + } + + private void initializePainters() { + mReachedBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mReachedBarPaint.setColor(mReachedBarColor); + + mUnreachedBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mUnreachedBarPaint.setColor(mUnreachedBarColor); + + mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mTextPaint.setColor(mTextColor); + mTextPaint.setTextSize(mTextSize); + } + + private void calculateDrawRectFWithoutProgressText() { + mReachedRectF.left = getPaddingLeft(); + mReachedRectF.top = getHeight() / 2.0f - mReachedBarHeight / 2.0f; + mReachedRectF.right = (getWidth() - getPaddingLeft() - getPaddingRight()) / (getMax() * 1.0f) * getProgress() + getPaddingLeft(); + mReachedRectF.bottom = getHeight() / 2.0f + mReachedBarHeight / 2.0f; + + mUnreachedRectF.left = mReachedRectF.right; + mUnreachedRectF.right = getWidth() - getPaddingRight(); + mUnreachedRectF.top = getHeight() / 2.0f + -mUnreachedBarHeight / 2.0f; + mUnreachedRectF.bottom = getHeight() / 2.0f + mUnreachedBarHeight / 2.0f; + } + + private void calculateDrawRectF() { + + mCurrentDrawText = String.format("%d", getProgress() * 100 / getMax()); + mCurrentDrawText = mPrefix + mCurrentDrawText + mSuffix; + mDrawTextWidth = mTextPaint.measureText(mCurrentDrawText); + + if (getProgress() == 0) { + mDrawReachedBar = false; + mDrawTextStart = getPaddingLeft(); + } else { + mDrawReachedBar = true; + mReachedRectF.left = getPaddingLeft(); + mReachedRectF.top = getHeight() / 2.0f - mReachedBarHeight / 2.0f; + mReachedRectF.right = (getWidth() - getPaddingLeft() - getPaddingRight()) / (getMax() * 1.0f) * getProgress() - mOffset + getPaddingLeft(); + mReachedRectF.bottom = getHeight() / 2.0f + mReachedBarHeight / 2.0f; + mDrawTextStart = (mReachedRectF.right + mOffset); + } + + mDrawTextEnd = (int) ((getHeight() / 2.0f) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2.0f)); + + if ((mDrawTextStart + mDrawTextWidth) >= getWidth() - getPaddingRight()) { + mDrawTextStart = getWidth() - getPaddingRight() - mDrawTextWidth; + mReachedRectF.right = mDrawTextStart - mOffset; + } + + float unreachedBarStart = mDrawTextStart + mDrawTextWidth + mOffset; + if (unreachedBarStart >= getWidth() - getPaddingRight()) { + mDrawUnreachedBar = false; + } else { + mDrawUnreachedBar = true; + mUnreachedRectF.left = unreachedBarStart; + mUnreachedRectF.right = getWidth() - getPaddingRight(); + mUnreachedRectF.top = getHeight() / 2.0f + -mUnreachedBarHeight / 2.0f; + mUnreachedRectF.bottom = getHeight() / 2.0f + mUnreachedBarHeight / 2.0f; + } + } + + /** + * Get progress text color. + * + * @return progress text color. + */ + public int getTextColor() { + return mTextColor; + } + + /** + * Get progress text size. + * + * @return progress text size. + */ + public float getProgressTextSize() { + return mTextSize; + } + + public int getUnreachedBarColor() { + return mUnreachedBarColor; + } + + public int getReachedBarColor() { + return mReachedBarColor; + } + + public int getProgress() { + return mCurrentProgress; + } + + public int getMax() { + return mMaxProgress; + } + + public float getReachedBarHeight() { + return mReachedBarHeight; + } + + public float getUnreachedBarHeight() { + return mUnreachedBarHeight; + } + + public void setProgressTextSize(float textSize) { + this.mTextSize = textSize; + mTextPaint.setTextSize(mTextSize); + invalidate(); + } + + public void setProgressTextColor(int textColor) { + this.mTextColor = textColor; + mTextPaint.setColor(mTextColor); + invalidate(); + } + + public void setUnreachedBarColor(int barColor) { + this.mUnreachedBarColor = barColor; + mUnreachedBarPaint.setColor(mUnreachedBarColor); + invalidate(); + } + + public void setReachedBarColor(int progressColor) { + this.mReachedBarColor = progressColor; + mReachedBarPaint.setColor(mReachedBarColor); + invalidate(); + } + + public void setReachedBarHeight(float height) { + mReachedBarHeight = height; + } + + public void setUnreachedBarHeight(float height) { + mUnreachedBarHeight = height; + } + + public void setMax(int maxProgress) { + if (maxProgress > 0) { + this.mMaxProgress = maxProgress; + invalidate(); + } + } + + public void setSuffix(String suffix) { + if (suffix == null) { + mSuffix = ""; + } else { + mSuffix = suffix; + } + } + + public String getSuffix() { + return mSuffix; + } + + public void setPrefix(String prefix) { + if (prefix == null) mPrefix = ""; + else { + mPrefix = prefix; + } + } + + public String getPrefix() { + return mPrefix; + } + + public void incrementProgressBy(int by) { + if (by > 0) { + setProgress(getProgress() + by); + } + + if (mListener != null) { + mListener.onProgressChange(getProgress(), getMax()); + } + } + + public void setProgress(int progress) { + if (progress <= getMax() && progress >= 0) { + this.mCurrentProgress = progress; + invalidate(); + } + } + + @Override + protected Parcelable onSaveInstanceState() { + final Bundle bundle = new Bundle(); + bundle.putParcelable(INSTANCE_STATE, super.onSaveInstanceState()); + bundle.putInt(INSTANCE_TEXT_COLOR, getTextColor()); + bundle.putFloat(INSTANCE_TEXT_SIZE, getProgressTextSize()); + bundle.putFloat(INSTANCE_REACHED_BAR_HEIGHT, getReachedBarHeight()); + bundle.putFloat(INSTANCE_UNREACHED_BAR_HEIGHT, getUnreachedBarHeight()); + bundle.putInt(INSTANCE_REACHED_BAR_COLOR, getReachedBarColor()); + bundle.putInt(INSTANCE_UNREACHED_BAR_COLOR, getUnreachedBarColor()); + bundle.putInt(INSTANCE_MAX, getMax()); + bundle.putInt(INSTANCE_PROGRESS, getProgress()); + bundle.putString(INSTANCE_SUFFIX, getSuffix()); + bundle.putString(INSTANCE_PREFIX, getPrefix()); + bundle.putBoolean(INSTANCE_TEXT_VISIBILITY, getProgressTextVisibility()); + return bundle; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (state instanceof Bundle) { + final Bundle bundle = (Bundle) state; + mTextColor = bundle.getInt(INSTANCE_TEXT_COLOR); + mTextSize = bundle.getFloat(INSTANCE_TEXT_SIZE); + mReachedBarHeight = bundle.getFloat(INSTANCE_REACHED_BAR_HEIGHT); + mUnreachedBarHeight = bundle.getFloat(INSTANCE_UNREACHED_BAR_HEIGHT); + mReachedBarColor = bundle.getInt(INSTANCE_REACHED_BAR_COLOR); + mUnreachedBarColor = bundle.getInt(INSTANCE_UNREACHED_BAR_COLOR); + initializePainters(); + setMax(bundle.getInt(INSTANCE_MAX)); + setProgress(bundle.getInt(INSTANCE_PROGRESS)); + setPrefix(bundle.getString(INSTANCE_PREFIX)); + setSuffix(bundle.getString(INSTANCE_SUFFIX)); + setProgressTextVisibility(bundle.getBoolean(INSTANCE_TEXT_VISIBILITY) ? ProgressTextVisibility.Visible : ProgressTextVisibility.Invisible); + super.onRestoreInstanceState(bundle.getParcelable(INSTANCE_STATE)); + return; + } + super.onRestoreInstanceState(state); + } + + public float dp2px(float dp) { + final float scale = getResources().getDisplayMetrics().density; + return dp * scale + 0.5f; + } + + public float sp2px(float sp) { + final float scale = getResources().getDisplayMetrics().scaledDensity; + return sp * scale; + } + + public void setProgressTextVisibility(ProgressTextVisibility visibility) { + mIfDrawText = visibility == ProgressTextVisibility.Visible; + invalidate(); + } + + public boolean getProgressTextVisibility() { + return mIfDrawText; + } + + public void setOnProgressBarListener(OnProgressBarListener listener) { + mListener = listener; + } +} diff --git a/app/src/main/res/drawable/bt_delete.png b/app/src/main/res/drawable/bt_delete.png new file mode 100644 index 0000000000000000000000000000000000000000..c1d374019cdf93d6b7c41d157cfe26306b91f07f GIT binary patch literal 3349 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 EUs}{J(EtDd literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/icon_english.png b/app/src/main/res/drawable/icon_english.png new file mode 100644 index 0000000000000000000000000000000000000000..6f5ddc1bdc2793e5adf365020d9095cdeddd3043 GIT binary patch literal 8709 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 L)={cdunzuT{-75Y literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/icon_game.png b/app/src/main/res/drawable/icon_game.png new file mode 100644 index 0000000000000000000000000000000000000000..72394c91a377697a6bc27e06e7bcde3628514177 GIT binary patch literal 5089 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 HedGTJk-0IJ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/icon_geography.png b/app/src/main/res/drawable/icon_geography.png new file mode 100644 index 0000000000000000000000000000000000000000..b50d18cbd71f5515c281710ac5c447bebd06d67e GIT binary patch literal 11735 zcmaKSbyOYC^58|>8Rdv;zK4(4=s>-tHsKlrM0HDjuNvXfIx&PeAh%e8maZ8q$hR8)) z+eO2{!o}U#$s7q_HuL;4Xf6Z*U@B`(Z5M4NML|;sJ67X=WLQ1y z9AC5nKuFZX(b)8}xeKLDy5`@ zlQ|_1D-Vk)I|nx4#nS)nf}P`kVC|j%CrmE^WAiX}WaD6E|Hq|& z3o0r7|3&TW{v++|qHg|w?EQZNJ8OD6nzN~!J3F{KnZ9hC1?r@`v7q&Tw?*o|;{A`V*?+Y~T>8K2vb`9?_RqflU;F&; zp_kck#MR_T4O^?L`eIyUu8GI4T->Nk2d^65+Mu|2_CVq=}6Kw)aFdzakCb(dT z3s^6Or>S`M!Hqs;qY&{? zJpAfdE4bB!@^lzOJ9>aSpZ#D@@79-KG;w}(Vs}5%<`_p}RdzylAM;LMbZn1b)F(3Q zm0X&!oa;}zFj(dLFORxlR760MEhk0}Nq7A#borxdk;y5l%E|BMx0G$K||~Mu4i}?aeng zSY2B<(4s?s;s!Ehf|{kHfGwVET-rclkLFMME1laONVt|YNV^Ykn9?k|flzOPfa}c3 zZA2jG_qBZoO5+25#mqF+%^d&8vUo=+NY|1ie-b!yqR@d*)b<^0AC-;#Lyx{yq(MPE`=5yWZfoPO+Wp%Y`6Edn0(9umU|Z(?O*HHn zZ|gi`CE{78-XeI1@4(tuJ=|x3Mze0{-vz|2x2{^{Gk?E7`lMZuFKa6TUdGp%zgy?xXm~NkFi}RJj=_ZLp_NR$8>PcT(2ult1NU3;}2Il zKL+<97eZK9YC+=fTzWTE!Kwv_w^h*d-VpCV@}1nX#j@sAhjDKxAV1L zXz)a(RzFL$arHwx`ce^n+XRB{*Vi&TKP@60NRLvTzH;O_f(txF_W1Jiw;W^xe@2*= zRUU4-okD!EnXq1%abGfrZr?-A_d}uYLwXb)dGIpmp|$atxs>N`&E)62_8?7X%{5 zLUMMz2o;%{Hb9l&kR|4KvC|0Y`9%Vo#_a@lk1>+Y>Q%}!dJNf>@;8*-gDk8FO_QF` z&HVzXEKH!vzX|67VFn8rWugbY`vNRXYK)tB>9YhDx<%iJV7*F@lzV^?W7;Pbb~*u!?jPk;S7pfog)&f1Cm zx&4Vcw7Gt-ph|JD2@3J6IS7x)qU#Y0?0}ol!zP8DLG@Ju>kqXUv)5H!q{b-sW-LeN zVM9*}OEY7atEmBA`vp~xnZ4}Zj%Z*=Udl7Nq=tqc(Oj9xyQmf|!c34g3!V5}OI^q1 z*7&{A1kM0)t(aRw^0aPX>98;*pL9MRVyF@a^@TMvAl-i=BpY(nFI+@>4X#kQ_YI6i zLVVLNviX4eTcZQ=mrvgsV4ttqt>&*2>)+7nervlkb7xCM%YDPAcN=~6H|c%9bm!jO zcgmA*h)F-cWzCT2?W|!qwb2dvrP9d75M`F$@P9l0N$=!qTkK7t)%40CDs)OQ&2JM6 z4Czb`^nVI5YWaz}$c@l_6+^GG->+t=RV|H4-Y!t<=Xi`I#F3; zmG?NXl=K$vxt5b^ZTPEK@H+nCN(ot(*8~{moqA5;g70R;$#e5bLqJOS*|TMs&`?K% zUi@$2Y{#_oH(!|9#xlFSmUlRT-O34|qwR#sw(+CiD@>xx_FsAZ;nErVk7qxv_&s-d zU}I15@c}TBm+DGy5fN}X!M;BX(Gs8Z&G~3E ze3ypGl0U0-chH(z@7nsC$n|$}_S|ZCVa+RcSedK><3!=G&qunV(<>5m z#w@jMpF3O4IUwb73xDQd6M6Sv40A75WtH;F?u0SL8RBHIFoIV^*})K-UUP5Uq0mKW z>MDGbN%VCFLH+ogaLpmeQSeAsc>F*RX=W&yOA?7ZTP1-NnYY1`C5@L&>SWs+q7t@_ zh2_ycevYUQK;J&UR;^DT=u0?q+ z93hy_&c9WcVr(QLG-rlql;l>9IQ@MbvVivXqP^-D6#R`v%_#9h5Jb&df0FkrO3 z;V4A4s$PK_e`pk&UPd<2&8fEIjnK$6GjqooKhCdj4ITvN-|Gyf zlZo5=MRcj=X}7@Z!4XFqSnr^+KL)@G5?+Hu$c2`^tO2OtrP~Gi>!8*S*{+Jka?Edn zfS?`b#+sCCcaTHqP4qABxzrX127ZN#z%(zIjWgb2z-i~Zv|*^_mmr7!Uq*Mn0i1t6 zN21^?eaI?}I+QSx5J{}`XetFk5oq>|P4&R$x3?w4a0e?t&IjE0}-Z=O-l( zY#T)_P)o5?f3tpMe#1PR2$R>MswUGiuW+o@eg0u%$8bmXSf~ zb}K2vmuWa>@e}@)6EZ&C!3$S)7q+3-koSRf9FGb2^Cj1^;JJg6Yx%cXvdi{TIiaB# zwF?>3`i3MJUpRNw7kI2pxQcTmuq0MZlK-X?GRPDD*)mERlErLnrq7#bMpa zNLU*?aIPy;0Apl}1|EMtEM+`{8I?LFHRrOZ>{Lk&q;-NE`I8z8^Qx;j>Lr;+VoMmf zP(E&v$i7>XCIG!`m&0r8N!@`itfEQKIZ{4Dw6%XCn0q?w%LKKWY3JIzi zhh#APuI}+@ppL(K+WJBv7&>y%sEHNew0SOOzcMV02R!6-H^o&uvUR{l<7=5{YCt!e zE;zUpvUcBo1Vwh>4dWA@eCO2<9wsO-#!|uQ_muFxE~_JE<+QjZIr6GaHx~n?UnpdctAEljrbKHn3XCe9BkJ(qy zFi%8+PRXdTWrMxX^GObqHi}TXrsNsj1FOBYWFwWH4|iONTHAi%z4k7sw^GFy>n-rt zMQ=pZfAMt_;IgRpIn1pwjVb0fB@@?5*ycU1LDmwn08G!u;}gI>Dv3Koi5U0r*V|2R zLeio=UDe*Hed8)&;?BVhqyTa0l{=&N14o!+2(RysZ4Dq8S&O5JIBJL*@3U4(In3^o zTSvI^AfjAan20LmsoCLIeZ1g^kG0j%<9kI}OjRqEpZxS59~SfOW3+41=VDifGi3OR zsQZ@CWdzb9dF&C$8#AB6o7tG2xbFDnlVX{ojyIg>LE1jX1+OJ@R`3x|ssl}dkY@%W zJnq_T3jNVJx2eZ5s~_4O_Ss1rWZU0&z$!MdZzg+4JurfJ6b3b78CF2RArO;~ zMN6?U65=V<{{0ni5Gee`WYCKs30;aoCnAJ|@y_(e@@upne(b5`slX36;Epgi7$*Y{K5`fk&1V(AvtVG9*Bh&eZ^ZXml~lvt zL!t%FJPhsG-G@m-=e+{x?fJ?6dMHb-$L6NBWz0T@pEBq%x#uJ{CUPCd8VfH8zssw#Mr3SLad1{XqS)#`j^M3NH*L|35FdHLBQtrCJV7B#;N z-;|Wvl0@t!_d%yK7)_VE0`aNaiTDpD6W#`DNaNG5aX54G_88k;4&V|IYgu5Ue(CWX ztxp+z6O&=;tnvIBVh88NdiKHXS zk;)jxyY1jF)#|II=l~S%J>z|3B}Ofpq`t2tt|p0%L=riU+wviv~KXoy&o=Tf{1= zI{JKA+kBkC>8SPVn9B+^Wj!mC8zoeHuL5((*1*w%nMfve!2NISteygtnCMVT#)O3@ z86VYW9On6T8>0??46JPJOJa}8)<--jNgZ5+b2*$kLk?dI@>M2j2?jVmt(0^3t)CNo zZl9iijr3os@4&IK~ZBFu9dC#O7!7o`N3y|JZCtf%#&}31DJ1>yh+?rxIO@}J;EIE zvbpLHXNR&df!iC?&^DJB^bsAahChCk-QXOoK-oArn`j33_D zdhDLQ1ik+7!gkASY(PD0JeIxp-3C`p*XE5-vi6Fm`~w$xI#Ad|zDJz~Eu4AKGO)W# z48F07)*oDYjk`+wCO-`-LrM3;US5P%Aksj~2@Yn)D~D^7bI&KX>W2VtvKn?zIzG-B ztouM+JG7%0O`=hRldqyl#h;6j{f^JR$`{1lMczYVJUdNDB;cJ?r$q)73 zyJ%%( zrbPIM*VlQ?*#jPLM_lh&+g|f+%d!J`^W(|Vp@;|qX*{%48zB-)7V}*5kYtAN_k!qr zQvsyK{dTv?keF=#SIn5tG*|)=f@ZuNEi7~>KI2tr1WO1&ArF3`(tD8873k%wDHhZ~ z`GZ%Q>aUZ3lRQQS=#x0Bm~{dlc#7UEeJffKaN_@-sPGY#dmuN7NbU?8aP#q1vNl1? z8WlEG(n6Z}B}Phl_yn4t)F1L-sL2a2ou(%42Fc|xfBfrVT)il^SdVWs9tS1KMvLBf zCUye^3PSSBBOEezX<5|CcBfxc_h!x1vc*5W(UpS3$~g{hnMscu>$P(#kvSoir3#2l zPo|>-V*{6*l#JKamyjH9;VQgJhHg(AMrLJ%rJs>T6|H(^Ae_GxH|T|rB;sSXNM(bb zU4KUyg`zZ3jK6*cQETY2_YOdK%_gQF?lbjN~-UL7BBZJpZ(4Wznt+3tu8wv0ZUnI zWEF90EmM3oWcEvvG9}+3a(oKH@@qV8(Zc1~i;YAy84nyNjSvQCzR)xVT-6-9q1v&D z@OJ$#JTxWVogG%c5T8z8J$V(LLQ~Yrpj923KDPAzH?dj-<*(g8ZnBWrpV3&9j+D@e zOdb8N=vX=Z?-dAj6sfCB?*q#3krmfkQLEhBcpU&DjpV_AS6E3+XTP-Fs!PegNYeoV z@50AFss0QZPJ?q5`-bp_zRtk=SCz7SDkDGY>j<1Azf{R69?3zZZ4OVuVdNbN_X0z)0r)8AVm@| zej?iU5fpt3h}>t@ej%bJAX;98QOfcI>}!1hk@$p~hCYk;K|lWehj9H!OQwM@QL1g- zX$tmu0_8*}zv&dDYTrV;2Ho<)4OOM=Y+pBeEysLxB#Cpt`)uTMhwpTXV&3y({=|52rJ`s1HlZtd>t-!R zJ#6jFPO0LQzm7ifhDo-R1T?cQ{Q@a=u)!;rv3dF}B(WH!#tp?-0g?R&qC~xH2bWxG z^yId^!aOrG=JC-#%D(5(#O7j~^cocUYf6P1*U{l{iiw>27_oX6CF2RsR|xj9t)?#j z^@?B8-y4g}?bZ+Tq8JuM{T_n`iltCp=EJ+B5I=3fSiIW6#uP&ksKS3vDJG=GeZ8vX ztY1BtEbp3))h+9e{7A>Zad1_7TE`r}|I3Y@8nf%e>u8=*=?6h9AIodJyie5Bvg7k? zZ>9=iL8FEN7jwk$rdj>H!Iv#SMpiAEjoMEGAcJ{ry?f^cottj?s|rPO1 z>K?}{UL*RNAe`QM$K|k>QndX@^|23-60BHp+3# zE7FbM3CR6XS%5OeP9s6c?j8F2SAUpaGbmnsap@&e%aywd7_dtHt2P=2t+t%FOdNJyYcB<~_ELA%kEIPhaWa zX>TS{1QAJI%2yi%%6%uM?SLnpw5Rr+!9HrVRn&JKb6dYnuUY29rkp`Fxg!`46{CcV znVaIa>hWOPO-myKevHD)^W@`zG-hMMpU7$t`ST$?ca(-6BtX$JmH^;dY`sAfqim$x zj@YnIZ?5p;+VNXZy9|^(P0K?EG;xTd7j5TQr!cNr^&aWz@;4gy9KqKU5x5h}2u|EB za>skKPk7rJ>Dp%ME`IwZrEk$7B-Gg> zc{&w+_*%NUt=KHK*s^*sWXraUkx!FO;^BgxF)Blmu~1EXneU3{Y(i8j7+r+O%0q(K zcy(xV-?;HQADgJDAL|WjGxK)FH;Do=0q+xpd&S#koa!8RelLtPmfCqThtC)y+m*}l z$X6Tu*c>Z$rsOTR2$n16VnsgT=(=T`P?bpzt&?B&w)sTAF;BjuDLO3N8mGT>Dt{a2h1w7@abHs*CRED8@xF@|!tQ;p-ofSkQ zb@1=#b)yfbx_&)GH+?Zvb2pP3(2sRthx$JhdEX8qMPyZewuD$Oz{k5i<+{|I;BngB z=@&O=TZjb`q87qSI_;1!5$dH{B_yNBzVnCw`zvH9E9LDi;Q}94(oG)5i9RyMl1aQ8 z+s*`WYDM_EylV12uYca$Cs%xk?QXP$v5@1~2knVy%$1xDPgB!T*I2!acY8r>HEsL) zQ-92jbk2F@hUo%;!n7HSpMZfY0xCR$T+%GQO2#%&tK+$4Kim8iKBAaCkMvKqg%`e^=)VBw%{&{WAuU0v^er1^$+3EMJ zuGo0y_9fzi#G$tOv!ykD8=GNjFycf~yC}{6`q0nmSH-(_8mGr!*2WA@naEu8P9K)E zcreduyhVgiQi|2I`y1k2YT|n;Gm?l}1JS}Cx2{qHeV)+G*Y=U~(OIAadymEyODN-S z_kE+NL@ovNUH?${YMUuuf`qPV=K1B`?+F15;G9Eza?cJ>j+4rK-N8rw%UGROqTZ=T z18NZ~3Vn9l9VP;kqQm63#zHjfE7eZ+W%u^LYn}Y!k`(E zV#lDYfYMBuRkNYA>>97&>$fye#cDT*QwLD3p={&2U(Qc`tVf(raga)OkxpT%whbvx zd$)iyeA#;^prH2lR7z#SQA9i2;8&s{B%U8T|4}Z+Avm!SVv)@x;!*x9J}17y9Jd$2ORwT znt7qg7yl)Ams_nf2a0}LE_#Np=Bw@Nu6oDAWgp^J<5-BCQd zVY5%$Y4>9s-!<{w-!5V3#9n`i-Ep3e3b1rVxZPO7rIF&#eBAXl|I|8=9~Z0TBAwAK zfztQO56$*IBl5)SO;8tgcqBzIY#MKqUZq_S!F7;tNUAcGYS&IlC8@8XWcEh9PoT1z zyWD4Q`xq94c`h0osYrzP%*Jc8F8+9F=*e0$L0eX!(o146b6=3rueyKr_C9T6?Y#ry z*#0DC(a&UQaxaRQvE=QyxJ^h(Lp|OKfyxoRjM+MB6>_C?aP((!kc%CK5b@!@=JX3r z+CpWcIOd`0CDya~ieoAqO!SkGYuEVM3(mjm2+}VHB5OYG9+}>h5d0_c&XoMuqT%Sz z42IV{1}!4s7zllJr>qTFo6_<2s4}wH^!rF-loDvpRV8To?)4jryuobLSPZIyV2MsF zz1N^xSffcA?w|GQTZJqdbE_h3oChT$UC)IuF9-!#{tlDmdb7-EQO~cLNrBumkOHR0 zD1+7#cQLZ)IujE>jF9WGn#y_{gkP>kHC59&K#KP{^$j8|s*DEzL@K+N@cV)Qi-J9(t1 zB;nvq5Nog`Gt?32f=fiV zYZqN)SPOpVHS5P5e6i!$AQ!>kAkJQO5_20#>Ex~dmihO1u| zdKzwGF^tVngW|ICYQRsjssK@Z#=$XL5H^X7on%q)?H912;a(Lciq7bd7I@5SW=ia@ zg6>Fhaa(6Rb-r(z`Tk$n zT=pl+&M!6QT~(Tty@9T)57BHs_WYbv+^#g8pO@l{BHH7G8+_htS3K{yNBdto5zoJ$ zo-h(1_;t|}dl37q8#d;)9r;Jbr{iNmdn0NY#}bq{v!7f)iy(F>b*+gD6~jI|{MsjO zj-d3nizuB^$la&S!i4A21edmYOvrC$LnWTh;GgkCN$52y^ksl*u5pFN+b3&L^03t2!9*DRe z>MdyZ%yCXrKLs%`Nno`HG*8p=o=xM-xEWH2m;?lpa?MAL!dx;`EqR)1h1j=CBcnHc z2ezL_KhjI}3*;F{TDp$1ZXU2e zxOVl?X(z+pRnClFbUH!id#|Y%Y<*EP-0q4Rit>=X?`>~WPRDZ`;2Jh8t7Gi@+`>_f z{;F3;5%4gYwqEok5|1b9l!6$Bcz>ga`n$24?BhLrEt|v|=$HA1$ZuH_lnV`G!kZFn|eBNQ>ImPZ?Rz0?flyhNa5M(F-Xk|Pp6aU@5~X| zt*^l&@qz0pVz$KePawdCdN=u#W{n)dEB9Od7H6A=dYF@(i6xRJ8O*bn@(QbDgL199 z2DR!Cya&1U_KD&V+W35<`k=#Dy2k`p1NqB>D6Od#N|3Hi7N{{r?1oz!#p9S3%|SQ8 zDag7jw9;nDd4>^){FB8sg^Kj#fg(Mw`ao1Uj@jRyPh@I^Yikzg?^>)3IBa}nv0XN@^0wl`c*BN%MCD@@5x;_9UYhh~;973XVe8d&@es4y-xX8s9PjPL z#kiNFWPXfc>nmxezfp~DMa`vs6uOR1DYm`LQ86x z6}JUoZ(o#t*m`Xz=IfwmhuegW06qIdvG!=Ze!F8%_B|a%*q>bDOTA(tp2eS)fmkG} z{XkpZ+&fOmLADEP2%D;8{9!r+a|?_M76nfhWYkiV7{m74$10vjhM1?Lko7`e3Q(<1 zysra?9e#mTl!BITmAEmpKm1kf%^4^Z9asv@#0VO6!ll(9Pb;VYBy*m0F!qbv9A|?o zRQI;}@f60O1f6)=3i z)nxxzrD5%NvJGRZk(LcsD4G-<+I0koI7u?m2~HU~owoik0kGbqq+(&~KdQ_XI8~gE zro^k5ph~JI`+T)%n-vwdAwBDjQ1?9(e`!)cnxZ^|v2*YGsHZ+){!|#O*p31(gg#QJ zD~lF#I5dQb-1;Uy{;uNU_nF}SX0#lTa=pOBM+c2K2xxRe-_z0p&?oLeaF|SD+y~R+ zZkp|~U+xilW-?!X;aq9!9~Xop8ikO%x#N;9da=B{)1t(Z=eE4FY~3?@7x#D<;7+<= zk8m~X6fUpxR=DlVs_mUz)KK+A-*{4ON<0wfaPpM zxQ)Br6Xq#?kO9N`lo}QCp*uh%|Z9r;;*x&W8UdYo`X;1}x@4g`Hl{nHqm7s9} zx7*CR+)>yxKE%Mt=cnOo+&>LS3zO=DF(dCbrpVoS$}D&b8++}L1v5??P@a#2cW>;F zn-8Xo4$*--xUJw}E~vp2q284u{05O&w{O-mJ*V$>w%_!tS~IC6Tp+n-VvS!FkivjF zC|(Mw#UUg`9Lz$u*r)oN&U3Fa*T4%Dr@yIGf+PmgItt*Z3DfkSNx*-U0UC7CW{YJf zZdhN)AwXF*LfN_NZM~=M3%5)kw3W1Th~V`09;Z5*C7$j{5NgG8X~LU9xl3(5@mMCq zMFf<6?+S9ICFuo>JWdF(JVvbOz>puP5r|7KQ~FRYENkE-Im##q8y%QcDCl|N1foBV z14d@XaU@V~Kzq2iYCZIYAGqs}2&kA&5EZ|Sjb4TMI}&rw1G&c7BtZ1U1}bypr-(_# zr(tUxOa_%kYNE3^SDCtrR?M~W5%6Rx(tP8Cp`HqgOtQa+) zsDD_jD3#j(0ITYq2YjU|OO;gU22G`HBP2|TVxx;Zjj=P$D?A;N_aV&pnu3PY-G!4TKpt|40u zO3u)|$vF?6T^Fx_{zp<3#*mlDVgsv}q2L#1dSf40*o#YE=u<(O+a7%)0f;Pri0spv z|3nh4)T7V8r*QQT<-zV$UKSl7?qvu?=KU8>vlqx! pd-c!2KP=7{GN;VsrzbWjpk1X(mett5^AFWpURqhILc%EUe*p?F)t&$V literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/icon_history.png b/app/src/main/res/drawable/icon_history.png new file mode 100644 index 0000000000000000000000000000000000000000..99c7b5a0b844bb566cc0e8f8975e389357bd9203 GIT binary patch literal 7192 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 VHy1;F^ygolmYSYwxw38We*so#6&3&h literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/icon_language.png b/app/src/main/res/drawable/icon_language.png new file mode 100644 index 0000000000000000000000000000000000000000..b48fa58d13350e192b28cc86bd29ad219dea304c GIT binary patch literal 8898 zcmaKSby!qi*Y*%nlF~gih``XDGo+M&q{P5b5(5kj-JMEzw{#5(Qj$tbmx3TYfKmzq zAO7CwdEW1j@A=Mkot^i(*V=opbIv|zomgFMRU!g90ssI&q^<_hyU&aMo_IL-Y5b%; z-+e~wsch`254ZF5hI-fn6k%{{TcElN)ZSLl77Fuq|7a@%0AR5=8W?*TYiUZ^z+L#E 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/icon_math.png b/app/src/main/res/drawable/icon_math.png new file mode 100644 index 0000000000000000000000000000000000000000..61ec0f88bf42d8003a99434cc115047be7ad2889 GIT binary patch literal 9954 zcmb7qby!qi_wNkdh)4+1Afmu9G($+2lyt)YLo-9n(BO~~A_&sbNOwyPEs`SL2uOzl zQUVvhzxRFb{o}s(dG0ySIlDe9_FC)N=h^$LNG%N|QX)Dc002O$qAag{S7!fx2=MOS z(W7=ecf~`bf+11|VT<&Fy2Ai6HV7*ivx*DU4yFx*+W5E)z$5?wY!(MyL!_a)8pImm z!VCS2;q`WLy`u&IB%XV_Lam)(NMVLaItjzyFkWP}U|D%+l zx)!q>!X3se#4E&O%?B1_77^nG3xNei!Q9OJd|&|(p8yCfzys!o@QFddLd^fVSns5{ z+t@<1#ilqYL7&^LO>udFE3s%0bYc=9SAHYCI;f;2l4as+#z^8eBel^HxJx{ z?cW^aVIJ1*4z5TC1f2OVN2nD7g_LBy)AWC);Ntopws4Psnd#18AaAHE2+Yg(cS`>T zs;mFMp)M}}L3<#zVgFm-|0`n;T_0B%NE_yXK)GArInI{tuP9fDoI4DPM7ZlB5YGR$ zqLw`ZiSV#TxH8MhF+VYIfZHIvJh=XWtE)p);2uaQ+#04LFUfkxfY-sn2EqrHk&}@X z5ap8-;sb*fWW5 ztwG~k**|&ucXHkSw-xBY2b_G&*Ql%HTm`0Q#A zb|yDILi-E?QyN=007E8ZnAQy*e9I+wzz-gT7~nVwn-B5N=wHx3qj#XC`!0$ipqm^` zlYr7)!>0(0O&ggP8>a1K*BV`wWqw#NorgU6`f?9hll|*8?LI=XJ!0n-Fv8L6O_WI3 zFw?JSItD6;{`R88^#?ql0S+d*nau75u3KPss1vA7$>P;;>_h!2+Ix%2{k}gWqBLJE zHWwPd&dO~B<-K-&6L{bb5K-%wzMmjRic|q;T!X+zrL-_}Z{W!B6;DoO`Vg7&pG~K}X#nahSw)fdS2MepS5a`O;O9BOl72&(xAAuh3hVy|&2l3&d}e%t(}>P#1jtFvvklStU7vj+7pWK z-cuBJZtpC31LyPN7rD=RfuR3e>N{9c@qmuL^g2iPgqcShOI~so6WwSmj z{a#%e-R-AO4ZkgM_Ky8LzU~Xir)-A>&iXQn29K-b{n^ztx^7HfO>|HY`8AbL{yA{b zn8Ey-^Oerhv>u+RVJBg8szqTk=l5E3?QjL0KH@))rpsm@Qn*Wq&_57Xq1B4@?__jc ze>(?AD6?L?`vs9ws_a;x!dov(awI*-p}$q}6)z~uf|k-NyyQGNXZ3|Vm(}fRsAN6Q zAjF1D94G=UXri*{KdMy9y(*4Tzch|uTr8S~9(~Tf%80WI;=xwB-9FiYLdI*s%XZu;agYbQdF>1L6~ z5T5LAzA;>^3MwSjb9stB7R-+ce!ZDomMr!Aklwcd@F5ew8`@zIl)*PQX=??RXj95|MMnKVBTt5EXT@38P$u7=yL24&?a9Zf z{3>-&>|+Am-4QRG+gR{Pv+GASF3OL`Db6ds-6-)eyC9-DWQ)Kp;JWFRZRea!r5!&u zb2UjwENSd2I+NW}*Rtp;Ul}tmN#yEE-ZeszM%uIMM1@X22I$lR2Ayz}Pu%W7kz**H z$EEga06ZmMKH(LA8Grk>U`~GAY=^EkEYL-pcR&#vcZd2#{2;;A&}^Q+r6x+>k{}E((pcA*glLCM5!za`^ZL*5y=&2U zEMIa$k&MQ?C3-)EsAjh3W2=iDMCj63E;JU1I&m4V3kdrpmOfKW8D*~ zDHJVi1g76oB&4i$JSb@|1KJnuP^?<9U0N#QW@^$>(NWtyTk?7@570GbW+Z`-G;rs( zyyTm~5aDGYURyznDJh=%hFo|6=h=9u8Q*AR`O!@RO&hEXh%Vm`7Nc{GVN|dTP-^M7S!WqN}(;k=sx`3(Ac0L;7{Xh()4`S!avFLR-?evv*T zD5{*AnYYn>J%hEq<{x+(QeuN@oNz2hHPD?>{lnv3zlK;P()+z;ZlpSHrgs4!Suxn0 zI3FZt7zrsxLZFImZ9_k;7f9x}js5!^kU2*I&1L<8gNxR5dklHg0l8r=);o=)uf`w@ z7tM)JF?;a}0kxJNotNA9=t~~z1!k+Uek&?H1*R)~x#?V)ZzNth2gXs5dGa4yUYsW8 zG1;PNe~g}g=qHQ7V{XK*QvL4ZMrGM#fw|xc{aC+V_rfdqBoXJ)(zBKGe8q$p80mq` zdl^#76i2|2%x~X*B;%N7w`a7#$8l=)Xue__x4t7=!;P=s1hKEhsnIbh4Cp5KIb)8b zakqUO-lh7e#G(!X&FQZ6|eYJ38ZHa-TELQkl0Vz9eDIZ>Cnh?bz^Qx>&_stf?MKU#DuFKup3Hmi&V@ED`b@z^#` zj0jI@Un-c+vECkqqCdf`I`wdHsRRVII|JyshQx6~tsH0XgkT>nK zIX3rMFa=RFbDO?$_=fzf8iSsFtX*fe-wcZ~IaPykwt}6Qnl><$kz0G|;OW+qB)g)P zy`A;ovFGf|Lc;3ev){4;5IQ|&cus@s`_oC(yrVV6qXt*b?Y`W1sop3CXr3c>0R^}y z-hcO};^WWS%lcEI*7MYz_OM;qWUDMle{Q38w_>k8c(pzxe@&2<&l%Tf#a$&97M+&R z>u*g4h4wP?@mM#zQW_W9UNAlag!7o382+3#K8(7meksFiA?wRwrZkweaiLv?Rb~LF zSg?9F!-^_|qB37+QFQv9nqIlZ=u;{)@$>9#`}C{`}^#%w$wWcLQ&YdDEViETO_Ph(Vz^=AF0(;AMzW!wGP)A5Y#M}vZ8B*pSl^4_ zJeqO2dK5MlIG!(G>$+fArlB@!)saW>JDsj469t{KJaMtxnUXM5AG}o&J5PgQTWiuF&qW zB!SK$ROzfpaqjvv{RYQ2a))c}&6z4nBwL`q>%>M{3c%t_<4FZ>sDz>a`KhJ{plElM zWO#E;87Cx`Jn_L*e)(Y^2afX|t?zCv_3Bbc_#qAlt|huJMRB>A7$aDMEk7U+)YTeQ zkW+u4bx&+(Q0E*kpM_C$_EtvyTOlstO!tjzwTI@wB)7G*Y;T|@)eyrG{h+qWU)t3l&NTcX=p79uc3)IU3#u?W>}E zb|H0<%g8%-{a%DoM7^-hRaop4u%=ij|4}msiJYIT84f)dISA zCYhWr_W@vqdf1oxxK@`J3{`7b(IvIdd)3}Ei>NN_0KSC9JeXnB6BV_J;C`DzY9+Oo z_+4J*btD$5I=`o^gr36|h0eFGQB~B%8SDMJtBXbUiC|=&Jc!_}VJ53qgr~|79@xAn zg%mX_%}d2{)WV36(p+%T5_8#`iKIB?99#dE>be*=ywam=R|Cpa`>Ga6as3@!K#^#K2wQL21T>qY;+92vWkHEWD0D??QJJWO= zUWCTvjMq$uT3vdjkPznhFGrZ1rq(CXjkS#%_BgV`IXxX*m%p-BTgdGAnJ#Na z*<9|egtD9ouWl58%*(&HdxmFT0V!1j9I0#JGT2_-ZGGhixJ9ts&tDA5mgshGKMk=k zFJz!qdTgkW*21DlGt7D>7DKohbkfnY*7fUvq&4Kxq5*pFo6!BpRIpBp_mdp~F6bx+s{3zsFCeZA^rC5P?TbiI(b2$ zq_b0!Ztu~QO+7YmznkAfsNSrNk z230;L6^vVchCeR~o?gG(4Ri6Lc*KW2vH0XhL-$iE@EsY136UugFzGF6~}C z{U%~!6+d~GPhGl>CKFM9tFZaVA8s3;kbt+XgL~h^SSKbWb)NeD$k$I5(>blHdXiEp z+iOv?m~{$h>}ppH;fvD8iygeP^NR)}6(rBH)%GMCZN6MDuPeTxb^gu9UL~a<1Qj)T zZixib_be@NP5Iyrd=Xc`v|PpU_NrUcZ{asZOT#_~cu`c0rf9RbwDTuRDk?851;k;z zK43(e^^T`uEp0f;4g3a!)E}}+28~Bt6y9iSI3ZB<+Zw2=XN@~m=?51R6zcHWjb`Ho z$efM`s)u?+Vo2|Jw+q$ET9fOcH$=A+DF|l(KQ_Y10hs<1OhaY#EJsdF3ox{JU@REsl( zTH?2tQ^ADG9ft2jJZ!Rnc|)OC`JHYq4S09o3ycsJ*V(}0t;h((NtZR1&$BHADUGK& zP(|gMx8nT!&Hi%ry6ptk+xrAQ$22!zCesTR;w_O8DX|!@OT!>Tc@N<{nNcSwr*Da*oyw;>w>$TFeLvnnGVY(;62X@ z7uMW~KSOAUnpAA*@*FP*38CCm(P6z_XIUX=ynyV3yETqDyKM+>e6R6LZ@!53_oh^O zP?HpRDLg23jM*%*TPL9fH6xVk5=a)r{NspP^=tSFlXgU7lo}7^M>l?9&q<+zlGAX> zd3MpTw!%mPY@Oo*QU{*jhnY2Y6~We%Qi0Z1eQ4l<6Z- z%~L?19jL&qdov%N%&9TJmnsGU9lL|1rdYEw?F`GY4s#^7^}vdEexaZk7Vim;J~Wu> zw{W@pA(cqTV{N0QEyzK`8FtP2YD~r~KMxh~KvFx7x_qovrZ+@SWRVmu&%FEd{P^r# zVp^p&;MR)DmU@RdUg&5JrN17T^YXcKY2cu%R_e~z7~-!hp_Os)mR8$2sveAAWz+r! zeviG?)bh?*X2^J@0-I2WO(!|;?22eF^P`FNkRIlboS0zO#&xTsYWV5Ur;;IVs8U@{ zbWbU^Al{Q1ORLiBiPigiET!qF>3md)u!-Uf`HX zoIg$hvq1}-od)PD&G*XcPn7ve_$>)-{j;rr?JNUz^SL2)x$$BS_f6M@sk7FCj4ieOI`5PX9)Xg=j6w+O0>*xa8~kYZwa5=O!gi18{d*rWf8mXr=AfJnLt4nF zSC8an^iMXI^#m4b%axs55DREOCypi+>eGD~WmN7}PX7qQS3+HUf@fW9r$Py-WAT`3 zZo1EG3fnEmi$}A0Zpe*3Q-<~{St?$QcQcD>(4D)jg<(6(t|bYe!eUYL`RvSqZ-+FV z1ihJG9|`TA)8xCnXd|^Ed09xHv|2ks>>Ictb4=KaZ&<(I8nJ65Q|ax(kxLmHADRYO zTAqpQ@>4){HeWPB0GIxuTu~;dMFvUkgQJn0sN))^K+JomY`#EKrk`3JW`wW2F;h9* zx(|_8cEv?1%gju>RN@hcy3g67&pvnk+CRTPY8)Tp&oriEq>>*DWqrd zy!ub`5x98*L9<)lL5~4U8&iAR1kGmSyWit0clCXgBlHqwSBH8Sz##Gqz;D=BVxDR*m3B4-W11#=?e=5htEG7Pr(n0MRwf9glz z^pu?mH_t||=$2r+LJ}ok%`RGnjE*?EmCrJV$l)~E(RF)rK91HXOT@m>=qj}3#3~wc z(&dtyu1YbrC!oT@7^hZ*0ZaeL0%&aoy{l=j{*Z_~DD0hive3=kVvUkUO6$AdC)rc8 zO~3eq*$kgHTsZ11zJfC^6N`SR;mGJ_f18>rTte{8qu>p0Vf@=Llgn)0RQ*tIs)n2T zXKbPYGA4E|6>r9fv?zwdYIv^EDW)j&l%|>O!<}i^iRCh{T=?=I!(o`@Jk&+?r9kz! z><;qZlr=iO*_t7g_{6RO4S2Q0sQD6V@FX!+LoBoH;+5?r8f75-AylB)F;w|meSTP! z5WNP9mesL389?i_(mkcq6i~mXM%92cx)s>seU=D#rb8M3a5mff(jPZTpXVV;RDDPc zl7H1Kv9RoBg&Cm`Z|>5=ryDfOudjZqx{p~>K|wDk zOXQlVsD-z3YAWH!ykH=Be(Jd{-;ce|WSXr3YY++78u9~(?t81I9h_LPq@i|sjqI5s z7BQFex4buj&VbUX!ifdK<%2|gKo!J)y%uYzAkvnD#gmOe>pBekqXnhEKT#1$fB zp6=)sZ9%1@9cEK|NV*v0E`OwaN~AVyTtCTA?S*hBt6yk!(-dnlg{j{ts{TD);rk3zE;yy^Lc*LENoHBiXuAVe>lh@$mThBLT zdera!Fl0z;v!KmWD>}c?l(+_U8dM9X#&`6L1}8f#8C!mRbKkx&HYvY?lMW!-Xj97> z?TS}kfzoGSj;!-c&hNMHa=pnjfZ>>5b$xwrHo@eH%nM5=Hi}2)6&-{k`>8msyl`W; zCrwa<%`kqlK1D&X!PELQU3f`P!TGD0?+FyA?GH6bhq!zEk{;nR)_#yBle!t=a&^kPC&{m z>u#IH@z7qOZ$Gi;Hk0%kSzX8&jtK7Q?x1jObM4d0)6LtB!rH`1wHYq+>k*MZq?S|4 zRk*ZRO^5l_=kF&$&(!KR(8Q%}3LKG}}ercxb=iKGpNr{gV3RA- z5{gsD3lVX;GO`U0TgpR>sp>52P;Ur8mRwjG5np#ogH0;~FV5;oky zZcza531aj+Lr^^r_Bb+6o~oAOBl@+y2AgqEl$Ad41O_Rnq3)#F7F~VB<$_OD>Fr6o z1&PrQjh-VDc*)Ao@Rap|I8ALJr)kcbYy;rIk;Jth9R;QJr?TUH)@sutIL+Hfdk>Rz zKGd!D?A^3*J4?vJtIN-FSw_ZV2dmoHWPDx}3{x;#0r`Zn$i3}MS9CPOAFE>$BR?+T=lp<%vhf|^#sx=;+^iM@~^BjTANMxM6*YYy1GI=GT6TV z*zr375;iZ(ocJH5mo6DEG1q;dNhfzcuiS6lGROdkCflhzsQ$G8PokCZ=oIEk;|sl%u-6AtUkQ!5sOL(gYUtp6x-_x^_luD-I<&e z1sIt4Zp%(US*sr9js)+Pcgefkt5E`;cX$>4RO39~rbTyLeU7;J{0x6|eE3!5+uA^1 zKHqFWo8f=~{!jlO?P)L5!E2rF?adj%;MLr6hVz@Us2f#>s-Sz*DS%hjtavMQ^Q=LK z%Xq4Djn8jJ2HXvRTRS1w96yUx6mp4;w3h=h+?Qddiq~!y;#=1cGA~qG?AYXgg!Rk9v<0TTPWpDggfM zVRK%M{JB7APWa-X$3>dv&lxk0tYueT=_oy`UVO5(si>kqbQxXqBVD%~K9@h=OI5q| zO2kb%*)@Ad0c+Ws4f}FU1r2fo1RkCBi!h45^bLmpLTsOd;x2-HTIA-$d<$a9PMs|N zY@RWssqf?m+d6-rU0}NYo_kY&`|P=F!iCpZa8|Yj>3OrLagTFcC37oxYT9x>{UnfY zfre^0CO9hKu(kTtA$QQBPLF@q`NioonM~&j+prd`obmpK{vT za8hA^uG}q>id|&~Y}$)hKs+G*39`5>WwRS^HGB+M|4m`ljgZo7zdO$t89TP&W6TM9 zRFI`mKT*hZA3QIiSz{$Jb?hB5`UrSvg7mjNO!?XKymfa)b&h6)1P%8)zA8L}>y}&B zL!{)pFKc@uK!p~X?EUWh_PhEhz?-m@&)``NdhA0ZTM0jLgXj=yJ5G>NhqplGdYwT$5|t%Unol7QaRxA1<}q{Sx&Tbt3iW*_!J4x37N_4NjlqD>Sc qA06kh{$`x~|E8rZ`t0B0(gK9V>kBUM?e_j=ovA2j$XCc(2LCTZ0g$Z# literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/icon_physics.png b/app/src/main/res/drawable/icon_physics.png new file mode 100644 index 0000000000000000000000000000000000000000..fc622ae233d31c9b110cf16566e9f8b947f217d5 GIT binary patch literal 11698 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 DVYTXV literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/activity_details.xml b/app/src/main/res/layout/activity_details.xml new file mode 100644 index 0000000..5ffe751 --- /dev/null +++ b/app/src/main/res/layout/activity_details.xml @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_download_manager.xml b/app/src/main/res/layout/activity_download_manager.xml new file mode 100644 index 0000000..26bedbb --- /dev/null +++ b/app/src/main/res/layout/activity_download_manager.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_kind_detail.xml b/app/src/main/res/layout/activity_kind_detail.xml new file mode 100644 index 0000000..5b2b678 --- /dev/null +++ b/app/src/main/res/layout/activity_kind_detail.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_local_manager.xml b/app/src/main/res/layout/activity_local_manager.xml new file mode 100644 index 0000000..7c6be19 --- /dev/null +++ b/app/src/main/res/layout/activity_local_manager.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_manage.xml b/app/src/main/res/layout/fragment_manage.xml index 6c58107..f522508 100644 --- a/app/src/main/res/layout/fragment_manage.xml +++ b/app/src/main/res/layout/fragment_manage.xml @@ -42,16 +42,10 @@ android:layout_height="match_parent" android:layout_weight="1" android:gravity="center_vertical" - android:text="53个应用可以升级" + android:text="0个应用可以升级" android:textColor="@color/black" android:textSize="16sp" /> -