| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | <project version="4"> | ||||
| <component name="ASMIdeaPluginConfiguration"> | |||||
| <asm skipDebug="false" skipFrames="false" skipCode="false" expandFrames="false" /> | |||||
| <groovy codeStyle="LEGACY" /> | |||||
| </component> | |||||
| <component name="DesignSurface"> | <component name="DesignSurface"> | ||||
| <option name="filePathToZoomLevelMap"> | <option name="filePathToZoomLevelMap"> | ||||
| <map> | <map> |
| buildTypes { | buildTypes { | ||||
| release { | release { | ||||
| buildConfigField "int", "PORT", "10006" | |||||
| buildConfigField "String", "ENVIRONMENT", "\"production\"" | |||||
| // buildConfigField "int", "PORT", "10006" | |||||
| // buildConfigField "String", "ENVIRONMENT", "\"production\"" | |||||
| minifyEnabled false | minifyEnabled false | ||||
| shrinkResources false | shrinkResources false | ||||
| debuggable = true | |||||
| debuggable = false | |||||
| proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' | ||||
| signingConfig signingConfigs.relese | signingConfig signingConfigs.relese | ||||
| } | } | ||||
| debug { | debug { | ||||
| buildConfigField "int", "PORT", "10003" | |||||
| buildConfigField "String", "ENVIRONMENT", "\"development\"" | |||||
| // buildConfigField "int", "PORT", "10003" | |||||
| // buildConfigField "String", "ENVIRONMENT", "\"development\"" | |||||
| minifyEnabled false | minifyEnabled false | ||||
| zipAlignEnabled false | zipAlignEnabled false | ||||
| debuggable = true | debuggable = true |
| <application | <application | ||||
| android:name=".module.XKLApplication" | android:name=".module.XKLApplication" | ||||
| android:allowBackup="true" | android:allowBackup="true" | ||||
| android:icon="@mipmap/ic_launcher" | |||||
| android:icon="@mipmap/app_icon" | |||||
| android:label="@string/app_name" | android:label="@string/app_name" | ||||
| android:roundIcon="@mipmap/ic_launcher_round" | android:roundIcon="@mipmap/ic_launcher_round" | ||||
| android:supportsRtl="true" | android:supportsRtl="true" |
| // TODO: 2022/6/2 时间需改为day 开发期间用于调式,故意设此值 | // TODO: 2022/6/2 时间需改为day 开发期间用于调式,故意设此值 | ||||
| //间隔时间单位 | //间隔时间单位 | ||||
| private val reviewIntervalUnit = "hour" | |||||
| private val reviewIntervalUnit = "day" | |||||
| //间隔单位时间 | //间隔单位时间 | ||||
| private val reviewTime = IntArray(7) { | private val reviewTime = IntArray(7) { |
| SQLiteDatabase.OPEN_READONLY) | SQLiteDatabase.OPEN_READONLY) | ||||
| } | } | ||||
| if (mDataBase == null) { | if (mDataBase == null) { | ||||
| AppExecutors.mainThread.run { | |||||
| AppExecutors.mainThread.execute { | |||||
| Toast.makeText(AppGlobals.application, "课程数据获取失败,请重启应用或联系业务员", Toast.LENGTH_LONG) | Toast.makeText(AppGlobals.application, "课程数据获取失败,请重启应用或联系业务员", Toast.LENGTH_LONG) | ||||
| } | } | ||||
| } | } | ||||
| mutableList.add(lesson) | mutableList.add(lesson) | ||||
| positionIndex += 1 | positionIndex += 1 | ||||
| // TODO: 2022/5/9 对课时数量进行限制,课时太多,开发人员不好进行测试 | |||||
| val needBreak = when (base.coursePackType) { | |||||
| AppConstants.COURSEPACK_TYPE_CHINESE_COMPOSITION -> positionIndex == 10 //保留十个课时 | |||||
| AppConstants.COURSEPACK_TYPE_ENGLISH_SPOKEN -> positionIndex == 6 //保留六个课时 | |||||
| else -> positionIndex == 6 //保留六个课时 | |||||
| } | |||||
| if (needBreak) { | |||||
| break | |||||
| } | |||||
| // 对课时数量进行限制,课时太多,开发人员不好进行测试 | |||||
| // val needBreak = when (base.coursePackType) { | |||||
| // AppConstants.COURSEPACK_TYPE_CHINESE_COMPOSITION -> positionIndex == 10 //保留十个课时 | |||||
| // AppConstants.COURSEPACK_TYPE_ENGLISH_SPOKEN -> positionIndex == 6 //保留六个课时 | |||||
| // else -> positionIndex == 6 //保留六个课时 | |||||
| // } | |||||
| // if (needBreak) { | |||||
| // break | |||||
| // } | |||||
| } | } | ||||
| it.close() | it.close() | ||||
| } | } |
| mDataBase = SQLiteDatabase.openDatabase(FilePathManager.getDictionaryDbPath().path, pwd, null, SQLiteDatabase.OPEN_READONLY) | mDataBase = SQLiteDatabase.openDatabase(FilePathManager.getDictionaryDbPath().path, pwd, null, SQLiteDatabase.OPEN_READONLY) | ||||
| } | } | ||||
| if (mDataBase == null) { | if (mDataBase == null) { | ||||
| AppExecutors.mainThread.run { | |||||
| AppExecutors.mainThread.execute { | |||||
| Toast.makeText(AppGlobals.application, "词典数据获取失败,请重启应用或联系业务员", Toast.LENGTH_LONG) | Toast.makeText(AppGlobals.application, "词典数据获取失败,请重启应用或联系业务员", Toast.LENGTH_LONG) | ||||
| } | } | ||||
| } | } |
| SQLiteDatabase.OPEN_READONLY) | SQLiteDatabase.OPEN_READONLY) | ||||
| } | } | ||||
| if (mDataBase == null) { | if (mDataBase == null) { | ||||
| AppExecutors.mainThread.run { | |||||
| AppExecutors.mainThread.execute { | |||||
| Toast.makeText(AppGlobals.application, "词汇量测试数据获取失败,请重启应用或联系业务员", Toast.LENGTH_LONG) | Toast.makeText(AppGlobals.application, "词汇量测试数据获取失败,请重启应用或联系业务员", Toast.LENGTH_LONG) | ||||
| } | } | ||||
| } | } |
| } | } | ||||
| // TODO: 2022/5/17 如果不需要小游戏,则这个可以取消,存放的value为time,暂时感觉没必要,所以直接用空内容数据 | // TODO: 2022/5/17 如果不需要小游戏,则这个可以取消,存放的value为time,暂时感觉没必要,所以直接用空内容数据 | ||||
| //添加到错误本中,现在主要用于小游戏取值 | //添加到错误本中,现在主要用于小游戏取值 | ||||
| learnEventData.newErrorMap?.forEach { | |||||
| vm.courseDetail.temporary_words[it.key] = "" | |||||
| } | |||||
| // learnEventData.newErrorMap?.forEach { | |||||
| // vm.courseDetail.temporary_words[it.key] = "" | |||||
| // } | |||||
| } | } | ||||
| AppConstants.LESSON_TYPE_SENTENCE -> { | AppConstants.LESSON_TYPE_SENTENCE -> { | ||||
| } | } | ||||
| binding.tvNickname.click { | binding.tvNickname.click { | ||||
| XPopup.Builder(context) | XPopup.Builder(context) | ||||
| .hasStatusBarShadow(false) | |||||
| .isDestroyOnDismiss(true) | |||||
| .autoOpenSoftInput(true) | |||||
| .isDarkTheme(false) | |||||
| .isViewMode(true) | |||||
| // .hasStatusBarShadow(false) | |||||
| // .isDestroyOnDismiss(true) | |||||
| // .autoOpenSoftInput(true) | |||||
| // .isDarkTheme(false) | |||||
| // .isViewMode(true) | |||||
| .asInputConfirm("修改昵称", null, binding.tvNickname.text.toString(), "限制为1-12个字符") { | .asInputConfirm("修改昵称", null, binding.tvNickname.text.toString(), "限制为1-12个字符") { | ||||
| //修改确认 | //修改确认 | ||||
| if (it.isNotEmpty() && it.length <= 12) { | if (it.isNotEmpty() && it.length <= 12) { |
| import com.xkl.cdl.module.m_statics.StaticsFragment | import com.xkl.cdl.module.m_statics.StaticsFragment | ||||
| import com.xkl.cdl.module.splash.SplashActivity | import com.xkl.cdl.module.splash.SplashActivity | ||||
| import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX | import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX | ||||
| import com.zackratos.ultimatebarx.ultimatebarx.navigationBar | |||||
| import com.zackratos.ultimatebarx.ultimatebarx.statusBar | |||||
| import com.zackratos.ultimatebarx.ultimatebarx.statusBarOnly | import com.zackratos.ultimatebarx.ultimatebarx.statusBarOnly | ||||
| /** | /** | ||||
| fun setStatusBar(isMyFragment : Boolean){ | fun setStatusBar(isMyFragment : Boolean){ | ||||
| when{ | when{ | ||||
| isMyFragment -> statusBarOnly { | |||||
| fitWindow = false //布局是否侵入状态栏 | |||||
| color = Color.TRANSPARENT //透明 | |||||
| light = true | |||||
| isMyFragment -> { | |||||
| statusBar { | |||||
| fitWindow = false //布局是否侵入状态栏 | |||||
| color = Color.TRANSPARENT //透明 | |||||
| light = true | |||||
| } | |||||
| navigationBar { | |||||
| fitWindow = false //布局是否侵入状态栏 | |||||
| color = Color.TRANSPARENT //透明 | |||||
| light = true | |||||
| } | |||||
| } | } | ||||
| else -> initStatusBar() | else -> initStatusBar() | ||||
| } | } |
| downLoadSuccess = false | downLoadSuccess = false | ||||
| } | } | ||||
| } | } | ||||
| println("${coursePackBaseInfo.coursePackName} : 下载成功") | |||||
| println("${coursePackBaseInfo.coursePackName} : 下载功能结束") | |||||
| //下载成功,解压到对应的文件 | //下载成功,解压到对应的文件 | ||||
| saveFile?.let { file -> | saveFile?.let { file -> | ||||
| downLoadNameAndProgress.postValue("课程数据包解析中,请稍等...") | |||||
| coursePackBaseInfo.let { | coursePackBaseInfo.let { | ||||
| try { | try { | ||||
| unZipFile_1(file, it) | unZipFile_1(file, it) | ||||
| nextEntry = entries.nextElement() | nextEntry = entries.nextElement() | ||||
| zipInputStream = zipFile.getInputStream(nextEntry) | zipInputStream = zipFile.getInputStream(nextEntry) | ||||
| outPut = when { | outPut = when { | ||||
| nextEntry.name.endsWith(("cover.png")) -> { | |||||
| nextEntry.name.startsWith("cover.") -> { | |||||
| Array<BufferedOutputStream>(1) { | Array<BufferedOutputStream>(1) { | ||||
| val fileName = File(FilePathManager.getIconRootPath(), | val fileName = File(FilePathManager.getIconRootPath(), | ||||
| "${coursePack.subjectId}/${coursePack.coursePackId}/icon") | "${coursePack.subjectId}/${coursePack.coursePackId}/icon") | ||||
| while ((zipInputStream.read(buffer).also { count = it }) != -1) { | while ((zipInputStream.read(buffer).also { count = it }) != -1) { | ||||
| outPut.forEach { | outPut.forEach { | ||||
| it.write(buffer, 0, count) | it.write(buffer, 0, count) | ||||
| it.flush() | |||||
| } | } | ||||
| } | } | ||||
| zipInputStream?.close() | |||||
| outPut.forEach { | outPut.forEach { | ||||
| it.close() | it.close() | ||||
| } | } |
| companion object{ | companion object{ | ||||
| val apiService by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { | val apiService by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { | ||||
| HttpUtil.instance.getRetrofitService(ApiService::class.java) | |||||
| // HttpUtil.instance.getRetrofitService(ApiService::class.java) | |||||
| } | } | ||||
| } | } | ||||
| package com.suliang.common.util.net | package com.suliang.common.util.net | ||||
| import android.webkit.WebSettings | |||||
| import android.widget.Toast | import android.widget.Toast | ||||
| import com.suliang.common.base.LibApplication | |||||
| import com.suliang.common.util.AppGlobals | import com.suliang.common.util.AppGlobals | ||||
| import com.suliang.common.util.thread.AppExecutors | import com.suliang.common.util.thread.AppExecutors | ||||
| import com.suliang.common.util.thread.MainThreadExecutor | import com.suliang.common.util.thread.MainThreadExecutor | ||||
| .connectTimeout(time_out, TimeUnit.SECONDS) | .connectTimeout(time_out, TimeUnit.SECONDS) | ||||
| .readTimeout(time_out, TimeUnit.SECONDS) | .readTimeout(time_out, TimeUnit.SECONDS) | ||||
| .writeTimeout(time_out, TimeUnit.SECONDS) | .writeTimeout(time_out, TimeUnit.SECONDS) | ||||
| .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) | |||||
| .addInterceptor(RetryInterceptor(2)) //重试拦截器 | |||||
| .build() | |||||
| } | |||||
| private val retrofit : Retrofit by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { | |||||
| Retrofit.Builder().baseUrl("http://api.offline.xuekaole.com") | |||||
| .client(okHttpClient) | |||||
| // .addConverterFactory(GsonConverterFactory.create()) | |||||
| // .addConverterFactory(ScalarsConverterFactory.create()) | |||||
| // .addConverterFactory(ProtoConverterFactory.create()) | |||||
| .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) | |||||
| //下载大文件时不能添加日志拦截器,否则会将所有数据加入内存,造成OOM | |||||
| // .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) | |||||
| // .addInterceptor(RetryInterceptor(2)) //重试拦截器 | |||||
| .build() | .build() | ||||
| } | } | ||||
| // private val retrofit : Retrofit by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { | |||||
| // Retrofit.Builder().baseUrl("http://api.offline.xuekaole.com") | |||||
| // .client(okHttpClient) | |||||
| //// .addConverterFactory(GsonConverterFactory.create()) | |||||
| //// .addConverterFactory(ScalarsConverterFactory.create()) | |||||
| //// .addConverterFactory(ProtoConverterFactory.create()) | |||||
| // .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) | |||||
| // .build() | |||||
| // } | |||||
| } | } | ||||
| fun getOkhttpClient() : OkHttpClient { | fun getOkhttpClient() : OkHttpClient { | ||||
| * @param service | * @param service | ||||
| * @return | * @return | ||||
| */ | */ | ||||
| fun <T : IApiService> getRetrofitService (service : Class<T> ) : T { | |||||
| return retrofit.create(service) | |||||
| } | |||||
| // fun <T : IApiService> getRetrofitService (service : Class<T> ) : T { | |||||
| // return retrofit.create(service) | |||||
| // } | |||||
| // | |||||
| /** | /** | ||||
| return | return | ||||
| } | } | ||||
| //下载 | //下载 | ||||
| val newCall = okHttpClient.newCall(Request.Builder().url(urlPath).build()) | |||||
| newCall.execute().use { response -> | |||||
| //下载失败 | |||||
| if (!response.isSuccessful) { | |||||
| downLoadFileListener.downFileResult(null) | |||||
| return | |||||
| } | |||||
| //下载成功 | |||||
| var inputStream : InputStream? = null | |||||
| var outputStream : OutputStream? = null | |||||
| response.body?.let { | |||||
| try { | |||||
| val contentLength:Double = it.contentLength().toDouble() //总长度 | |||||
| downLoadFileListener.downFileSize(contentLength.toLong()) //回调总长度 | |||||
| inputStream = it.byteStream() | |||||
| outputStream = FileOutputStream(savePathFile) | |||||
| var readLength : Int = 0 | |||||
| var writeLength = 0f | |||||
| val bytes = ByteArray(1025 * 1025 * 5) | |||||
| var progress = 0 | |||||
| while ((inputStream!!.read(bytes)).also { | |||||
| readLength = it | |||||
| } != -1) { | |||||
| outputStream!!.write(bytes, 0, readLength) | |||||
| writeLength += readLength | |||||
| progress = (writeLength / contentLength * 100).toInt() | |||||
| // AppExecutors.mainThread.execute { | |||||
| // 进度回调给放到主线程中 | |||||
| downLoadFileListener.downFileProgress(progress) //下载百分比 | |||||
| // } | |||||
| } | |||||
| outputStream?.flush() | |||||
| outputStream?.close() | |||||
| downLoadFileListener.downFileResult(savePathFile) | |||||
| } catch (e : Exception) { | |||||
| e.printStackTrace() | |||||
| val newCall = okHttpClient.newCall(Request.Builder().url(urlPath).removeHeader("User-Agent") | |||||
| .addHeader("User-Agent", WebSettings.getDefaultUserAgent(LibApplication.instance())).build()) | |||||
| try { | |||||
| newCall.execute().let { response -> | |||||
| //下载失败 | |||||
| if (!response.isSuccessful) { | |||||
| downLoadFileListener.downFileResult(null) | downLoadFileListener.downFileResult(null) | ||||
| }finally { | |||||
| return | |||||
| } | |||||
| //下载成功 | |||||
| var inputStream : InputStream? = null | |||||
| var outputStream : OutputStream? = null | |||||
| var result = false //是否成功标志 | |||||
| response.body?.let { | |||||
| try { | try { | ||||
| val contentLength:Double = it.contentLength().toDouble() //总长度 | |||||
| downLoadFileListener.downFileSize(contentLength.toLong()) //回调总长度 | |||||
| inputStream = it.byteStream() | |||||
| outputStream = BufferedOutputStream(FileOutputStream(savePathFile)) | |||||
| var readLength : Int = 0 | |||||
| var writeLength = 0f | |||||
| val bytes = ByteArray(1025 * 1025 * 5) | |||||
| var progress = 0 | |||||
| while ((inputStream!!.read(bytes)).also { | |||||
| readLength = it | |||||
| } != -1) { | |||||
| outputStream!!.write(bytes, 0, readLength) | |||||
| writeLength += readLength | |||||
| progress = (writeLength / contentLength * 100).toInt() | |||||
| // AppExecutors.mainThread.execute { | |||||
| // 进度回调给放到主线程中 | |||||
| downLoadFileListener.downFileProgress(progress) //下载百分比 | |||||
| // } | |||||
| } | |||||
| outputStream?.flush() | |||||
| outputStream?.close() | outputStream?.close() | ||||
| inputStream?.close() | |||||
| }catch (e: Exception){ | |||||
| // downLoadFileListener.downFileResult(savePathFile) | |||||
| result = true | |||||
| } catch (e : Exception) { | |||||
| e.printStackTrace() | e.printStackTrace() | ||||
| // downLoadFileListener.downFileResult(null) | |||||
| result = false | |||||
| }finally { | |||||
| try { | |||||
| outputStream?.close() | |||||
| inputStream?.close() | |||||
| }catch (e: Exception){ | |||||
| e.printStackTrace() | |||||
| } | |||||
| } | } | ||||
| //回调中太多需要处理的数据,这里需要先释放,避免OOM | |||||
| if (result) downLoadFileListener.downFileResult(savePathFile) else downLoadFileListener.downFileResult(null) | |||||
| } | } | ||||
| } | } | ||||
| } catch (e : Exception) { | |||||
| e.printStackTrace() | |||||
| downLoadFileListener.downFileResult(null) | |||||
| } | } | ||||
| } | } | ||||