From 0b69c9294a3576c711f693294ca0e6aac8095e1d Mon Sep 17 00:00:00 2001 From: fanhuitong <981964879@qq.com> Date: Wed, 20 Jul 2022 15:22:37 +0800 Subject: [PATCH] =?UTF-8?q?version:3.2=20fix:=E4=BF=AE=E5=A4=8D=E5=85=A8?= =?UTF-8?q?=E5=B1=80=E6=9B=B4=E6=96=B0=EF=BC=8C=E6=9B=B4=E6=8D=A2=E8=85=BE?= =?UTF-8?q?=E8=AE=AF=E6=8E=A8=E9=80=81=20update:=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 15 +- app/src/main/AndroidManifest.xml | 2 +- .../uiui/appstore/activity/MainActivity.java | 4 +- .../uiui/appstore/base/BaseApplication.java | 6 + .../com/uiui/appstore/base/BasePresenter.java | 7 + .../java/com/uiui/appstore/base/BaseView.java | 4 + .../java/com/uiui/appstore/bean/BatchID.java | 28 ++ .../appstore/disklrucache/CacheHelper.java | 473 ++++++++++++++++++ .../com/uiui/appstore/disklrucache/Utils.java | 101 ++++ .../com/uiui/appstore/gson/GsonUtils.java | 144 ++++++ .../appstore/gson/IntegerDefault0Adapter.java | 35 ++ .../gson/NullStringToEmptyAdapterFactory.java | 45 ++ .../uiui/appstore/manager/ConnectManager.java | 134 +++++ .../uiui/appstore/manager/ConnectMode.java | 12 + .../appstore/network/NetInterfaceManager.java | 210 ++++++-- .../appstore/network/api/GetBatchApi.java | 17 + .../interceptor/RepeatRequestInterceptor.java | 108 ++++ .../uiui/appstore/receiver/BootReceiver.java | 4 +- .../appstore/service/InitJpushServer.java | 100 ---- .../appstore/service/main/MainSContact.java | 15 + .../appstore/service/main/MainSPresenter.java | 49 ++ .../appstore/service/main/MainService.java | 167 +++++++ .../uiui/appstore/tpush/MessageReceiver.java | 33 ++ .../java/com/uiui/appstore/utils/MD5Util.java | 112 +++++ .../com/uiui/appstore/utils/TimeUtils.java | 17 + 25 files changed, 1703 insertions(+), 139 deletions(-) create mode 100644 app/src/main/java/com/uiui/appstore/base/BasePresenter.java create mode 100644 app/src/main/java/com/uiui/appstore/base/BaseView.java create mode 100644 app/src/main/java/com/uiui/appstore/bean/BatchID.java create mode 100644 app/src/main/java/com/uiui/appstore/disklrucache/CacheHelper.java create mode 100644 app/src/main/java/com/uiui/appstore/disklrucache/Utils.java create mode 100644 app/src/main/java/com/uiui/appstore/gson/GsonUtils.java create mode 100644 app/src/main/java/com/uiui/appstore/gson/IntegerDefault0Adapter.java create mode 100644 app/src/main/java/com/uiui/appstore/gson/NullStringToEmptyAdapterFactory.java create mode 100644 app/src/main/java/com/uiui/appstore/manager/ConnectManager.java create mode 100644 app/src/main/java/com/uiui/appstore/manager/ConnectMode.java create mode 100644 app/src/main/java/com/uiui/appstore/network/api/GetBatchApi.java create mode 100644 app/src/main/java/com/uiui/appstore/network/interceptor/RepeatRequestInterceptor.java delete mode 100644 app/src/main/java/com/uiui/appstore/service/InitJpushServer.java create mode 100644 app/src/main/java/com/uiui/appstore/service/main/MainSContact.java create mode 100644 app/src/main/java/com/uiui/appstore/service/main/MainSPresenter.java create mode 100644 app/src/main/java/com/uiui/appstore/service/main/MainService.java create mode 100644 app/src/main/java/com/uiui/appstore/utils/MD5Util.java diff --git a/app/build.gradle b/app/build.gradle index dac2a05..cd5d76c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -45,8 +45,8 @@ android { productFlavors { beta { flavorDimensions "default" - versionCode 22 - versionName "3.1" + versionCode 23 + versionName "3.2" } official { @@ -175,6 +175,8 @@ dependencies { //glide implementation 'com.github.bumptech.glide:glide:4.13.2' annotationProcessor 'com.github.bumptech.glide:compiler:4.13.2' + //磁盘缓存 + implementation 'com.jakewharton:disklrucache:2.0.2' //aria implementation 'com.arialyy.aria:core:3.8.15' annotationProcessor 'com.arialyy.aria:compiler:3.8.15' @@ -190,12 +192,19 @@ dependencies { //RxJava implementation 'io.reactivex.rxjava3:rxjava:3.0.0' implementation 'io.reactivex.rxjava3:rxandroid:3.0.0' - + //生命周期管理 + implementation 'com.trello.rxlifecycle4:rxlifecycle:4.0.2' + implementation 'com.trello.rxlifecycle4:rxlifecycle-android:4.0.2' + implementation 'com.trello.rxlifecycle4:rxlifecycle-components:4.0.2' + implementation 'com.trello.rxlifecycle4:rxlifecycle-components-preference:4.0.2' + implementation 'com.trello.rxlifecycle4:rxlifecycle-android-lifecycle:4.0.2' //google implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.google.zxing:core:3.5.0' //fastjson implementation 'com.alibaba:fastjson:1.2.83' + //MMKV + implementation 'com.tencent:mmkv-static:1.2.13' //腾讯移动推送 TPNS implementation 'com.tencent.tpns:tpns:1.3.2.0-release' // //极光推送 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index aba7557..51c06db 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -102,7 +102,7 @@ diff --git a/app/src/main/java/com/uiui/appstore/activity/MainActivity.java b/app/src/main/java/com/uiui/appstore/activity/MainActivity.java index 583a072..4370338 100644 --- a/app/src/main/java/com/uiui/appstore/activity/MainActivity.java +++ b/app/src/main/java/com/uiui/appstore/activity/MainActivity.java @@ -29,7 +29,7 @@ import com.uiui.appstore.fragment.FeaturedFragment; import com.uiui.appstore.fragment.ManageFragment; import com.uiui.appstore.network.NetInterfaceManager; import com.uiui.appstore.service.GuardService; -import com.uiui.appstore.service.InitJpushServer; +import com.uiui.appstore.service.main.MainService; import com.uiui.appstore.service.MyDownloadService; import com.uiui.appstore.service.StepService; import com.uiui.appstore.utils.SPUtils; @@ -66,7 +66,7 @@ public class MainActivity extends BaseActivity { private void starService() { startService(new Intent(this, GuardService.class)); startService(new Intent(this, StepService.class)); - startService(new Intent(this, InitJpushServer.class)); + startService(new Intent(this, MainService.class)); startService(new Intent(this, MyDownloadService.class)); } diff --git a/app/src/main/java/com/uiui/appstore/base/BaseApplication.java b/app/src/main/java/com/uiui/appstore/base/BaseApplication.java index 0234512..2824e77 100644 --- a/app/src/main/java/com/uiui/appstore/base/BaseApplication.java +++ b/app/src/main/java/com/uiui/appstore/base/BaseApplication.java @@ -23,7 +23,9 @@ import com.scwang.smartrefresh.layout.header.ClassicsHeader; import com.tencent.android.tpush.XGIOperateCallback; import com.tencent.android.tpush.XGPushConfig; import com.tencent.android.tpush.XGPushManager; +import com.tencent.mmkv.MMKV; import com.uiui.appstore.R; +import com.uiui.appstore.manager.ConnectManager; import com.uiui.appstore.network.NetInterfaceManager; import com.uiui.appstore.receiver.AppManagerReceiver; import com.uiui.appstore.utils.JGYUtils; @@ -75,9 +77,13 @@ public class BaseApplication extends Application { public void onCreate() { super.onCreate(); app = this; + String rootDir = MMKV.initialize(this); + Log.i(TAG, "mmkv root: " + rootDir); + tPushInit(); ToastUtil.init(this); JGYUtils.init(this); + ConnectManager.init(this); Aria.init(this); Aria.get(this).getDownloadConfig().setMaxTaskNum(1); Aria.get(this).getDownloadConfig().setConvertSpeed(true); diff --git a/app/src/main/java/com/uiui/appstore/base/BasePresenter.java b/app/src/main/java/com/uiui/appstore/base/BasePresenter.java new file mode 100644 index 0000000..54921c8 --- /dev/null +++ b/app/src/main/java/com/uiui/appstore/base/BasePresenter.java @@ -0,0 +1,7 @@ +package com.uiui.appstore.base; + +public interface BasePresenter { + void attachView(V view); + + void detachView(); +} diff --git a/app/src/main/java/com/uiui/appstore/base/BaseView.java b/app/src/main/java/com/uiui/appstore/base/BaseView.java new file mode 100644 index 0000000..3238992 --- /dev/null +++ b/app/src/main/java/com/uiui/appstore/base/BaseView.java @@ -0,0 +1,4 @@ +package com.uiui.appstore.base; + +public interface BaseView { +} diff --git a/app/src/main/java/com/uiui/appstore/bean/BatchID.java b/app/src/main/java/com/uiui/appstore/bean/BatchID.java new file mode 100644 index 0000000..2e7ac9b --- /dev/null +++ b/app/src/main/java/com/uiui/appstore/bean/BatchID.java @@ -0,0 +1,28 @@ +package com.uiui.appstore.bean; + +import androidx.annotation.NonNull; + +import com.google.gson.Gson; +import com.google.gson.JsonParser; + +import java.io.Serializable; + +public class BatchID implements Serializable { + private static final long serialVersionUID = -2320968190625923242L; + + String batch_id; + + public String getBatch_id() { + return batch_id; + } + + public void setBatch_id(String batch_id) { + this.batch_id = batch_id; + } + + @NonNull + @Override + public String toString() { + return JsonParser.parseString(new Gson().toJson(this)).getAsJsonObject().toString(); + } +} diff --git a/app/src/main/java/com/uiui/appstore/disklrucache/CacheHelper.java b/app/src/main/java/com/uiui/appstore/disklrucache/CacheHelper.java new file mode 100644 index 0000000..0b83d99 --- /dev/null +++ b/app/src/main/java/com/uiui/appstore/disklrucache/CacheHelper.java @@ -0,0 +1,473 @@ +package com.uiui.appstore.disklrucache; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.os.Environment; +import android.util.Log; + +import com.jakewharton.disklrucache.DiskLruCache; +import com.tencent.mmkv.MMKV; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Serializable; + +/** + * 磁盘缓存帮助类 + */ +public class CacheHelper { + private static final String TAG = "DiskLruCacheHelper"; + + private MMKV mMMKV = MMKV.defaultMMKV(); + + private static final String DIR_NAME = "diskCache"; + private static final int MAX_COUNT = 1024 * 1024 * 1024; + private static final int DEFAULT_APP_VERSION = 1; + + private DiskLruCache mDiskLruCache; + + public CacheHelper(Context context) { + mDiskLruCache = generateCache(context, DIR_NAME, MAX_COUNT); + } + + public CacheHelper(Context context, String dirName) { + mDiskLruCache = generateCache(context, dirName, MAX_COUNT); + } + + public CacheHelper(Context context, String dirName, int maxCount) { + mDiskLruCache = generateCache(context, dirName, maxCount); + } + + //custom cache dir + public CacheHelper(File dir) { + mDiskLruCache = generateCache(null, dir, MAX_COUNT); + } + + public CacheHelper(Context context, File dir) { + mDiskLruCache = generateCache(context, dir, MAX_COUNT); + } + + public CacheHelper(Context context, File dir, int maxCount) { + mDiskLruCache = generateCache(context, dir, maxCount); + } + + private DiskLruCache generateCache(Context context, File dir, int maxCount) { + if (!dir.exists() || !dir.isDirectory()) { + throw new IllegalArgumentException( + dir + " is not a directory or does not exists. "); + } + + int appVersion = context == null ? DEFAULT_APP_VERSION : Utils.getAppVersion(context); + + DiskLruCache diskLruCache = null; + try { + diskLruCache = DiskLruCache.open( + dir, + appVersion, + DEFAULT_APP_VERSION, + maxCount); + } catch (IOException e) { + e.printStackTrace(); + } + return diskLruCache; + } + + private DiskLruCache generateCache(Context context, String dirName, int maxCount) { + DiskLruCache diskLruCache = null; + try { + diskLruCache = DiskLruCache.open( + getDiskCacheDir(context, dirName), + Utils.getAppVersion(context), + DEFAULT_APP_VERSION, + maxCount); + } catch (IOException e) { + e.printStackTrace(); + } + return diskLruCache; + } + + // ======================================= + // ============== String 数据 读写 ============= + // ======================================= + + public void put(String key, String value) { + Log.e(TAG, "put: key = " + key + " value = " + value); + mMMKV.encode(key, System.currentTimeMillis()); + mMMKV.encode(key + "_mmkv", value); + + DiskLruCache.Editor edit = null; + BufferedWriter bw = null; + try { + edit = editor(key); + if (edit == null) { + return; + } + OutputStream os = edit.newOutputStream(0); + bw = new BufferedWriter(new OutputStreamWriter(os)); + bw.write(value); + edit.commit();//write CLEAN + } catch (IOException e) { + e.printStackTrace(); + Log.e(TAG, "put: " + e.getMessage()); + try { + //s + edit.abort();//write REMOVE + } catch (IOException e1) { + e1.printStackTrace(); + Log.e(TAG, "put: " + e1.getMessage()); + } + } finally { + try { + if (bw != null) { + bw.close(); + } + } catch (IOException e) { + e.printStackTrace(); + Log.e(TAG, "put: " + e.getMessage()); + } + } + } + + public String getAsString(String key) { + Log.e(TAG, "getAsString: " + key); + InputStream inputStream = null; + try { + //write READ + inputStream = get(key); + if (inputStream == null) { + return mMMKV.decodeString(key + "_mmkv", null); + } + StringBuilder sb = new StringBuilder(); + int len = 0; + byte[] buf = new byte[128]; + while ((len = inputStream.read(buf)) != -1) { + sb.append(new String(buf, 0, len)); + } + return sb.toString(); + } catch (IOException e) { + e.printStackTrace(); + Log.e(TAG, "getAsString: " + e.getMessage()); + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e1) { + e1.printStackTrace(); + Log.e(TAG, "getAsString: " + e1.getMessage()); + } + } + } + return null; + } + + + public void put(String key, JSONObject jsonObject) { + put(key, jsonObject.toString()); + } + + public JSONObject getAsJson(String key) { + String val = getAsString(key); + try { + if (val != null) { + return new JSONObject(val); + } + } catch (JSONException e) { + e.printStackTrace(); + } + return null; + } + + // ======================================= + // ============ JSONArray 数据 读写 ============= + // ======================================= + + public void put(String key, JSONArray jsonArray) { + put(key, jsonArray.toString()); + } + + public JSONArray getAsJSONArray(String key) { + String JSONString = getAsString(key); + try { + JSONArray obj = new JSONArray(JSONString); + return obj; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + // ======================================= + // ============== byte 数据 读写 ============= + // ======================================= + + /** + * 保存 byte数据 到 缓存中 + * + * @param key 保存的key + * @param value 保存的数据 + */ + public void put(String key, byte[] value) { + OutputStream out = null; + DiskLruCache.Editor editor = null; + try { + editor = editor(key); + if (editor == null) { + return; + } + out = editor.newOutputStream(0); + out.write(value); + out.flush(); + editor.commit();//write CLEAN + } catch (Exception e) { + e.printStackTrace(); + try { + editor.abort();//write REMOVE + } catch (IOException e1) { + e1.printStackTrace(); + } + + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + + public byte[] getAsBytes(String key) { + byte[] res = null; + InputStream is = get(key); + if (is == null) { + return null; + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + byte[] buf = new byte[256]; + int len = 0; + while ((len = is.read(buf)) != -1) { + baos.write(buf, 0, len); + } + res = baos.toByteArray(); + } catch (IOException e) { + e.printStackTrace(); + } + return res; + } + + + // ======================================= + // ============== 序列化 数据 读写 ============= + // ======================================= + public void put(String key, Serializable value) { + DiskLruCache.Editor editor = editor(key); + ObjectOutputStream oos = null; + if (editor == null) { + return; + } + try { + OutputStream os = editor.newOutputStream(0); + oos = new ObjectOutputStream(os); + oos.writeObject(value); + oos.flush(); + editor.commit(); + } catch (IOException e) { + e.printStackTrace(); + try { + editor.abort(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } finally { + try { + if (oos != null) { + oos.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public T getAsSerializable(String key) { + T t = null; + InputStream is = get(key); + ObjectInputStream ois = null; + if (is == null) { + return null; + } + try { + ois = new ObjectInputStream(is); + t = (T) ois.readObject(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (ois != null) { + ois.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return t; + } + + // ======================================= + // ============== bitmap 数据 读写 ============= + // ======================================= + public void put(String key, Bitmap bitmap) { + put(key, Utils.bitmap2Bytes(bitmap)); + } + + public Bitmap getAsBitmap(String key) { + byte[] bytes = getAsBytes(key); + if (bytes == null) { + return null; + } + return Utils.bytes2Bitmap(bytes); + } + + // ======================================= + // ============= drawable 数据 读写 ============= + // ======================================= + public void put(String key, Drawable value) { + put(key, Utils.drawable2Bitmap(value)); + } + + public Drawable getAsDrawable(String key) { + byte[] bytes = getAsBytes(key); + if (bytes == null) { + return null; + } + return Utils.bitmap2Drawable(Utils.bytes2Bitmap(bytes)); + } + + // ======================================= + // ============= other methods ============= + // ======================================= + public boolean remove(String key) { + try { + key = Utils.hashKeyForDisk(key); + return mDiskLruCache.remove(key); + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + + public void close() throws IOException { + mDiskLruCache.close(); + } + + public void delete() throws IOException { + mDiskLruCache.delete(); + mMMKV.clearAll(); + } + + public void flush() throws IOException { + mDiskLruCache.flush(); + } + + public boolean isClosed() { + return mDiskLruCache.isClosed(); + } + + public long size() { + return mDiskLruCache.size(); + } + + public void setMaxSize(long maxSize) { + mDiskLruCache.setMaxSize(maxSize); + } + + public File getDirectory() { + return mDiskLruCache.getDirectory(); + } + + public long getMaxSize() { + return mDiskLruCache.getMaxSize(); + } + + + // ======================================= + // ===遇到文件比较大的,可以直接通过流读写 ===== + // ======================================= + //basic editor + public DiskLruCache.Editor editor(String key) { + try { + key = Utils.hashKeyForDisk(key); + //wirte DIRTY + DiskLruCache.Editor edit = mDiskLruCache.edit(key); + //edit maybe null :the entry is editing + if (edit == null) { + Log.w(TAG, "the entry spcified key:" + key + " is editing by other . "); + } + return edit; + } catch (IOException e) { + e.printStackTrace(); + Log.e(TAG, "editor: " + e.getMessage()); + } + + return null; + } + + + //basic get + public InputStream get(String key) { + try { + DiskLruCache.Snapshot snapshot = mDiskLruCache.get(Utils.hashKeyForDisk(key)); + if (snapshot == null) //not find entry , or entry.readable = false + { + Log.e(TAG, "not find entry , or entry.readable = false"); + return null; + } + //write READ + return snapshot.getInputStream(0); + + } catch (IOException e) { + e.printStackTrace(); + return null; + } + + } + + + // ======================================= + // ============== 序列化 数据 读写 ============= + // ======================================= + + private File getDiskCacheDir(Context context, String uniqueName) { + String cachePath; + if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) + || !Environment.isExternalStorageRemovable()) { + if (context.getExternalCacheDir() != null) { + cachePath = context.getExternalCacheDir().getPath(); + } else if (context.getExternalFilesDir("cache") != null) { + cachePath = context.getExternalFilesDir("cache").getPath(); + } else { + cachePath = context.getCacheDir().getPath(); + } + } else { + cachePath = context.getCacheDir().getPath(); + } + return new File(cachePath + File.separator + uniqueName); + } + +} diff --git a/app/src/main/java/com/uiui/appstore/disklrucache/Utils.java b/app/src/main/java/com/uiui/appstore/disklrucache/Utils.java new file mode 100644 index 0000000..da0171b --- /dev/null +++ b/app/src/main/java/com/uiui/appstore/disklrucache/Utils.java @@ -0,0 +1,101 @@ +package com.uiui.appstore.disklrucache; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.PixelFormat; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; + +import java.io.ByteArrayOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class Utils { + public static int getAppVersion(Context context) { + try { + PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); + return info.versionCode; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return 1; + } + + + public static String hashKeyForDisk(String key) { + String cacheKey; + try { + final MessageDigest mDigest = MessageDigest.getInstance("MD5"); + mDigest.update(key.getBytes()); + cacheKey = bytesToHexString(mDigest.digest()); + } catch (NoSuchAlgorithmException e) { + cacheKey = String.valueOf(key.hashCode()); + } + return cacheKey; + } + + public static String bytesToHexString(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + String hex = Integer.toHexString(0xFF & bytes[i]); + if (hex.length() == 1) { + sb.append('0'); + } + sb.append(hex); + } + return sb.toString(); + } + + public static byte[] bitmap2Bytes(Bitmap bm) { + if (bm == null) { + return null; + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bm.compress(Bitmap.CompressFormat.PNG, 100, baos); + return baos.toByteArray(); + } + + public static Bitmap bytes2Bitmap(byte[] bytes) { + return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); + } + + + /** + * Drawable → Bitmap + */ + public static Bitmap drawable2Bitmap(Drawable drawable) { + if (drawable == null) { + return null; + } + // 取 drawable 的长宽 + int w = drawable.getIntrinsicWidth(); + int h = drawable.getIntrinsicHeight(); + // 取 drawable 的颜色格式 + Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; + // 建立对应 bitmap + Bitmap bitmap = Bitmap.createBitmap(w, h, config); + // 建立对应 bitmap 的画布 + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, w, h); + // 把 drawable 内容画到画布中 + drawable.draw(canvas); + return bitmap; + } + + /* + * Bitmap → Drawable + */ + @SuppressWarnings("deprecation") + public static Drawable bitmap2Drawable(Bitmap bm) { + if (bm == null) { + return null; + } + BitmapDrawable bd = new BitmapDrawable(bm); + bd.setTargetDensity(bm.getDensity()); + return new BitmapDrawable(bm); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/uiui/appstore/gson/GsonUtils.java b/app/src/main/java/com/uiui/appstore/gson/GsonUtils.java new file mode 100644 index 0000000..f4fc637 --- /dev/null +++ b/app/src/main/java/com/uiui/appstore/gson/GsonUtils.java @@ -0,0 +1,144 @@ +package com.uiui.appstore.gson; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; + +import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +public class GsonUtils { + //https://blog.csdn.net/zte1055889498/article/details/122400299 + + public static JsonObject getJsonObject(String jsonString) { + JsonObject jsonObject = JsonParser.parseString(jsonString).getAsJsonObject(); + return jsonObject; + } + + private static final Gson gson; + + static { + GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapterFactory(new NullStringToEmptyAdapterFactory()); + builder.registerTypeAdapter(Integer.class, new IntegerDefault0Adapter()); + builder.registerTypeAdapter(int.class, new IntegerDefault0Adapter()); + builder.disableHtmlEscaping(); + builder.enableComplexMapKeySerialization(); + // builder.excludeFieldsWithoutExposeAnnotation(); + builder.setDateFormat("yyyy-MM-dd HH:mm:ss"); + gson = builder.create(); + } + + public static Type makeJavaType(Type rawType, Type... typeArguments) { + return TypeToken.getParameterized(rawType, typeArguments).getType(); + } + + public static String toString(Object value) { + if (Objects.isNull(value)) { + return null; + } + if (value instanceof String) { + return (String) value; + } + return toJSONString(value); + } + + public static String toJSONString(Object value) { + return gson.toJson(value); + } + + public static String toPrettyString(Object value) { + return gson.newBuilder().setPrettyPrinting().create().toJson(value); + } + + public static JsonElement fromJavaObject(Object value) { + JsonElement result = null; + if (Objects.nonNull(value) && (value instanceof String)) { + result = parseObject((String) value); + } else { + result = gson.toJsonTree(value); + } + return result; + } + + public static JsonElement parseObject(String content) { + return JsonParser.parseString(content); + } + + public static JsonElement getJsonElement(JsonObject node, String name) { + return node.get(name); + } + + public static JsonElement getJsonElement(JsonArray node, int index) { + return node.get(index); + } + + public static T toJavaObject(JsonElement node, Class clazz) { + return gson.fromJson(node, clazz); + } + + public static T toJavaObject(JsonElement node, Type type) { + return gson.fromJson(node, type); + } + + public static T toJavaObject(JsonElement node, TypeToken typeToken) { + return toJavaObject(node, typeToken.getType()); + } + + public static List toJavaList(JsonElement node, Class clazz) { + return toJavaObject(node, makeJavaType(List.class, clazz)); + } + + public static List toJavaList(JsonElement node) { + return toJavaObject(node, new TypeToken>() { + }.getType()); + } + + public static Map toJavaMap(JsonElement node, Class clazz) { + return toJavaObject(node, makeJavaType(Map.class, String.class, clazz)); + } + + public static Map toJavaMap(JsonElement node) { + return toJavaObject(node, new TypeToken>() { + }.getType()); + } + + public static T toJavaObject(String content, Class clazz) { + JsonObject jsonObject = getJsonObject(content); + String jsonString = jsonObject.toString(); + return gson.fromJson(jsonString, clazz); + } + + public static T toJavaObject(String content, Type type) { + return gson.fromJson(content, type); + } + + public static T toJavaObject(String content, TypeToken typeToken) { + return toJavaObject(content, typeToken.getType()); + } + + public static List toJavaList(String content, Class clazz) { + return toJavaObject(content, makeJavaType(List.class, clazz)); + } + + public static List toJavaList(String content) { + return toJavaObject(content, new TypeToken>() { + }.getType()); + } + + public static Map toJavaMap(String content, Class clazz) { + return toJavaObject(content, makeJavaType(Map.class, String.class, clazz)); + } + + public static Map toJavaMap(String content) { + return toJavaObject(content, new TypeToken>() { + }.getType()); + } +} diff --git a/app/src/main/java/com/uiui/appstore/gson/IntegerDefault0Adapter.java b/app/src/main/java/com/uiui/appstore/gson/IntegerDefault0Adapter.java new file mode 100644 index 0000000..91eb4dc --- /dev/null +++ b/app/src/main/java/com/uiui/appstore/gson/IntegerDefault0Adapter.java @@ -0,0 +1,35 @@ +package com.uiui.appstore.gson; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.JsonSyntaxException; + +import java.lang.reflect.Type; + +public class IntegerDefault0Adapter implements JsonSerializer, JsonDeserializer { + @Override + public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + try { + if (json.getAsString().equals("")) { + return 0; + } + } catch (Exception ignore) { + } + try { + return json.getAsInt(); + } catch (NumberFormatException e) { + throw new JsonSyntaxException(e); + } + } + + @Override + public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/uiui/appstore/gson/NullStringToEmptyAdapterFactory.java b/app/src/main/java/com/uiui/appstore/gson/NullStringToEmptyAdapterFactory.java new file mode 100644 index 0000000..0fdfae8 --- /dev/null +++ b/app/src/main/java/com/uiui/appstore/gson/NullStringToEmptyAdapterFactory.java @@ -0,0 +1,45 @@ +package com.uiui.appstore.gson; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; + +public class NullStringToEmptyAdapterFactory implements TypeAdapterFactory { + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + + Class rawType = (Class) type.getRawType(); + if (rawType != String.class) { + return null; + } + return (TypeAdapter) new StringAdapter(); + } + + public static class StringAdapter extends TypeAdapter { + @Override + public String read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return ""; + } + return reader.nextString(); + } + + @Override + public void write(JsonWriter writer, String value) throws IOException { + if (value == null) { + writer.nullValue(); + return; + } + writer.value(value); + } + } + +} + diff --git a/app/src/main/java/com/uiui/appstore/manager/ConnectManager.java b/app/src/main/java/com/uiui/appstore/manager/ConnectManager.java new file mode 100644 index 0000000..b59a29f --- /dev/null +++ b/app/src/main/java/com/uiui/appstore/manager/ConnectManager.java @@ -0,0 +1,134 @@ +package com.uiui.appstore.manager; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.util.Log; + +import com.tencent.mmkv.MMKV; +import com.uiui.appstore.utils.TimeUtils; + +public class ConnectManager { + private static final String TAG = ConnectManager.class.getSimpleName(); + + + public static final long ONE_MINUTES_TIME = 60 * 1000; + public static final long FIFTEEN_MINUTES_TIME = ONE_MINUTES_TIME * 15; + public static final long HALF_HOUR_TIME = FIFTEEN_MINUTES_TIME * 2; + public static final long ONE_HOUR_TIME = HALF_HOUR_TIME * 2; + public static final long SIX_HOUR_TIME = ONE_HOUR_TIME * 6; + public static final long HALF_DAY_TIME = SIX_HOUR_TIME * 2; + public static final long ONE_DAY_TIME = HALF_DAY_TIME * 2; + + /*重启后连接成功的时间*/ + public static final String REBOOT_LAST_ONNECT_TIME = "reboot_last_connect_time"; + /*WiFi连接后连接成功的时间*/ + public static final String WIFI_LAST_CONNECT_TIME = "WiFi_last_connect_time"; + /*打开设备信息连接成功的时间*/ + public static final String OPENINFO_LAST_ONNECT_TIME = "opneinfo_last_connect_time"; + + + @SuppressLint("StaticFieldLeak") + private static ConnectManager sInstance; + private Context mContext; + private MMKV mMMKV = MMKV.defaultMMKV(); + + private ConnectManager(Context context) { + if (context == null) { + throw new RuntimeException("Context is NULL"); + } + this.mContext = context; + + } + + public static void init(Context context) { + if (sInstance == null) { + Log.e(TAG, "init: "); + sInstance = new ConnectManager(context); + } + } + + public static ConnectManager getInstance() { + if (sInstance == null) { + throw new IllegalStateException("You must be init ConnectManager first"); + } + return sInstance; + } + + public long getConnectModeTime(ConnectMode connectMode) { + long time = 0; + switch (connectMode) { + case DEFAULT: + time = 0; + break; + case ONE_MINUTE: + time = ONE_MINUTES_TIME; + break; + case FIFTEEN_MINUTES: + time = FIFTEEN_MINUTES_TIME; + break; + case HALF_HOUR: + time = HALF_HOUR_TIME; + break; + case ONE_HOUR: + time = ONE_HOUR_TIME; + break; + case SIX_HOUR: + time = SIX_HOUR_TIME; + break; + case HALF_DAY: + time = HALF_DAY_TIME; + break; + case ONE_DAY: + time = ONE_DAY_TIME; + break; + default: + } + return time; + } + + public boolean isNeedConnect(String key, ConnectMode connectMode) { + long nowTime = System.currentTimeMillis(); + long lastTime = mMMKV.decodeLong(key, 0); + long intervalTime = getConnectModeTime(connectMode); + //防止一分钟内重复请求 + return nowTime - lastTime > intervalTime && nowTime - lastTime > ONE_MINUTES_TIME; + } + + + /** + * @return 重启后是否连接 + */ + public boolean isRebootFistConnect() { + long rebootTime = mMMKV.decodeLong(REBOOT_LAST_ONNECT_TIME, 0); + //只在开机后15内连接,其他情况为service重启 + long time = System.currentTimeMillis() - rebootTime; + return time < 15 * 1000; + } + + /** + * @return 今天WiFi连接是否有连接 + */ + public boolean isWiFiFistConnect() { + long time = mMMKV.decodeLong(WIFI_LAST_CONNECT_TIME, 0); + return !TimeUtils.isTodayTime(time); + } + + /** + * @return 今天打开设备信息后是否连接 + */ + public boolean isOpenInfoFistConnect() { + long time = mMMKV.decodeLong(OPENINFO_LAST_ONNECT_TIME, 0); + return !TimeUtils.isTodayTime(time); + } + + /** + * @param WiFiAlias + * @return 今天切换WiFi后是否连接 + */ + public boolean isWiFiCutoverFistConnect(String WiFiAlias) { + long time = mMMKV.decodeLong(WiFiAlias, 0); + return !TimeUtils.isTodayTime(time); + } + + +} diff --git a/app/src/main/java/com/uiui/appstore/manager/ConnectMode.java b/app/src/main/java/com/uiui/appstore/manager/ConnectMode.java new file mode 100644 index 0000000..937474b --- /dev/null +++ b/app/src/main/java/com/uiui/appstore/manager/ConnectMode.java @@ -0,0 +1,12 @@ +package com.uiui.appstore.manager; + +public enum ConnectMode { + DEFAULT, + ONE_MINUTE, + FIFTEEN_MINUTES, + HALF_HOUR, + ONE_HOUR, + SIX_HOUR, + HALF_DAY, + ONE_DAY, +} \ No newline at end of file diff --git a/app/src/main/java/com/uiui/appstore/network/NetInterfaceManager.java b/app/src/main/java/com/uiui/appstore/network/NetInterfaceManager.java index 3322265..a71cd78 100644 --- a/app/src/main/java/com/uiui/appstore/network/NetInterfaceManager.java +++ b/app/src/main/java/com/uiui/appstore/network/NetInterfaceManager.java @@ -1,53 +1,102 @@ package com.uiui.appstore.network; +import android.annotation.SuppressLint; import android.content.Context; import android.os.Environment; +import android.text.TextUtils; +import android.util.Log; +import com.tencent.android.tpush.XGIOperateCallback; +import com.tencent.android.tpush.XGPushManager; +import com.tencent.mmkv.MMKV; +import com.trello.rxlifecycle4.RxLifecycle; +import com.trello.rxlifecycle4.android.ActivityEvent; import com.uiui.appstore.bean.AppInfo; import com.uiui.appstore.bean.Banner; import com.uiui.appstore.bean.BaseResponse; +import com.uiui.appstore.bean.BatchID; +import com.uiui.appstore.disklrucache.CacheHelper; +import com.uiui.appstore.gson.GsonUtils; +import com.uiui.appstore.manager.ConnectManager; +import com.uiui.appstore.manager.ConnectMode; import com.uiui.appstore.network.api.AdminApp; import com.uiui.appstore.network.api.BannerImage; import com.uiui.appstore.network.api.DownloadApp; import com.uiui.appstore.network.api.GetBanner; +import com.uiui.appstore.network.api.GetBatchApi; import com.uiui.appstore.network.api.NewestAppUpdate; import com.uiui.appstore.network.api.QueryAllApp; +import com.uiui.appstore.network.api.QueryPackageApp; import com.uiui.appstore.network.api.SNInfo; import com.uiui.appstore.network.api.SearchApp; -import com.uiui.appstore.network.api.QueryPackageApp; +import com.uiui.appstore.network.interceptor.RepeatRequestInterceptor; +import com.uiui.appstore.utils.JGYUtils; import com.uiui.appstore.utils.Utils; import java.io.File; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.TimeUnit; -import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subjects.BehaviorSubject; import okhttp3.Cache; import okhttp3.OkHttpClient; -import retrofit2.CallAdapter; -import retrofit2.Converter; import retrofit2.Retrofit; import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; public class NetInterfaceManager { + @SuppressLint("StaticFieldLeak") private static NetInterfaceManager sInstance; private Context mContext; - private static Retrofit mRetrofit; + private Retrofit mRetrofit; + private MMKV mMMKV = MMKV.defaultMMKV(); + private OkHttpClient okHttpClient; + private CacheHelper cacheHelper; - public static final String ROOT_URL = UrlAddress.ROOT_URL; + //超时时间 + private static final int timeOut = 5; + // 缓存文件最大限制大小20M + private static final long cacheSize = 1024 * 1024 * 64; + public static final String CUSTOM_REPEAT_REQ_PROTOCOL = "MY_CUSTOM_REPEAT_REQ_PROTOCOL"; public static final String HTTP_KEY = "YTM3YTAxNTJmMmZmNzkyM2E2YzIwZjlhZTc0NzNmMGI="; private NetInterfaceManager(Context context) { + if (context == null) { + throw new RuntimeException("Context is NULL"); + } this.mContext = context; + this.cacheHelper = new CacheHelper(mContext); + if (null == mRetrofit) { + if (okHttpClient == null) { + //如果无法生存缓存文件目录,检测权限使用已经加上,检测手机是否把文件读写权限禁止了 + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + builder.connectTimeout(timeOut, TimeUnit.SECONDS); // 设置连接超时时间 + builder.writeTimeout(timeOut, TimeUnit.SECONDS);// 设置写入超时时间 + builder.readTimeout(timeOut, TimeUnit.SECONDS);// 设置读取数据超时时间 + builder.retryOnConnectionFailure(true);// 设置进行连接失败重试 + builder.addInterceptor(new RepeatRequestInterceptor()); + + // 设置缓存文件路径 + String cacheDirectory = getCacheDir() + "/OkHttpCache"; + Cache cache = new Cache(new File(cacheDirectory), cacheSize); + builder.cache(cache);// 设置缓存 + okHttpClient = builder.build(); + } + mRetrofit = new Retrofit.Builder() - .client(OK_HTTP_CLIENT) - .baseUrl(ROOT_URL) - .addConverterFactory(gsonConverterFactory) - .addCallAdapterFactory(rxJavaCallAdapterFactory) + .client(okHttpClient) + .baseUrl(UrlAddress.ROOT_URL) + .addConverterFactory(GsonConverterFactory.create()) + .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) .build(); } } @@ -56,7 +105,23 @@ public class NetInterfaceManager { if (sInstance == null) { sInstance = new NetInterfaceManager(context); } + } + private String getCacheDir() { + String cachePath; + if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) + || !Environment.isExternalStorageRemovable()) { + if (mContext.getExternalCacheDir() != null) { + cachePath = mContext.getExternalCacheDir().getPath(); + } else if (mContext.getExternalFilesDir("cache") != null) { + cachePath = mContext.getExternalFilesDir("cache").getPath(); + } else { + cachePath = mContext.getCacheDir().getPath(); + } + } else { + cachePath = mContext.getCacheDir().getPath(); + } + return cachePath; } public static NetInterfaceManager getInstance() { @@ -66,25 +131,6 @@ public class NetInterfaceManager { return sInstance; } - private static final long cacheSize = 1024 * 1024 * 32;// 缓存文件最大限制大小20M - private static String cacheDirectory = Environment.getExternalStorageDirectory() + "/okttpcaches"; // 设置缓存文件路径 - private static Cache cache = new Cache(new File(cacheDirectory), cacheSize); // - private static final OkHttpClient OK_HTTP_CLIENT; - private static final int timeOut = 5; - - static { - //如果无法生存缓存文件目录,检测权限使用已经加上,检测手机是否把文件读写权限禁止了 - OkHttpClient.Builder builder = new OkHttpClient.Builder(); - builder.connectTimeout(timeOut, TimeUnit.SECONDS); // 设置连接超时时间 - builder.writeTimeout(timeOut, TimeUnit.SECONDS);// 设置写入超时时间 - builder.readTimeout(timeOut, TimeUnit.SECONDS);// 设置读取数据超时时间 - builder.retryOnConnectionFailure(true);// 设置进行连接失败重试 - builder.cache(cache);// 设置缓存 - OK_HTTP_CLIENT = builder.build(); - } - - private static CallAdapter.Factory rxJavaCallAdapterFactory = RxJava3CallAdapterFactory.create(); - private static Converter.Factory gsonConverterFactory = GsonConverterFactory.create(); public Observable> getBannerObservable() { return mRetrofit.create(GetBanner.class) @@ -125,12 +171,114 @@ public class NetInterfaceManager { return mRetrofit.create(QueryPackageApp.class); } - public Observable>> getAdminAppObservable() { + public Observable>> getAdminAppObservable() { return mRetrofit.create(AdminApp.class).getAdminApp() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); - } + public Observable> getBatchApiControl() { + return mRetrofit.create(GetBatchApi.class) + .getBatch(Utils.getSerial()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + + public interface onCompleteCallback { + void onComplete(); + } + + /** + * 设置推送标签 + * + * @param lifecycle + * @param callback + */ + public void setPushTags(boolean refresh, BehaviorSubject lifecycle, onCompleteCallback callback) { + ConnectMode connectMode = ConnectMode.SIX_HOUR; + if (refresh) { + connectMode = ConnectMode.ONE_HOUR; + } + if (ConnectManager.getInstance().isNeedConnect(UrlAddress.GET_BATCH, connectMode)) { + setPushTags(lifecycle, callback); + } else { + String jsonString = cacheHelper.getAsString(UrlAddress.GET_BATCH); + //为 "" 是已经请求成功的 + if (jsonString == null) { + setPushTags(lifecycle, callback); + } else { + Set tagSets = mMMKV.decodeStringSet(UrlAddress.GET_BATCH); + Log.e("setPushTags", "tagSets: " + tagSets); + clearAndAppendTags(tagSets); + callback.onComplete(); + } + } + } + + public void setPushTags(BehaviorSubject lifecycle, onCompleteCallback callback) { + Set set = new HashSet(); + JGYUtils.getInstance().getAppPlatform(platform -> { + if (platform == JGYUtils.MTKPlatform) { + set.add(JGYUtils.MTKTag); + } else if (platform == JGYUtils.ZhanruiPlatform) { + set.add(JGYUtils.ZhanruiTag); + } + }); + getBatchApiControl() + .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY)) + .subscribe(new Observer>() { + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.e("setPushTags", "onSubscribe: "); + } + + @Override + public void onNext(@NonNull BaseResponse response) { + Log.e("setPushTags", "onNext: " + response); + if (response.code == 200) { + BatchID batchID = response.data; + String batch = batchID.getBatch_id(); + Log.e("setPushTags", "onNext: " + batch); + if (!TextUtils.isEmpty(batch)) { + set.add(batch); + } else { + Log.e("setPushTags", "onNext: " + "batch empty"); + } + cacheHelper.put(UrlAddress.GET_BATCH, GsonUtils.toJSONString(response.data)); + } else { + cacheHelper.put(UrlAddress.GET_BATCH, ""); + } + mMMKV.encode(UrlAddress.GET_BATCH, set); + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("setPushTags", "onError: " + e.getMessage()); + onComplete(); + } + + @Override + public void onComplete() { + Log.e("setPushTags", "onComplete: "); + Log.e("setPushTags", "tagSets: " + set); + clearAndAppendTags(set); + callback.onComplete(); + } + }); + } + + private void clearAndAppendTags(Set tagSets) { + XGPushManager.clearAndAppendTags(mContext, "clearAndAppendTags :" + System.currentTimeMillis(), tagSets, new XGIOperateCallback() { + @Override + public void onSuccess(Object o, int i) { + Log.e("setPushTags", "onSuccess: " + o); + } + + @Override + public void onFail(Object o, int i, String s) { + Log.e("setPushTags", "onFail: " + o); + } + }); + } } diff --git a/app/src/main/java/com/uiui/appstore/network/api/GetBatchApi.java b/app/src/main/java/com/uiui/appstore/network/api/GetBatchApi.java new file mode 100644 index 0000000..c099831 --- /dev/null +++ b/app/src/main/java/com/uiui/appstore/network/api/GetBatchApi.java @@ -0,0 +1,17 @@ +package com.uiui.appstore.network.api; + +import com.uiui.appstore.bean.BaseResponse; +import com.uiui.appstore.bean.BatchID; +import com.uiui.appstore.network.UrlAddress; + +import io.reactivex.rxjava3.core.Observable; +import retrofit2.http.GET; +import retrofit2.http.Query; + +public interface GetBatchApi { + @GET(UrlAddress.GET_BATCH) + Observable> getBatch( + @Query("sn") String sn + ); + +} diff --git a/app/src/main/java/com/uiui/appstore/network/interceptor/RepeatRequestInterceptor.java b/app/src/main/java/com/uiui/appstore/network/interceptor/RepeatRequestInterceptor.java new file mode 100644 index 0000000..e67902d --- /dev/null +++ b/app/src/main/java/com/uiui/appstore/network/interceptor/RepeatRequestInterceptor.java @@ -0,0 +1,108 @@ +package com.uiui.appstore.network.interceptor; + +import android.util.Log; + + +import com.uiui.appstore.BuildConfig; +import com.uiui.appstore.utils.MD5Util; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.concurrent.ConcurrentHashMap; + +import okhttp3.Interceptor; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.Buffer; + +/** + * v1.0 2022-07-15 16:16:52 + */ +public class RepeatRequestInterceptor implements Interceptor { + private static final String TAG = RepeatRequestInterceptor.class.getSimpleName(); + + private final ConcurrentHashMap requestIdsMap = new ConcurrentHashMap<>(); + public static final String REPEAT_REQUEST_PROTOCOL = "OKHTTP_REPEAT_REQUEST_PROTOCOL"; + + @NotNull + @Override + public Response intercept(@NotNull Chain chain) throws IOException { + Request request = chain.request(); + Response response = chain.proceed(request); + ResponseBody responseBody = response.body(); + + //会消费请求,导致请求多次 + String content = responseBody.string(); +// Response copy = response.newBuilder().body(responseBody).build(); + ResponseBody copy = ResponseBody.create(responseBody.contentType(), content); + if (BuildConfig.DEBUG) { +// Log.e(TAG, "请求体返回:| Response: " + request.url().encodedPath() + "\t body: " + content); + } + //相同的请求 + String requestKey = MD5Util.getUpperMD5Str(request.method() + request.url().toString() + requestBodyToString(request.body())); + long time = System.currentTimeMillis();//请求时间 + try { + if (requestIdsMap.size() > 0 && requestIdsMap.containsKey(requestKey)) { + log("重复请求:", requestKey, request); + //下面这行写了不会抛出onerror +// chain.call().cancel(); + return new Response.Builder() + .protocol(Protocol.get(REPEAT_REQUEST_PROTOCOL)) + .request(request) //multi thread + .build(); + } + requestIdsMap.put(requestKey, time); + log("注册请求:", requestKey, request); +// RepeatRequestInterceptor.Builder builder = request.newBuilder(); +// builder.addHeader("header", jsonObject.toString()); + return response.newBuilder().body(copy).build(); + } catch (IOException e) { + Log.e(TAG, "intercept: " + e.getMessage()); + throw e; + } finally { + if (requestIdsMap.containsKey(requestKey) && requestIdsMap.containsValue(time)) {//请求任务完成删除map中的数据 + requestIdsMap.remove(requestKey); + log("移除请求:", requestKey, request); + } + } + } + + private void log(String action, String requestKey, Request request) { + if (BuildConfig.DEBUG) { + Log.e("REPEAT-REQUEST", action + requestKey + " Method @" + request.method() + " --- " + " URL = " + request.url().encodedPath() + "\t" + bodyToString(request)); + } else { + Log.e("REPEAT-REQUEST", action + requestKey + " Method @" + request.method()); + } + } + + private static String bodyToString(final Request request) { + try { + final Request copy = request.newBuilder().build(); + final Buffer buffer = new Buffer(); + copy.body().writeTo(buffer); + if (buffer.size() > 4096) { + return "-too long"; + } + return buffer.readUtf8(); + } catch (Exception e) { + return "-"; + } + } + + private static String requestBodyToString(RequestBody body) { + try { + final Buffer buffer = new Buffer(); + body.writeTo(buffer); + if (buffer.size() > 4096) { + return "-too long"; + } + return buffer.readUtf8(); + } catch (Exception e) { + return "-"; + } + } +} diff --git a/app/src/main/java/com/uiui/appstore/receiver/BootReceiver.java b/app/src/main/java/com/uiui/appstore/receiver/BootReceiver.java index 5df732e..c9906af 100644 --- a/app/src/main/java/com/uiui/appstore/receiver/BootReceiver.java +++ b/app/src/main/java/com/uiui/appstore/receiver/BootReceiver.java @@ -6,7 +6,7 @@ import android.content.Intent; import android.util.Log; import com.uiui.appstore.service.GuardService; -import com.uiui.appstore.service.InitJpushServer; +import com.uiui.appstore.service.main.MainService; import com.uiui.appstore.service.StepService; import com.uiui.appstore.utils.JGYUtils; import com.uiui.appstore.utils.LogUtils; @@ -23,7 +23,7 @@ public class BootReceiver extends BroadcastReceiver { break; case Intent.ACTION_BOOT_COMPLETED: case BOOT_COMPLETED: - context.startService(new Intent(context, InitJpushServer.class)); + context.startService(new Intent(context, MainService.class)); context.startService(new Intent(context, StepService.class)); context.startService(new Intent(context, GuardService.class)); LogUtils.e("StoreBootReceiver", "booting BootReceiver"); diff --git a/app/src/main/java/com/uiui/appstore/service/InitJpushServer.java b/app/src/main/java/com/uiui/appstore/service/InitJpushServer.java deleted file mode 100644 index 1fa0b6f..0000000 --- a/app/src/main/java/com/uiui/appstore/service/InitJpushServer.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.uiui.appstore.service; - -import android.app.Service; -import android.content.Intent; -import android.os.Environment; -import android.os.Handler; -import android.os.IBinder; -import android.util.Log; - -import com.uiui.appstore.KeepAliveConnection; -import com.uiui.appstore.network.HTTPInterface; -import com.uiui.appstore.utils.SPUtils; - -import java.io.File; -import java.util.concurrent.TimeUnit; - -import io.reactivex.rxjava3.core.Observable; -import io.reactivex.rxjava3.core.ObservableEmitter; -import io.reactivex.rxjava3.core.ObservableOnSubscribe; -import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposable; - -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"); - return new KeepAliveConnection.Stub() { - }; - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - start.onstar(System.currentTimeMillis()); - return START_STICKY; - } - - @Override - public void onCreate() { - super.onCreate(); - Observable.create(subscribe) - .throttleFirst(3, TimeUnit.HOURS) - .subscribe(timeObserver); - File file = new File(DOWNLOAD_PATH); - if (file.isFile() || !file.exists()) { - file.delete(); - file.mkdirs(); - } - } - - private interface Start { - void onstar(long time); - } - - private Start start; - - private final ObservableOnSubscribe subscribe = new ObservableOnSubscribe() { - @Override - public void subscribe(ObservableEmitter emitter) throws Exception { - start = new Start() { - @Override - public void onstar(long time) { - emitter.onNext(time); - } - }; - } - }; - - private Observer timeObserver = new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(Long aLong) { - Log.e("TimeObserver", "onNext: " + aLong); - int isLogined = (int) SPUtils.get(InitJpushServer.this, "isLogined", 2); - if (isLogined == 2) return; - HTTPInterface.checkUpdate(InitJpushServer.this); - HTTPInterface.checkUpdate(InitJpushServer.this, "com.uiui.sn"); -// HTTPInterface.checkUpdate(InitJpushServer.this, "com.uiui.os"); - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - - } - }; - - private final String DOWNLOAD_PATH = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath() + File.separator + "jgy"; -} diff --git a/app/src/main/java/com/uiui/appstore/service/main/MainSContact.java b/app/src/main/java/com/uiui/appstore/service/main/MainSContact.java new file mode 100644 index 0000000..5b70c78 --- /dev/null +++ b/app/src/main/java/com/uiui/appstore/service/main/MainSContact.java @@ -0,0 +1,15 @@ +package com.uiui.appstore.service.main; + +import com.uiui.appstore.base.BasePresenter; +import com.uiui.appstore.base.BaseView; + +public class MainSContact { + public interface MainView extends BaseView { + void setSnTag(); + } + + interface Presenter extends BasePresenter { + //获取设备信息 + void getSnTag(); + } +} diff --git a/app/src/main/java/com/uiui/appstore/service/main/MainSPresenter.java b/app/src/main/java/com/uiui/appstore/service/main/MainSPresenter.java new file mode 100644 index 0000000..6d2ce0e --- /dev/null +++ b/app/src/main/java/com/uiui/appstore/service/main/MainSPresenter.java @@ -0,0 +1,49 @@ +package com.uiui.appstore.service.main; + +import android.content.Context; + +import com.trello.rxlifecycle4.android.ActivityEvent; +import com.uiui.appstore.network.NetInterfaceManager; + +import io.reactivex.rxjava3.subjects.BehaviorSubject; + +public class MainSPresenter implements MainSContact.Presenter { + private String TAG = MainSPresenter.class.getSimpleName(); + + private MainSContact.MainView mView; + private Context mContext; + + public MainSPresenter(Context context) { + this.mContext = context; + } + + private BehaviorSubject lifecycle; + + public void setLifecycle(BehaviorSubject lifecycle) { + this.lifecycle = lifecycle; + } + + public BehaviorSubject getLifecycle() { + return lifecycle; + } + + @Override + public void attachView(MainSContact.MainView view) { + this.mView = view; + } + + @Override + public void detachView() { + this.mView = null; + } + + @Override + public void getSnTag() { + NetInterfaceManager.getInstance().setPushTags(true, getLifecycle(), new NetInterfaceManager.onCompleteCallback() { + @Override + public void onComplete() { + mView.setSnTag(); + } + }); + } +} diff --git a/app/src/main/java/com/uiui/appstore/service/main/MainService.java b/app/src/main/java/com/uiui/appstore/service/main/MainService.java new file mode 100644 index 0000000..8fef205 --- /dev/null +++ b/app/src/main/java/com/uiui/appstore/service/main/MainService.java @@ -0,0 +1,167 @@ +package com.uiui.appstore.service.main; + +import android.app.Service; +import android.content.Intent; +import android.os.Environment; +import android.os.IBinder; +import android.util.Log; + +import com.blankj.utilcode.util.NetworkUtils; +import com.trello.rxlifecycle4.LifecycleProvider; +import com.trello.rxlifecycle4.LifecycleTransformer; +import com.trello.rxlifecycle4.RxLifecycle; +import com.trello.rxlifecycle4.android.ActivityEvent; +import com.trello.rxlifecycle4.android.RxLifecycleAndroid; +import com.uiui.appstore.KeepAliveConnection; +import com.uiui.appstore.network.HTTPInterface; +import com.uiui.appstore.utils.SPUtils; +import com.uiui.appstore.utils.ToastUtil; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.ObservableEmitter; +import io.reactivex.rxjava3.core.ObservableOnSubscribe; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.subjects.BehaviorSubject; + +public class MainService extends Service implements MainSContact.MainView, NetworkUtils.OnNetworkStatusChangedListener, LifecycleProvider { + private String TAG = MainService.class.getSimpleName(); + + private final String DOWNLOAD_PATH = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath() + File.separator + "jgy"; + + public MainSPresenter mPresenter; + + private final BehaviorSubject lifecycleSubject = BehaviorSubject.create(); + + + @Override + public void onDisconnected() { + Log.e(TAG, "网络未连接"); + ToastUtil.show("网络未连接"); + } + + @Override + public void onConnected(NetworkUtils.NetworkType networkType) { + Log.e(TAG, "网络已连接"); + ToastUtil.show("网络已连接"); + mPresenter.getSnTag(); + } + + @NotNull + @Override + public Observable lifecycle() { + return lifecycleSubject.hide(); + } + + @NotNull + @Override + public LifecycleTransformer bindUntilEvent(@NotNull ActivityEvent event) { + return RxLifecycle.bindUntilEvent(lifecycleSubject, event); + } + + @NotNull + @Override + public LifecycleTransformer bindToLifecycle() { + return RxLifecycleAndroid.bindActivity(lifecycleSubject); + } + + public MainService() { + } + + @Override + public IBinder onBind(Intent intent) { + // TODO: Return the communication channel to the service. +// throw new UnsupportedOperationException("Not yet implemented"); + return new KeepAliveConnection.Stub() { + }; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + start.onstar(System.currentTimeMillis()); + return START_STICKY; + } + + @Override + public void onCreate() { + super.onCreate(); + lifecycleSubject.onNext(ActivityEvent.CREATE); + mPresenter = new MainSPresenter(this); + mPresenter.setLifecycle(lifecycleSubject); + mPresenter.attachView(this); + NetworkUtils.registerNetworkStatusChangedListener(this); + + Observable.create(subscribe) + .throttleFirst(3, TimeUnit.HOURS) + .subscribe(timeObserver); + File file = new File(DOWNLOAD_PATH); + if (file.isFile() || !file.exists()) { + file.delete(); + file.mkdirs(); + } + mPresenter.getSnTag(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + lifecycleSubject.onNext(ActivityEvent.DESTROY); + NetworkUtils.unregisterNetworkStatusChangedListener(this); + mPresenter.detachView(); + } + + private interface Start { + void onstar(long time); + } + + private Start start; + + private final ObservableOnSubscribe subscribe = new ObservableOnSubscribe() { + @Override + public void subscribe(ObservableEmitter emitter) throws Exception { + start = new Start() { + @Override + public void onstar(long time) { + emitter.onNext(time); + } + }; + } + }; + + private Observer timeObserver = new Observer() { + @Override + public void onSubscribe(Disposable d) { + Log.e("TimeObserver", "Disposable: "); + } + + @Override + public void onNext(Long aLong) { + Log.e("TimeObserver", "onNext: " + aLong); + int isLogined = (int) SPUtils.get(MainService.this, "isLogined", 2); + if (isLogined == 2) return; + HTTPInterface.checkUpdate(MainService.this); + HTTPInterface.checkUpdate(MainService.this, "com.uiui.sn"); +// HTTPInterface.checkUpdate(InitJpushServer.this, "com.uiui.os"); + } + + @Override + public void onError(Throwable e) { + Log.e("TimeObserver", "onError: " + e.getMessage()); + } + + @Override + public void onComplete() { + Log.e("TimeObserver", "onComplete: "); + } + }; + + @Override + public void setSnTag() { + + } +} diff --git a/app/src/main/java/com/uiui/appstore/tpush/MessageReceiver.java b/app/src/main/java/com/uiui/appstore/tpush/MessageReceiver.java index 0f16b91..56d1f2a 100644 --- a/app/src/main/java/com/uiui/appstore/tpush/MessageReceiver.java +++ b/app/src/main/java/com/uiui/appstore/tpush/MessageReceiver.java @@ -4,10 +4,15 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.os.Environment; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.download.DownloadEntity; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.tencent.android.tpush.NotificationAction; @@ -16,11 +21,16 @@ import com.tencent.android.tpush.XGPushClickedResult; import com.tencent.android.tpush.XGPushRegisterResult; import com.tencent.android.tpush.XGPushShowedResult; import com.tencent.android.tpush.XGPushTextMessage; +import com.uiui.appstore.gson.GsonUtils; import com.uiui.appstore.tpush.common.NotificationService; import com.uiui.appstore.tpush.po.XGNotification; +import com.uiui.appstore.utils.FileUtils; +import com.uiui.appstore.utils.ToastUtil; +import java.io.File; import java.text.SimpleDateFormat; import java.util.Calendar; +import java.util.List; public class MessageReceiver extends XGPushBaseReceiver { @@ -306,10 +316,33 @@ public class MessageReceiver extends XGPushBaseReceiver { switch (title) { case ACTION_FORCE_INSTALLAPK: + forceInstallApk(context, extras); break; default: } } + private void forceInstallApk(Context context, String jsonString) { + if (TextUtils.isEmpty(jsonString)) { + return; + } + JSONObject jsonObject = (JSONObject) JSON.parse(jsonString); + final String packages = jsonObject.getString("package"); + ToastUtil.debugShow("收到应用安装消息:包名" + packages); + String url = jsonObject.getString("url"); + if (TextUtils.isEmpty(url)) { + return; + } + if (Aria.download(this).taskExists(url)) { + List entity = Aria.download(this).getDownloadEntity(url); + for (DownloadEntity downloadEntity : entity) { + Aria.download(this).load(downloadEntity.getId()).cancel(true); + } + } + File file = new File(Environment.getExternalStoragePublicDirectory("Download") + "/Sninfo/apk"); + file.mkdirs(); + FileUtils.ariaDownload(context, url, jsonObject); + Aria.download(this).resumeAllTask(); + } } diff --git a/app/src/main/java/com/uiui/appstore/utils/MD5Util.java b/app/src/main/java/com/uiui/appstore/utils/MD5Util.java new file mode 100644 index 0000000..cb7c754 --- /dev/null +++ b/app/src/main/java/com/uiui/appstore/utils/MD5Util.java @@ -0,0 +1,112 @@ +package com.uiui.appstore.utils; + +import android.annotation.SuppressLint; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class MD5Util { + + public static String packetMD5(String str) { + MessageDigest messageDigest = null; + try { + messageDigest = MessageDigest.getInstance("MD5"); + + messageDigest.reset(); + + messageDigest.update(str.getBytes("UTF-8")); + } catch (NoSuchAlgorithmException e) { + System.out.println("NoSuchAlgorithmException caught!"); + System.exit(-1); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + byte[] byteArray = messageDigest.digest(); + + StringBuffer md5StrBuff = new StringBuffer(); + + for (int i = 0; i < byteArray.length; i++) { + if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) + md5StrBuff.append("0").append( + Integer.toHexString(0xFF & byteArray[i])); + else + md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); + } + + return md5StrBuff.toString(); + } + + @SuppressLint("DefaultLocale") + public static String getUpperMD5Str(String str) { + MessageDigest messageDigest = null; + + try { + messageDigest = MessageDigest.getInstance("MD5"); + + messageDigest.reset(); + + messageDigest.update(str.getBytes("UTF-8")); + } catch (NoSuchAlgorithmException e) { + System.out.println("NoSuchAlgorithmException caught!"); + System.exit(-1); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + byte[] byteArray = messageDigest.digest(); + + StringBuffer md5StrBuff = new StringBuffer(); + + for (int i = 0; i < byteArray.length; i++) { + if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) + md5StrBuff.append("0").append( + Integer.toHexString(0xFF & byteArray[i])); + else + md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); + } + + return md5StrBuff.toString().toUpperCase(); + } + + + /** + * 获取16位的MD5 值得 + * + * @param str + * @return + */ + @SuppressLint("DefaultLocale") + public static String getUpperMD5Str16(String str) { + MessageDigest messageDigest = null; + + try { + messageDigest = MessageDigest.getInstance("MD5"); + + messageDigest.reset(); + + messageDigest.update(str.getBytes("UTF-8")); + } catch (NoSuchAlgorithmException e) { + System.out.println("NoSuchAlgorithmException caught!"); + System.exit(-1); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + byte[] byteArray = messageDigest.digest(); + + StringBuffer md5StrBuff = new StringBuffer(); + + for (int i = 0; i < byteArray.length; i++) { + if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) + md5StrBuff.append("0").append( + Integer.toHexString(0xFF & byteArray[i])); + else + md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); + } + + return md5StrBuff.toString().toUpperCase().substring(8, 24); + } + +} diff --git a/app/src/main/java/com/uiui/appstore/utils/TimeUtils.java b/app/src/main/java/com/uiui/appstore/utils/TimeUtils.java index 29c0fde..c14bce2 100644 --- a/app/src/main/java/com/uiui/appstore/utils/TimeUtils.java +++ b/app/src/main/java/com/uiui/appstore/utils/TimeUtils.java @@ -1,6 +1,13 @@ package com.uiui.appstore.utils; +import android.os.Build; + +import androidx.annotation.RequiresApi; + import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.Date; public class TimeUtils { @@ -9,4 +16,14 @@ public class TimeUtils { Date date = new Date(millSec); return sdf.format(date); } + + @RequiresApi(api = Build.VERSION_CODES.O) + public static boolean isTodayTime(long timeStamp) { + String time = transferLongToDate(timeStamp); + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + LocalDateTime localTime = LocalDateTime.parse(time, dtf); + LocalDateTime startTime = LocalDate.now().atTime(0, 0, 0); + LocalDateTime endTime = LocalDate.now().atTime(23, 59, 59); + return localTime.isAfter(startTime) && localTime.isBefore(endTime); + } }