Commit a429bbd5 authored by 宋永孟's avatar 宋永孟

add first commit

parents
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.idea
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
\ No newline at end of file
/build
\ No newline at end of file
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'kotlin-android-extensions'
id 'kotlin-kapt'
}
android {
compileSdkVersion build_versions.compileSdkVersion
defaultConfig {
minSdkVersion build_versions.minSdkVersion
targetSdkVersion build_versions.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
ndk {
//选择要添加的对应 cpu 类型的 .so 库。
abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a'
// 还可以添加 'x86', 'x86_64', 'mips', 'mips64'
}
manifestPlaceholders = [
//TODO 改为生活圈id
JPUSH_PKGNAME : "com.yidian.shenghuoquan",
JPUSH_APPKEY : "1bb2eccc6922551c81f4d2f0", //JPush 上注册的包名对应的 Appkey.
JPUSH_CHANNEL : "developer-default", //暂时填写默认值即可.
]
}
buildTypes {
release {
buildConfigField "boolean", "ENV_DEBUG", "false"
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
buildConfigField "boolean", "ENV_DEBUG", "true"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
androidExtensions {
experimental = true
}
kapt{
generateStubs = true
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation rootProject.ext.dependencies.publicImplementation
testImplementation rootProject.ext.dependencies.testImplementation
androidTestImplementation rootProject.ext.dependencies.androidTestImplementation
api deps.yac.core
api deps.xarc.xbrid
api deps.xarc.xevent
kapt 'com.yidian.chameleon:annotationcompiler:0.3.29'
api 'com.yidian.chameleon:annotation:0.3.29'
api "com.yidian.xarc:xservice:0.0.1"
api 'com.tencent:mmkv-static:1.2.2'
//JPush begin
api 'cn.jiguang.sdk:jpush:4.0.6'
api 'cn.jiguang.sdk:jcore:2.7.6'
if (onlyNativeProject) {
api "com.yidian.xpage:xpagenative:0.0.1"
} else {
api project(':flutter')
api project(":xpage")
}
kapt "com.github.bumptech.glide:compiler:4.11.0"
api 'com.github.bumptech.glide:glide:4.11.0'
api 'com.github.bumptech.glide:okhttp3-integration:4.11.0'
// configurations.all {
// resolutionStrategy.force
// }
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.yidian.common">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application>
<!-- JPUSH推送 begin -->
<service android:name=".services.JPushService"
android:enabled="true"
android:exported="false"
android:process=":pushcore">
<intent-filter>
<action android:name="cn.jiguang.user.service.action" />
</intent-filter>
</service>
<receiver
android:name="com.yidian.common.receiver.JPushBroadcastReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="cn.jpush.android.intent.RECEIVE_MESSAGE" />
<category android:name="com.cutt.zhiyue.android" />
</intent-filter>
</receiver>
<!-- JPUSH推送 end -->
<provider
android:name="com.yidian.CuttFileProvider"
android:authorities="com.yidian.CuttFileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/cutt_file_paths" />
</provider>
</application>
</manifest>
\ No newline at end of file
package com.yidian;
/**
* description:
* author: hjl
* date 2019/5/29
*/
public class CuttFileProvider extends androidx.core.content.FileProvider {
}
package com.yidian.common
class AppConfig {
companion object{
const val ShareSDKAppKey = "m31e592edbfdf2"
const val ShareSDKAppSecret = "c86b14c6c6855651f63291d8ac827bac"
}
}
\ No newline at end of file
package com.yidian.common
class Constants {
companion object{
const val ITEM_TYPE_CHAMELEON = -1
const val CHAMELEON_ID = "chameleon_id"
const val CHAMELEON_D_TYPE = "dtype"
}
}
\ No newline at end of file
package com.yidian.common
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
@Parcelize
class ParcelizeMap(var map: Map<String, String?>?): Parcelable
\ No newline at end of file
package com.yidian.common
class XRouterPathConstants {
companion object{
const val USER_INFO = "/fragment/userInfo"
const val NEWS_MAIN = "/newscontent/MainActivity"
const val NEWS_CONTENT = "/newscontent/NewsContentActivity"
}
}
\ No newline at end of file
package com.yidian.common
import android.app.Application
import cn.jpush.android.api.JPushInterface
import com.yidian.common.http.ApiAddHeadersInterceptor
import com.yidian.common.http.BaseURLs
import com.yidian.common.http.HttpCommonParamsUtils
import com.yidian.common.utils.EncryptUtil
import com.yidian.common.utils.StringUtils
import com.yidian.common.utils.SystemUtil
import com.yidian.http.ServiceFactory
import com.yidian.yac.ftdevicefinger.core.FtDeviceFingerManager
open class YacBaseApplication : Application() {
companion object {
lateinit var context: Application
}
override fun onCreate() {
super.onCreate()
context= this
FtDeviceFingerManager.init(this)
//init http
ServiceFactory.getInstance().init(true, BaseURLs.BASE_URL, 10, 10)
ServiceFactory.getInstance().addInterceptor(ApiAddHeadersInterceptor())
//初始化通用网络参数
HttpCommonParamsUtils.deviceId = SystemUtil.calcDeviceId(context)
HttpCommonParamsUtils.imei = SystemUtil.getPhoneIMEI(context)
HttpCommonParamsUtils.mid = SystemUtil.getMacAddress()
HttpCommonParamsUtils.androidId = SystemUtil.getAndroidID(context)
HttpCommonParamsUtils.serialNumber = SystemUtil.getSerialNumber()
HttpCommonParamsUtils.version = StringUtils.formatVersion(SystemUtil.getVerName(context))
HttpCommonParamsUtils.deviceFinger = FtDeviceFingerManager.getDeviceFinger()?:""
HttpCommonParamsUtils.OS = android.os.Build.VERSION.RELEASE
HttpCommonParamsUtils.targetSdkVersion = this.applicationInfo.targetSdkVersion
HttpCommonParamsUtils.x_digest = EncryptUtil.xDigest(
HttpCommonParamsUtils.deviceId!!,
HttpCommonParamsUtils.packageName
)!!
// //极光推送
// JPushInterface.setDebugMode(true)
// JPushInterface.init(this)
}
}
\ No newline at end of file
package com.yidian.common.base
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.annotation.IdRes
import androidx.annotation.LayoutRes
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import com.yidian.common.R
/**
* 所有Activity的基类
*
*
* 完全自主布局时:
* 使用getContentViewId()返回你的布局layout文件id。
*
* 在BaseActivity支持统一的Toolbar+无数据页面显示时:
* 请使用getInnerViewId()返回你的内容布局(不需要toolbar)layout文件id;
* 请使用getToolbarTitle()设置你的标题内容。
*
* 当你需要自己实现toolbar布局时:
* 请使用getToolbarLayoutId()返回你的toolbar布局layout文件id;
* 请使用getToolbarId()返回你的Toolbar的id;
* 请使用getTitleId()返回你的TextView标题的id。
*/
abstract class BaseActivity : AppCompatActivity() {
private var titleTV: TextView? = null
private lateinit var mContentView: ViewGroup
/**
* 自主实现布局 setContentView
* 注意:getContentViewId()和getInnerViewId()方法需要二选一去重写!
*
* @return 布局layout文件id
*/
protected open val contentViewId: Int get() = -1
/**
* 使用支持toolbar和无数据页面展示
* 注意:getContentViewId()和getInnerViewId()方法需要二选一去重写!
*
* @return 嵌套布局layout文件id
*/
protected open val innerViewId: Int get() = -1
/**
* toolbar布局文件
* 不用默认toolbar时,此方法返回自己的toolbar布局
*/
protected open val toolbarLayoutId: Int @LayoutRes get() = R.layout.toolbar_layout
/**
* Toolbar的id
* 不用默认toolbar时,此方法返回自己的toolbar布局中的toolbarId
*/
protected open val toolbarId: Int @IdRes get() = R.id.toolbar
/**
* 标题TextView的id
* 不用默认toolbar时,此方法返回自己的toolbar布局中的标题TextView的id
*/
protected open val titleId: Int @IdRes get() = R.id.title_tv
/**
* toolbar标题
*
* @return 标题
*/
protected open val toolbarTitle: Int get() = -1
/**
* 根布局设置背景
*
* @return The identifier of the resource.
*/
protected open val backgroundResource: Int @DrawableRes get() = R.color.bgColor
protected var toolbar: Toolbar? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (contentViewId != -1) {//自主实现布局
setContentView(contentViewId)
} else if (innerViewId != -1) {//支持toolbar和无数据页面内嵌布局
//根布局是个LinearLayout
val rootView = LinearLayout(this)
rootView.setBackgroundResource(backgroundResource)
rootView.orientation = LinearLayout.VERTICAL
setContentView(rootView)//此方法默认是宽高MATCH_PARENT
//先添加toolbar
View.inflate(this, toolbarLayoutId, rootView)
//再添加FrameLayout作为主内容View
mContentView = FrameLayout(this)
View.inflate(this, innerViewId, mContentView)
//mContentFL需要宽高MATCH_PARENT,不设置默认是WRAP_CONTENT
rootView.addView(
mContentView,
LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
)
//初始化toolbar并设置标题
toolbar = findViewById(toolbarId)
setSupportActionBar(toolbar)
supportActionBar?.title = ""
supportActionBar?.setDisplayHomeAsUpEnabled(true)
titleTV = findViewById(titleId)
if (toolbarTitle != -1) {
titleTV?.setText(toolbarTitle)
} else {
titleTV?.text = ""
}
}
init(savedInstanceState)
}
protected open fun init(savedInstanceState: Bundle?) {}
/**
* 友盟统计,页面的名称
*/
protected open fun statisticName():String?{
return null
}
protected open fun setToolbarTitle(title: String) {
titleTV?.text = title
}
protected open fun onToolbarBack() {
finish()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (innerViewId != -1) {
when (item.itemId) {
android.R.id.home -> onToolbarBack()
}
}
return super.onOptionsItemSelected(item)
}
protected open fun exitAnimalEnable(): Boolean {
return true
}
override fun finish() {
super.finish()
if (exitAnimalEnable())
overridePendingTransition(R.anim.slide_left_in, R.anim.slide_right_out)
}
}
\ No newline at end of file
package com.yidian.common.base
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.fragment.app.Fragment
/**
* 所有Fragment的基类
*/
abstract class BaseFragment : Fragment() {
@get:LayoutRes
protected abstract val contentViewId: Int
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(contentViewId, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
init(savedInstanceState)
}
protected open fun init(savedInstanceState: Bundle?) {}
}
package com.yidian.common.base;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
public class BaseHeaderAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final int type_header = -1;
private RecyclerView.Adapter targetAdapter;
private View headerView;
private int headerViewRes;
public BaseHeaderAdapter(RecyclerView.Adapter targetAdapter,View headerView) {
this.targetAdapter = targetAdapter;
this.headerView = headerView;
}
public BaseHeaderAdapter(RecyclerView.Adapter targetAdapter,int headerViewRes) {
this.targetAdapter = targetAdapter;
this.headerViewRes = headerViewRes;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == type_header) {
if(headerView!=null){
return new ZeroViewHolder(headerView);
}else {
View view = LayoutInflater.from(parent.getContext()).inflate(headerViewRes, parent, false);
return new ZeroViewHolder(view);
}
}
return targetAdapter.onCreateViewHolder(parent,viewType);
}
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
switch (holder.getItemViewType()) {
case type_header:
break;
default:
targetAdapter.onBindViewHolder(holder,position-1);
break;
}
}
@Override
public int getItemViewType(int position) {
if (position == 0) {
return type_header;
} else {
return targetAdapter.getItemViewType(position-1);
}
}
@Override
public int getItemCount() {
return targetAdapter.getItemCount()+1;
}
static class ZeroViewHolder extends RecyclerView.ViewHolder {
public ZeroViewHolder(View itemView) {
super(itemView);
}
}
}
\ No newline at end of file
package com.yidian.common.chameleon
import org.json.JSONObject
data class ChameleonEvent (
var module:String?,
var action:String?,
var params:JSONObject?,
var options:JSONObject?
)
\ No newline at end of file
package com.yidian.common.chameleon
interface ChameleonEventService {
fun dispatchEvent(event:ChameleonEvent)
}
\ No newline at end of file
package com.yidian.common.chameleon
object ChameleonServiceManager {
private val services:MutableSet<ChameleonEventService> = mutableSetOf()
fun registerChameleonService(service:ChameleonEventService){
services.add(service)
}
fun unregisterChameleonService(service:ChameleonEventService){
services.remove(service)
}
fun unregisterAllChameleonService(){
services.clear()
}
fun dispatchEvent(event:ChameleonEvent){
services.forEach{
it.dispatchEvent(event)
}
}
}
\ No newline at end of file
package com.yidian.common.chameleon
import com.yidian.chameleon.js.BridgeService
import com.yidian.chameleon.js.BridgeService.IAppDispatcher
import io.reactivex.Observable
import org.json.JSONObject
class ChameleonServiceRouter: IAppDispatcher {
override fun connectEventReceiver(eventReceiver: IAppDispatcher.IServiceEventReceiver?): String {
return ""
}
override fun dispatchCall(
module: String?,
action: String?,
params: JSONObject?,
options: JSONObject?
): Observable<BridgeService.JsResult> {
ChameleonServiceManager.dispatchEvent(ChameleonEvent(module,action,params,options))
val jsResult = BridgeService.JsResult(params)
return Observable.just(jsResult)
}
override fun disconnectEventReceiver(evToken: String?) {}
}
\ No newline at end of file
package com.yidian.common.glide
import android.content.Context
import cc.shinichi.library.glide.progress.ProgressManager
import com.bumptech.glide.Glide
import com.bumptech.glide.Registry
import com.bumptech.glide.annotation.GlideModule
import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.module.AppGlideModule
import java.io.InputStream
/**
* @description: 图片浏览库相关配置 https://github.com/SherlockGougou/BigImageViewPager
* @author: huyajun
* @date: 2021/4/29
**/
@GlideModule
class BaseGlideModule : AppGlideModule() {
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
super.registerComponents(context, glide, registry)
// 替换底层网络框架为okhttp3,这步很重要!如果不添加会无法正常显示原图的加载百分比,或者卡在1%
// 如果您的app中已经存在了自定义的GlideModule,您只需要把这一行代码,添加到对应的重载方法中即可。
registry.replace(
GlideUrl::class.java, InputStream::class.java, OkHttpUrlLoader.Factory(
ProgressManager.getOkHttpClient()
)
)
}
}
\ No newline at end of file
package com.yidian.common.http
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
class ApiAddHeadersInterceptor: Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val builder: Request.Builder = chain.request().newBuilder()
HttpCommonParamsUtils.androidId?.let {
builder.addHeader("androidID", it)
}
builder.addHeader("app", HttpCommonParamsUtils.cycleId)
HttpCommonParamsUtils.version?.let {
builder.addHeader("appVersion", it)
}
HttpCommonParamsUtils.deviceId?.let {
builder.addHeader("device", it)
}
HttpCommonParamsUtils.deviceFinger?.let {
builder.addHeader("ftdf", it)
}
HttpCommonParamsUtils.mid?.let {
builder.addHeader("mid", it)
}
HttpCommonParamsUtils.imei?.let {
builder.addHeader("imei", it)
}
builder.addHeader("pkgName", HttpCommonParamsUtils.packageName)
builder.addHeader("portal", HttpCommonParamsUtils.portal)
HttpCommonParamsUtils.serialNumber?.let {
builder.addHeader("serialNumber", it)
}
HttpCommonParamsUtils.androidId?.let {
builder.addHeader("androidID", it)
}
builder.addHeader("ver", HttpCommonParamsUtils.currentVersion)
builder.addHeader("targetSdkVersion", HttpCommonParamsUtils.targetSdkVersion.toString())
HttpCommonParamsUtils.OS?.let {
builder.addHeader("cuttOs", it)
}
builder.addHeader("token", HttpCommonParamsUtils.token)
builder.addHeader("X-OC-SESSION", HttpCommonParamsUtils.token)
builder.addHeader("X-Oc-Merchant-Id", "1qaz2wsx")
HttpCommonParamsUtils.x_digest?.let {
builder.addHeader("x-digest", it)
}
HttpCommonParamsUtils.sign?.let {
builder.addHeader("Sign", it)
}
return chain.proceed(builder.build())
}
}
\ No newline at end of file
package com.yidian.common.http
import com.yidian.common.BuildConfig
class BaseURLs {
companion object{
val BASE_URL: String
private const val BASE_URL_DEBUG = "https://api.jwshq.cn"
private const val BASE_URL_PRO = "https://api.jwshq.cn"
init {
BASE_URL = if (BuildConfig.ENV_DEBUG){
BASE_URL_DEBUG
}else{
BASE_URL_PRO
}
}
}
}
\ No newline at end of file
package com.yidian.common.http
import com.yidian.common.YacBaseApplication
import com.yidian.common.utils.EncryptUtil.Companion.encryptToSHA
import com.yidian.common.utils.StringUtils
import com.yidian.xarc.xbase.helper.SPHelper
class HttpCommonParamsUtils{
companion object{
val spHelper = SPHelper(YacBaseApplication.context, "config")
//-----------header-------------
//圈id
var cycleId by spHelper.SharedPreference("cycleId", "1568648")
var token by spHelper.SharedPreference("token", "3rpPKwJlwiYaWPxusbeC0Ads5nTN7QMwOxIV6LLQkFg6CPtNdeQhVtK")
//设备id
var deviceId:String? = null
//写死的固定值,确认是否必要
const val portal = "1564343"
//当前版本,确认是否必要
var currentVersion = "210430"
var packageName = "com.cutt.zhiyue.android"
var imei:String? = null
//Md5过的Mac地址
var mid:String? = null
var androidId:String? = null
var serialNumber:String? = null
var version:String? = null
var deviceFinger:String? = null
var targetSdkVersion:Int=0
var OS:String? = null
var x_digest:String? = null
//本地生成很复杂,以后确认是否必要
var sign:String? = null
//---------------params-----------
var salt by spHelper.SharedPreference("salt", "0e2UegQY8uUPuslO")
var appKey by spHelper.SharedPreference("app_key", "token_8d171bf0ab33deea_jY5knW2dcpFD5mj1")
fun getParamsMap(): HashMap<String, String>{
val queryParamsMap = HashMap<String, String>()
if (StringUtils.isNotBlank(appKey) && StringUtils.isNotBlank(salt)){
val timeStamp = System.currentTimeMillis()
queryParamsMap["app_key"] = appKey
queryParamsMap["timestamp"] = timeStamp.toString()
queryParamsMap["sign"] = encryptToSHA("$appKey:$salt:$timeStamp")
}
return queryParamsMap
}
}
}
\ No newline at end of file
package com.yidian.common.http
/**
* create by Administrator
* date 2019/2/12 0012
* desc
*/
class HttpResult<T> {
var code = 0
var status: String? = null
var reason: String? = null
var result: T? = null
}
\ No newline at end of file
package com.yidian.common.http
import android.annotation.SuppressLint
import com.google.gson.JsonParseException
import com.yidian.common.YacBaseApplication
import com.yidian.utils.ToastUtil
import io.reactivex.rxjava3.core.Observer
import io.reactivex.rxjava3.disposables.Disposable
abstract class HttpResultSubscriber<T>(private var showProgress: Boolean = false) : Observer<HttpResult<T>?> {
private var isShowErrorMsg = true
override fun onSubscribe(d: Disposable) { //网络请求之前
if(showProgress){
}
}
override fun onComplete() { //网络请求完成
if(showProgress){
}
}
override fun onError(e: Throwable) {
if (e.toString().contains("ConnectException") || e.toString().contains("Unable to resolve host")) {
if (isShowErrorMsg) {
ToastUtil.showToast(YacBaseApplication.context, "当前网络链接不畅,请检查您的网络情况")
}
} else if ("connect timed out" == e.message) {
if (isShowErrorMsg) {
ToastUtil.showToast(YacBaseApplication.context, "服务器连接超时")
}
} else if (e is JsonParseException || e.toString().contains("Json") || e.toString().contains("Gson")) {
if (isShowErrorMsg) {
ToastUtil.showToast(YacBaseApplication.context, "数据解析异常")
}
} else {
if (isShowErrorMsg) {
ToastUtil.showToast(YacBaseApplication.context, "网络请求异常")
}
}
onRequestFailure(Exception(e.toString()))
}
override fun onNext(result: HttpResult<T>?) {
if (result?.code == 0) {
onRequestSuccess(result)
}else{
onFailer(result)
ToastUtil.showToast(YacBaseApplication.context, result?.reason)
}
}
abstract fun onSuccess(result: HttpResult<T>?)
abstract fun onFailer(result: HttpResult<T>?)
@SuppressLint("NewApi")
fun onRequestFailure(e: Throwable?) {
val result: HttpResult<T> = HttpResult()
result.code = -100
result.reason = e?.message
result.status = "failed"
onFailer(result)
}
@SuppressLint("NewApi")
fun onRequestSuccess(result: HttpResult<T>) {
onSuccess(result)
}
}
\ No newline at end of file
package com.yidian.common.http
import android.content.Context
interface IBaseView {
object LifeEvent {
const val CREATE = 0x01
const val START = 0x02
const val RESUME = 0x03
const val PAUSE = 0x04
const val STOP = 0x05
const val DESTROY = 0x06
}
/**
* 获取上下文对象
*
* @return
*/
val context: Context
/**
* 显示进度条
*
* @param message 要显示的信息
*/
fun showLoading(message: String?)
/**
* 显示进度条
*/
fun showLoading()
/**
* 隐藏进度条
*/
fun dismissLoading()
}
\ No newline at end of file
package com.yidian.common.http
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.ObservableTransformer
import io.reactivex.rxjava3.schedulers.Schedulers
class TransformUtil {
companion object{
fun <T> defaultSchedulers(): ObservableTransformer<T, T>? {
return ObservableTransformer { upstream: Observable<T> ->
upstream.observeOn(
AndroidSchedulers.mainThread()
).subscribeOn(Schedulers.io())
}
}
fun <T> allIo(): ObservableTransformer<T, T>? {
return ObservableTransformer { upstream: Observable<T> ->
upstream.observeOn(
Schedulers.io()
).subscribeOn(Schedulers.io())
}
}
}
}
\ No newline at end of file
package com.yidian.common.http.httpbean
import com.yidian.common.http.HttpResult
import com.yidian.common.http.IBaseView
interface IApiCallback: IBaseView {
fun loginCallBack(t: HttpResult<LoginBean.Response?>?)
}
\ No newline at end of file
package com.yidian.common.http.httpbean
class LoginBean (var request : Request,var response : Response){
data class Request(var mobileNo : String, var smsCode : String)
data class Response(var hasBindGatheringCard : String, var token : String)
}
package com.yidian.common.http.httpbean
/**
* FileName: WebResult
* Author: sym
* Date: 2021/3/5 2:26 PM
*/
class WebResult<T>(action: String, data: T) {
var action = action
var params: T? = data
}
\ No newline at end of file
package com.yidian.common.receiver;
import cn.jpush.android.service.JPushMessageReceiver;
/**
* Created by shanjia.li on 2017/12/14.
* 此类负责接收JPUSH推送消息
*/
public class JPushBroadcastReceiver extends JPushMessageReceiver {
}
package com.yidian.common.refresh
import android.content.Context
import android.graphics.drawable.AnimationDrawable
import android.util.AttributeSet
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import com.scwang.smart.refresh.layout.api.RefreshFooter
import com.scwang.smart.refresh.layout.api.RefreshHeader
import com.scwang.smart.refresh.layout.api.RefreshKernel
import com.scwang.smart.refresh.layout.api.RefreshLayout
import com.scwang.smart.refresh.layout.constant.RefreshState
import com.scwang.smart.refresh.layout.constant.SpinnerStyle
import com.yidian.common.R
class CustomFooter @JvmOverloads constructor(
context: Context,
attributeSet: AttributeSet? = null,
defStyle: Int = 0
) : FrameLayout(context, attributeSet, defStyle) , RefreshFooter{
init {
View.inflate(context, R.layout.layout_refresh_footer,this)
}
override fun getSpinnerStyle(): SpinnerStyle {
return SpinnerStyle.Translate
}
override fun onFinish(refreshLayout: RefreshLayout, success: Boolean): Int {
return 500
}
override fun onInitialized(kernel: RefreshKernel, height: Int, maxDragHeight: Int) {
}
override fun onHorizontalDrag(percentX: Float, offsetX: Int, offsetMax: Int) {
}
override fun setNoMoreData(noMoreData: Boolean): Boolean {
return noMoreData
}
override fun onReleased(refreshLayout: RefreshLayout, height: Int, maxDragHeight: Int) {
}
override fun getView(): View {
return this
}
override fun setPrimaryColors(vararg colors: Int) {
}
override fun onStartAnimator(refreshLayout: RefreshLayout, height: Int, maxDragHeight: Int) {
}
override fun onStateChanged(
refreshLayout: RefreshLayout,
oldState: RefreshState,
newState: RefreshState
) {
}
override fun onMoving(
isDragging: Boolean,
percent: Float,
offset: Int,
height: Int,
maxDragHeight: Int
) {
}
override fun isSupportHorizontalDrag(): Boolean {
return false
}
}
\ No newline at end of file
package com.yidian.common.refresh
import android.content.Context
import android.graphics.drawable.AnimationDrawable
import android.util.AttributeSet
import android.view.View
import android.view.animation.ScaleAnimation
import android.widget.FrameLayout
import android.widget.ImageView
import com.scwang.smart.refresh.layout.api.RefreshHeader
import com.scwang.smart.refresh.layout.api.RefreshKernel
import com.scwang.smart.refresh.layout.api.RefreshLayout
import com.scwang.smart.refresh.layout.constant.RefreshState
import com.scwang.smart.refresh.layout.constant.SpinnerStyle
import com.yidian.common.R
class CustomHeader @JvmOverloads constructor(
context: Context,
attributeSet: AttributeSet? = null,
defStyle: Int = 0
) : FrameLayout(context, attributeSet, defStyle) ,RefreshHeader{
private val TAG = "CustomHeader"
private var animDrawable:AnimationDrawable
private var imageView:ImageView
init {
View.inflate(context, R.layout.layout_refresh, this)
imageView = findViewById<ImageView>(R.id.iv_loading)
animDrawable = context.getDrawable(R.drawable.anim_loading) as AnimationDrawable
}
override fun getSpinnerStyle(): SpinnerStyle {
return SpinnerStyle.Translate
}
override fun onFinish(refreshLayout: RefreshLayout, success: Boolean): Int {
return 500
}
override fun onInitialized(kernel: RefreshKernel, height: Int, maxDragHeight: Int) {
}
override fun onHorizontalDrag(percentX: Float, offsetX: Int, offsetMax: Int) {
}
override fun onReleased(refreshLayout: RefreshLayout, height: Int, maxDragHeight: Int) {
}
override fun getView(): View {
return this
}
override fun setPrimaryColors(vararg colors: Int) {
}
override fun onStartAnimator(refreshLayout: RefreshLayout, height: Int, maxDragHeight: Int) {
imageView.setImageDrawable(animDrawable)
animDrawable.start()
}
override fun onStateChanged(
refreshLayout: RefreshLayout,
oldState: RefreshState,
newState: RefreshState
) {
if(oldState==RefreshState.RefreshFinish && newState ==RefreshState.None){
animDrawable.stop()
animDrawable.selectDrawable(0)
imageView.setImageResource(R.drawable.icon_pull_to_refresh_header_state_immobile_white)
}
}
private var oldPercent=0f
override fun onMoving(
isDragging: Boolean,
percent: Float,
offset: Int,
height: Int,
maxDragHeight: Int
) {
if(percent<=1f){
val scaleAnim = ScaleAnimation(oldPercent, percent, oldPercent, percent,imageView.width/2f,imageView.height/2f)
scaleAnim.fillAfter = true
scaleAnim.duration=1
imageView.startAnimation(scaleAnim)
oldPercent = percent
}
}
override fun isSupportHorizontalDrag(): Boolean {
return false
}
}
\ No newline at end of file
package com.yidian.common.refresh
import android.animation.ValueAnimator
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import com.scwang.smart.refresh.layout.SmartRefreshLayout
import com.scwang.smart.refresh.layout.api.RefreshComponent
import com.scwang.smart.refresh.layout.api.RefreshContent
import com.scwang.smart.refresh.layout.api.RefreshKernel
import com.scwang.smart.refresh.layout.api.RefreshLayout
import com.scwang.smart.refresh.layout.constant.RefreshState
class CustomSmartRefreshLayout @JvmOverloads constructor(
context: Context,
attributeSet: AttributeSet? = null
) : SmartRefreshLayout(context, attributeSet) {
init {
mKernel = InnerImpl(RefreshKernelImpl())
}
private var onMoving: ((Int) -> Unit)? = null
fun setOnLoadMoreMovingListener(onMoving: (Int) -> Unit) {
this.onMoving = onMoving
}
inner class InnerImpl(private val realKernelImpl: RefreshKernel) : RefreshKernel {
override fun requestNeedTouchEventFor(
internal: RefreshComponent,
request: Boolean
): RefreshKernel {
realKernelImpl.requestNeedTouchEventFor(internal, request)
return this
}
override fun requestRemeasureHeightFor(internal: RefreshComponent): RefreshKernel {
realKernelImpl.requestRemeasureHeightFor(internal)
return this
}
override fun moveSpinner(spinner: Int, isDragging: Boolean): RefreshKernel {
onMoving?.invoke(spinner)
realKernelImpl.moveSpinner(spinner, isDragging)
return this
}
override fun getRefreshLayout(): RefreshLayout {
return refreshLayout
}
override fun setState(state: RefreshState): RefreshKernel {
realKernelImpl.setState(state)
return this
}
override fun requestFloorBottomPullUpToCloseRate(rate: Float): RefreshKernel {
realKernelImpl.requestFloorBottomPullUpToCloseRate(rate)
return this
}
override fun requestDefaultTranslationContentFor(
internal: RefreshComponent,
translation: Boolean
): RefreshKernel {
realKernelImpl.requestDefaultTranslationContentFor(internal, translation)
return this
}
override fun requestFloorDuration(duration: Int): RefreshKernel {
realKernelImpl.requestFloorDuration(duration)
return this
}
override fun animSpinner(endSpinner: Int): ValueAnimator {
return realKernelImpl.animSpinner(endSpinner)
}
override fun requestDrawBackgroundFor(
internal: RefreshComponent,
backgroundColor: Int
): RefreshKernel {
realKernelImpl.requestDrawBackgroundFor(internal, backgroundColor)
return this
}
override fun getRefreshContent(): RefreshContent {
return refreshContent
}
override fun startTwoLevel(open: Boolean): RefreshKernel {
realKernelImpl.startTwoLevel(open)
return this
}
override fun finishTwoLevel(): RefreshKernel {
realKernelImpl.finishTwoLevel()
return this
}
}
}
\ No newline at end of file
package com.yidian.common.services.BsNewsContentTest
import com.yidian.common.base.BaseFragment
import com.yidian.yac.core.core.YacRouteNode
import com.yidian.yac.core.zap.ZapService
abstract class NewsTest: ZapService() {
abstract fun log()
}
\ No newline at end of file
package com.yidian.common.services
import com.yidian.common.base.BaseFragment
abstract class FragmentFactory :ZapServiceAdapter(){
abstract fun getFragment(path:String):BaseFragment?
}
\ No newline at end of file
package com.yidian.common.services
import cn.jpush.android.service.JCommonService
/**
* Author: hy
* Date: 4/29/21
* Description:
*/
class JPushService : JCommonService() {
}
\ No newline at end of file
### 所有公共服务接口放在该目录下
\ No newline at end of file
package com.yidian.common.services
import com.yidian.xarc.xrouter.ResultResolver
import com.yidian.yac.core.zap.ZapService
abstract class ZapServiceAdapter :ZapService (){
override fun onAction(
path: String,
action: String,
params: Any,
options: Any,
resolver: ResultResolver
) {}
override fun onExit() {}
override fun onInit() {}
}
\ No newline at end of file
package com.yidian.common.utils
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
class EncryptUtil {
companion object{
fun getMD5(info: String): String{
val md5: MessageDigest = MessageDigest.getInstance("MD5")
md5.update(info.toByteArray())
val m = md5.digest()
return byte2hex(m)
}
fun encryptToSHA(info: String): String{
val digesta: ByteArray
var rs = ""
try {
val alga: MessageDigest = MessageDigest.getInstance("SHA-1")
alga.update(info.toByteArray())
digesta = alga.digest()
rs = byte2hex(digesta)
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
}
return rs
}
private fun byte2hex(b: ByteArray): String{
var hs = ""
var stmp: String
repeat(b.size){
stmp = Integer.toHexString(b[it].toInt() and 0XFF)
hs = if(stmp.length == 1){
hs + "0" + stmp
}else{
hs + stmp
}
}
return hs
}
fun xDigest(device: String, pkgname: String): String? {
val millis = System.currentTimeMillis()
val key = MD5String.getMD5Low(device).substring(0, 8)
val string = pkgname + "_" + millis + "_" + key
val md5 = MD5String.getMD5Low(string)
return "" + millis + "_" + key + "_" + md5
}
}
}
\ No newline at end of file
package com.yidian.common.utils
import android.util.Log
import okhttp3.*
import java.io.IOException
import java.util.concurrent.TimeUnit
/**
* 该类只是为了模拟下载文件
*/
class FileDownloadUtil private constructor(){
companion object{
val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED){ FileDownloadUtil()}
}
private val TAG = "FileDownloadUtil"
private var requestCall: Call? = null
private val client: OkHttpClient = OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS).build()
private var cancel = false
fun startDownload(url:String){
cancel = false
Log.e(TAG, "startDownload: $url" )
if (!url.startsWith("http"))return
val request = Request.Builder().url(url).build()
client.newCall(request).enqueue(object :Callback{
override fun onFailure(call: Call, e: IOException) {
Log.e(TAG, "onFailure: " )
}
override fun onResponse(call: Call, response: Response) {
try {
val ins = response.body!!.byteStream()
val total = response.body!!.contentLength()
var sum = 0
var len =0
val buffer = ByteArray(2048)
while (!cancel){
len = ins.read(buffer)
if (len == -1) break
sum+=len
val progress = (sum.toFloat()/total)
Log.d("yue.huang","progress---:"+progress)
// if (progress>=0.3) break
}
}catch (e:Exception){
requestCall = null
}finally {
response.body?.close()
}
}
})
}
fun stopDownload(){
cancel = true
requestCall?.cancel()
requestCall=null
}
}
\ No newline at end of file
package com.yidian.common.utils;
/**
* Created by IntelliJ IDEA.
* User: lijun.he
* Date: 13-2-27
* Time: 下午5:01
* To change this template use File | Settings | File Templates.
*/
public class MD5String {
public static String getMD5(String string) {
return getMD5(string.getBytes());
}
public static String getMD5(byte[] source) {
return getMD5(source, hexDigits);
}
public static String getMD5Low(String string) {
return getMD5Low(string.getBytes());
}
public static String getMD5Low(byte[] source) {
return getMD5(source, hexDigitsLow);
}
private static char hexDigits[] = { // 用来将字节转换成 16 进制表示的字符
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
private static char hexDigitsLow[] = { // 用来将字节转换成 16 进制表示的字符
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
private static String getMD5(byte[] source, char hexDigits[]) {
String s = null;
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
md.update(source);
byte tmp[] = md.digest(); // MD5 的计算结果是一个 128 位的长整数,
// 用字节表示就是 16 个字节
char str[] = new char[16 * 2]; // 每个字节用 16 进制表示的话,使用两个字符,
// 所以表示成 16 进制需要 32 个字符
int k = 0; // 表示转换结果中对应的字符位置
for (int i = 0; i < 16; i++) { // 从第一个字节开始,对 MD5 的每一个字节
// 转换成 16 进制字符的转换
byte byte0 = tmp[i]; // 取第 i 个字节
str[k++] = hexDigits[byte0 >>> 4 & 0xf]; // 取字节中高 4 位的数字转换,
// >>> 为逻辑右移,将符号位一起右移
str[k++] = hexDigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换
}
s = new String(str); // 换后的结果转换为字符串
} catch (Exception e) {
}
return s;
}
}
package com.yidian.common.utils
import android.content.Context
import android.net.ConnectivityManager
import android.telephony.TelephonyManager
class NetWorkUtils {
companion object{
const val NET_TYPE_WIFI = "wifi"
const val NET_TYPE_4G = "4g"
const val NET_TYPE_5G = "5g"
const val NET_TYPE_3G = "3g"
const val NET_TYPE_2G = "2g"
fun getNetWorkType(context: Context): String{
var netType = "unknown"
val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val networkInfo = manager.activeNetworkInfo ?: return netType
val nType = networkInfo.type
if (nType == ConnectivityManager.TYPE_WIFI) {
netType = "wifi"
} else if (nType == ConnectivityManager.TYPE_MOBILE) {
val nSubType = networkInfo.subtype
val telephonyManager: TelephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
netType = if(nSubType ==TelephonyManager.NETWORK_TYPE_NR){
"5g"
} else if (nSubType == TelephonyManager.NETWORK_TYPE_LTE && !telephonyManager.isNetworkRoaming) {
"4g"
} else if (nSubType == TelephonyManager.NETWORK_TYPE_UMTS || nSubType == TelephonyManager.NETWORK_TYPE_HSDPA || (nSubType == TelephonyManager.NETWORK_TYPE_EVDO_0
&& !telephonyManager.isNetworkRoaming)) {
"3g"
} else if (nSubType == TelephonyManager.NETWORK_TYPE_GPRS || nSubType == TelephonyManager.NETWORK_TYPE_EDGE || (nSubType == TelephonyManager.NETWORK_TYPE_CDMA
&& !telephonyManager.isNetworkRoaming)) {
"2g"
} else {
"unknown"
}
}
return netType
}
fun isWifiConnected(context: Context):Boolean{
return NET_TYPE_WIFI == getNetWorkType(context)
}
}
}
\ No newline at end of file
package com.yidian.common.utils
import android.content.Context
import com.tencent.mmkv.MMKV
/**
* @description: 本地存储类(可切用户)
* @author: huyajun
* @date: 2021/4/27
**/
class StorageUtil {
var scopeStore: MMKV? = null
private set(value) {
field = value
}
var userStore: MMKV? = null
private set(value) {
field = value
}
companion object {
@JvmStatic
val INSTANCE: StorageUtil by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
StorageUtil()
}
}
fun init(context: Context) {
MMKV.initialize(context)
}
/**
* 切圈
* @param scopeName appid +城市名
*/
fun switchScope(scopeName: String) {
scopeStore = MMKV.mmkvWithID(scopeName)
}
/**
* 切用户
* @param userId 用户id
*/
fun switchUser(userId: String) {
userStore = MMKV.mmkvWithID(userId)
}
}
val StorageManager = StorageUtil.INSTANCE
\ No newline at end of file
package com.yidian.common.utils
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import java.io.File
class UploadFileUtils {
companion object{
fun fileToMultipartBodyPart(fileKey: String, file: File): MultipartBody.Part{
val requestFile = file.asRequestBody("multipart/form-data".toMediaTypeOrNull())
return MultipartBody.Part.createFormData(fileKey, file.name, requestFile)
}
}
}
\ No newline at end of file
package com.yidian.common.utils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.RecyclerView
class ViewHolderUtil {
companion object{
fun RecyclerView.Adapter<RecyclerView.ViewHolder>.createItemView(parent:ViewGroup,@LayoutRes layoutId:Int):View{
return LayoutInflater.from(parent.context).inflate(layoutId,parent,false)
}
}
}
\ No newline at end of file
package com.yidian.common.utils
import com.yidian.xarc.xbase.utils.XLogger
/**
* @description: TODO 类描述
* @author: huyajun
* @date: 2021/3/16
**/
class XLog{
companion object {
@JvmStatic
val INSTANCE: XLogger by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
XLogger("yac")
}
}
}
val XLogUtil = XLog.INSTANCE
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromXDelta="-100.0%p"
android:toXDelta="0.0" />
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromXDelta="100.0%p"
android:toXDelta="0.0" />
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="300"
android:fromXDelta="0.0"
android:toXDelta="100.0%p" />
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/icon_pull_to_refresh_header_state_white"
android:duration="200"/>
<item android:drawable="@drawable/icon_pull_to_refresh_header_state"
android:duration="200"/>
<item android:drawable="@drawable/icon_pull_to_refresh_header_state_white"
android:duration="200"/>
</animation-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:background="@color/gray_bg_color">
<ProgressBar
android:id="@+id/iv_loading"
style="@android:style/Widget.ProgressBar.Large"
android:layout_gravity="center"
android:layout_width="30dp"
android:layout_height="30dp"/>
<TextView
android:id="@+id/tv_loading"
android:layout_marginLeft="8dp"
android:layout_gravity="center"
android:text="加载中..."
android:textSize="18sp"
android:textColor="@color/color_a8abb1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="80dp">
<ImageView
android:id="@+id/iv_loading"
android:src="@drawable/icon_pull_to_refresh_header_state_immobile_white"
android:layout_gravity="center"
android:layout_width="180dp"
android:layout_height="70dp"
android:layout_centerInParent="true"/>
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:gravity="center">
<ProgressBar
android:id="@+id/iv_loading"
style="@android:style/Widget.ProgressBar.Small"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/tv_loading"
android:layout_marginLeft="8dp"
android:layout_gravity="center"
android:text="努力加载中"
android:textSize="12sp"
android:textColor="@color/color_a8abb1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:contentInsetStartWithNavigation="0dp"
app:navigationIcon="@drawable/ic_back_black">
<TextView
android:id="@+id/title_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:ellipsize="end"
android:maxLines="1"
android:textColor="#666666"
android:textSize="16sp" />
</androidx.appcompat.widget.Toolbar>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="bgColor">#f4f4f4</color>
<color name="gray_bg_color">#2d000000</color>
<color name="color_a8abb1">#a8abb1</color>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<root-path name="root" path="" />
<files-path name="files" path="" />
<cache-path name="cache" path="" />
<external-path name="external" path="" />
<external-files-path name="external_files" path="" />
<external-cache-path name="external_cache" path="" />
<external-files-path name="external_files_path" path="Download" />
</paths>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
\ No newline at end of file
/build
\ No newline at end of file
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'kotlin-android-extensions'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yidian.promptdialoglib">
/
</manifest>
\ No newline at end of file
package com.yidian.promptdialoglib
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.text.TextUtils
import android.util.DisplayMetrics
import android.view.*
import android.widget.LinearLayout
import android.widget.TextView
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import com.yidian.promptdialoglib.listener.OnDialogNegativeListener
import com.yidian.promptdialoglib.listener.OnDialogPositiveListener
/**
* 提示框
*/
open class PromptDialog : DialogFragment(), View.OnClickListener {
private var mTitle: String? = null
private var mSubTitle: String? = null
private var mContentTitle: String? = null
private var mContent: String? = null
private var mNegative: String? = null
private var mPositive: String? = null
private var mRequstCode = 0
private var mContext: Context? = null
private var widthRatio: Float = 0.5f
private var heightRatio: Float = 0.5f
private var canCancelable = false
private var orientationMode = Orientation.HORIZONTAL
private var llBottomHorizontal: LinearLayout? = null
private var llBottomVertical: LinearLayout? = null
private var tvTitle: TextView? = null
private var tvSubTitle: TextView? = null
private var tvContentTitle: TextView? = null
private var tvContent: TextView? = null
private var tvBottomVerticalNegative: TextView? = null
private var tvBottomVerticalPositive: TextView? = null
private var tvBottomHorizontalNegative: TextView? = null
private var tvBottomHorizontalPositive: TextView? = null
// private static SMSDiaolog mSmsDiaolog;
private var mOnDialogPositiveListener: OnDialogPositiveListener? = null
private var mOnDialogNegativeListener: OnDialogNegativeListener? = null
override fun onClick(v: View) {
val id = v.id
if (id == R.id.tvBottomHorizontalNegative) {
if (mOnDialogNegativeListener != null && mRequstCode != 0) {
mOnDialogNegativeListener!!.onNegativeButtonClicked(mRequstCode)
}
dialog!!.dismiss()
} else if (id == R.id.tvBottomHorizontalPositive) {
if (mOnDialogPositiveListener != null && mRequstCode != 0) {
mOnDialogPositiveListener!!.onPositiveButtonClicked(mRequstCode)
}
dialog!!.dismiss()
} else if (id == R.id.tvBottomVerticalNegative) {
if (mOnDialogPositiveListener != null && mRequstCode != 0) {
mOnDialogPositiveListener!!.onPositiveButtonClicked(mRequstCode)
}
dialog!!.dismiss()
} else if (id == R.id.tvBottomVerticalPositive) {
if (mOnDialogNegativeListener != null && mRequstCode != 0) {
mOnDialogNegativeListener!!.onNegativeButtonClicked(mRequstCode)
}
dialog!!.dismiss()
}
}
fun setOrientationr(mode: Orientation): PromptDialog {
this.orientationMode = mode
return this
}
fun setTitle(title: String?): PromptDialog {
mTitle = title
setText(tvTitle, mTitle)
return this
}
fun setSubTitle(subTitle: String?): PromptDialog {
mSubTitle = subTitle
setText(tvSubTitle, mSubTitle)
return this
}
fun setContentTitle(message: String?): PromptDialog {
mContentTitle = message
setText(tvContentTitle, mContentTitle)
return this
}
fun setContent(message: String?): PromptDialog {
mContent = message
setText(tvContent, mContent)
return this
}
fun setNegative(negative: String?): PromptDialog {
mNegative = negative
setBottomVisibility()
return this
}
//
// fun setNeutral(center: String?): PromptDialog {
// mBottomVerticalPositive = center
// setBottomVisibility()
// return this
// }
private fun setText(textView: TextView?, text: String?) {
if (textView != null) {
textView.text = text
}
}
private fun setBottomVisibility() {
if (orientationMode == Orientation.VERTICAL) {
llBottomHorizontal?.setVisibility(View.GONE)
llBottomVertical?.setVisibility(View.VISIBLE)
if (!TextUtils.isEmpty(mNegative)) {
tvBottomVerticalNegative?.visibility = View.VISIBLE
} else {
tvBottomVerticalNegative?.visibility = View.GONE
}
setText(tvBottomHorizontalPositive, mPositive)
setText(tvBottomVerticalNegative, mNegative)
} else {
llBottomHorizontal?.setVisibility(View.VISIBLE)
llBottomVertical?.setVisibility(View.GONE)
setText(tvBottomHorizontalPositive, mPositive)
setText(tvBottomHorizontalNegative, mNegative)
}
}
fun setPositive(positive: String?): PromptDialog {
mPositive = positive
setBottomVisibility()
return this
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
dialog!!.requestWindowFeature(Window.FEATURE_NO_TITLE) //去掉Dialog的标题部分
val view = inflater.inflate(R.layout.dialog_prompt_view, null)
llBottomHorizontal = view.findViewById(R.id.llBottomHorizontal)
llBottomVertical = view.findViewById(R.id.llBottomVertical)
tvTitle = view.findViewById(R.id.tvDialogTitle)
tvSubTitle = view.findViewById(R.id.tvDialogSubTitle)
tvContentTitle = view.findViewById(R.id.tvDialogContentTitle)
tvContent = view.findViewById(R.id.tvDialogContent)
tvBottomVerticalNegative = view.findViewById(R.id.tvBottomVerticalNegative)
tvBottomVerticalPositive = view.findViewById(R.id.tvBottomVerticalPositive)
tvBottomHorizontalNegative = view.findViewById(R.id.tvBottomHorizontalNegative)
tvBottomHorizontalPositive = view.findViewById(R.id.tvBottomHorizontalPositive)
setTitle(mTitle)
setSubTitle(mSubTitle)
setContentTitle(mContentTitle)
setContent(mContent)
setBottomVisibility()
tvBottomHorizontalNegative?.setOnClickListener(this)
tvBottomHorizontalPositive?.setOnClickListener(this)
tvBottomVerticalNegative?.setOnClickListener(this)
tvBottomVerticalPositive?.setOnClickListener(this)
if (mContext != null) {
if (mRequstCode != 0 && mContext is OnDialogPositiveListener) {
mOnDialogPositiveListener = mContext as OnDialogPositiveListener?
}
if (mRequstCode != 0 && mContext is OnDialogNegativeListener) {
mOnDialogNegativeListener = mContext as OnDialogNegativeListener?
}
}
//点击外部不消失
isCancelable = canCancelable
dialog!!.setOnKeyListener { dialog, keyCode, event ->
if (keyCode == KeyEvent.KEYCODE_BACK) {
true
} else false
}
return view
}
fun setListener(any: Any?): PromptDialog {
if (any != null) {
if (mRequstCode != 0 && any is OnDialogPositiveListener) {
mOnDialogPositiveListener = any
}
if (mRequstCode != 0 && any is OnDialogNegativeListener) {
mOnDialogNegativeListener = any
}
}
return this
}
override fun onStart() {
super.onStart()
val dialog = dialog
if (dialog != null) {
getDialog()!!.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
/**
* 设置Dialog的大小
*/
val dm = DisplayMetrics()
activity!!.windowManager.defaultDisplay.getMetrics(dm)
dialog.window!!
.setLayout(
(dm.widthPixels * widthRatio).toInt(),
(dm.heightPixels * heightRatio).toInt()
)
}
}
fun setWidthRatio(widthRatio: Float): PromptDialog {
this.widthRatio = widthRatio
return this
}
fun setHightRatio(heightRatio: Float): PromptDialog {
this.heightRatio = heightRatio
return this
}
fun setRequestCode(requestCode: Int): PromptDialog {
mRequstCode = requestCode
return this
}
// fun setCanCancelable(cancelable: Boolean): PromptDialog {
// this.canCancelable = canCancelable
// return this
// }
fun show(context: Context?) {
mContext = context
super.show(mFragmentManager!!, null)
}
companion object {
private var mFragmentManager: FragmentManager? = null
@JvmStatic
@Synchronized
fun create(fragmentManager: FragmentManager?): PromptDialog {
// if (mSmsDiaolog == null) {
// mSmsDiaolog = new SMSDiaolog();
// }
mFragmentManager = fragmentManager
return PromptDialog()
}
}
}
enum class Orientation() {
VERTICAL,
HORIZONTAL
}
\ No newline at end of file
package com.yidian.promptdialoglib.listener;
public interface OnDialogCancelListener {
void onCanceled(int requestCode);
}
package com.yidian.promptdialoglib.listener;
public interface OnDialogNegativeListener {
void onNegativeButtonClicked(int requestCode);
}
package com.yidian.promptdialoglib.listener;
public interface OnDialogPositiveListener {
void onPositiveButtonClicked(int requestCode);
}
package com.yidian.promptdialoglib.utils;
import android.content.Context;
import android.graphics.Typeface;
import androidx.collection.SimpleArrayMap;
import com.yidian.promptdialoglib.R;
public class TypefaceUtil {
private static TypefaceUtil mTypefaceUtil;
private static SimpleArrayMap<String, Typeface> mCache = new SimpleArrayMap<>();
private TypefaceUtil() {
// cannot be instantiated
}
public static synchronized TypefaceUtil getInstance() {
if (mTypefaceUtil == null) {
mTypefaceUtil = new TypefaceUtil();
}
return mTypefaceUtil;
}
public static void releaseInstance() {
if (mTypefaceUtil != null) {
mTypefaceUtil = null;
}
if (mCache != null) {
mCache.clear();
mCache = null;
}
}
public synchronized Typeface get(Context ctx, String name) {
try {
if (!mCache.containsKey(name)) {
Typeface typeface = Typeface.createFromAsset(ctx.getAssets(), String.format(ctx.getResources().getString(R.string.file_ttf), name));
mCache.put(name, typeface);
return typeface;
}
} catch (Exception e) {
}
return mCache.get(name);
}
}
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<corners
android:bottomLeftRadius="26dp"
android:bottomRightRadius="26dp"
android:topLeftRadius="26dp"
android:topRightRadius="26dp" />
<stroke
android:width="1dp"
android:color="#ff979797" />
<solid android:color="#ffffff" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<corners
android:bottomLeftRadius="26dp"
android:bottomRightRadius="26dp"
android:topLeftRadius="26dp"
android:topRightRadius="26dp" />
<!-- <stroke-->
<!-- android:width="0.5dp"-->
<!-- android:color="@color/color_ffc199" />-->
<solid android:color="#ff0036e9" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<corners
android:bottomLeftRadius="24dp"
android:bottomRightRadius="24dp"
android:topLeftRadius="24dp"
android:topRightRadius="24dp" />
<!-- <stroke-->
<!-- android:width="1dp"-->
<!-- android:color="#ff979797" />-->
<solid android:color="#ffffff" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_dialog_ffffff"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.8"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tvDialogTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="00"
android:textColor="#000000"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvDialogSubTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="00"
android:textColor="#101214"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.5"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:orientation="vertical">
<TextView
android:id="@+id/tvDialogContentTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="111"
android:textColor="#000000"
android:textSize="12sp" />
<TextView
android:id="@+id/tvDialogContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:lineSpacingExtra="3dp"
android:text="111"
android:textColor="#101214"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/llBottomVertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical"
android:visibility="visible">
<TextView
android:id="@+id/tvBottomVerticalPositive"
android:layout_width="150dp"
android:layout_height="38dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:background="@drawable/bg_dialog_bottom_btn_right"
android:gravity="center"
android:text="发送"
android:textColor="#ffffff"
android:textSize="15sp" />
<TextView
android:id="@+id/tvBottomVerticalNegative"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="10dp"
android:layout_marginRight="15dp"
android:background="#ffffff"
android:gravity="center"
android:text="发送"
android:textColor="#101214"
android:textSize="15sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/llBottomHorizontal"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<LinearLayout
android:id="@+id/llBottomHorizontalNegative"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center">
<TextView
android:id="@+id/tvBottomHorizontalNegative"
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="5dp"
android:background="@drawable/bg_dialog_bottom_btn_left"
android:gravity="center"
android:text="返回修改"
android:textSize="15sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/llBottomHorizontalPositive"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1.7"
android:gravity="center">
<TextView
android:id="@+id/tvBottomHorizontalPositive"
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="10dp"
android:background="@drawable/bg_dialog_bottom_btn_right"
android:gravity="center"
android:text="发送"
android:textColor="#ffffff"
android:textSize="15sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
\ No newline at end of file
<resources>
<string name="file_ttf">fonts/%s.ttf</string>
</resources>
/build
\ No newline at end of file
if (rootProject.ext.android.isApplication){
apply plugin: 'com.android.application'
apply plugin: 'yac-engine'
}else {
apply plugin: 'com.android.library'
}
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.mob.sdk'
MobSDK {
appKey ""
appSecret ""
ShareSDK {
loopShare true
devInfo {
SinaWeibo {
appKey ""
appSecret ""
callbackUri "http://www.sharesdk.cn"
}
Wechat {
appId ""
appSecret ""
userName ""
path "pages/index/index.html?id=1"
withShareTicket true
miniprogramType 2
}
WechatMoments {
appId ""
appSecret ""
}
// WechatFavorite {
// appId "wx4868b35061f87885"
// appSecret "64020361b8ec4c99936c0e3999a9f249"
// }
QQ {
appId ""
appKey ""
}
// QZone {
// appId "101923771"
// appKey "c34d63cfcf9f8b13a71f48424b48d692"
// }
}
}
}
android {
compileSdkVersion build_versions.compileSdkVersion
defaultConfig {
if (rootProject.ext.android.isApplication){
applicationId "com.yidian.yac"
}
minSdkVersion build_versions.minSdkVersion
targetSdkVersion build_versions.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets{
main{
if (rootProject.ext.android.isApplication){
manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
}else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
androidExtensions{
experimental = true
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
rootProject.ext.dependencies.other.each{
implementation project(it)
}
implementation rootProject.ext.dependencies.publicImplementation
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation rootProject.ext.dependencies.testImplementation
androidTestImplementation rootProject.ext.dependencies.androidTestImplementation
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yidian.yac.newscontent">
<application>
<activity
android:name="com.yidian.yac.newscontent.ui.MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.yidian.yac.newscontent.ui.NewsContentActivity" />
<activity
android:name="com.mob.tools.MobUIShell"
android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
</application>
</manifest>
\ No newline at end of file
{
"yellow":"#ffff00",
"colorAccent":"#D81B60",
"purple":"#8877ff",
"sepline":{
"day":"#f7f7f7",
"night":"#1a1a1a"
},
"superLink":"#6a85a9",
"clickColor":{
"day":"#f6f7f9",
"night":"#1a1a1a"
},
"textStrong":{
"day":"#919191",
"night":"#919191"
},
"textWeak":{
"day":"#bfbfbf",
"night":"#666666"
},
"summary":"#5a5a5a",
"title":"#222222",
"sepWeak":"#e6e6e6",
"sepStrong":"#d9d9d9",
"background":"#ffffff",
"backgroundColor":"#ffffff",
"themeBestPromptBgColor":{
"day":"#fafafa",
"night":"#2b2b2b"
},
"themeBestPromptTextColor":{
"day":"#b0b0b0",
"night":"#666666"
}
}
{
"ios":{
"padding":{
"small":15,
"middle":15,
"big":15,
"iPad":90
},
"paddingTop":{
"default":15,
"iPad":20
},
"marginTop":{
"default":24
}
},
"android":{
"padding":15,
"marginTop":19
}
}
{
"ios":{
"images": {
"share_logo": "assets/ios/images/local_share_logo@3x.png",
"yidianhao_promotion_card_focus": "assets/ios/images/yidianhao_promotion_card_focus@3x.png",
"refresh_position": "assets/ios/images/refresh_position@3x.png",
"quick_news_red_dot": "assets/ios/images/quick_news_red_dot@3x.png",
"special_topic_arrow": {
"day": "assets/android/images/special_topic_arrow@3x.png",
"night": "assets/android/images/local_share_logo@3x.png"
}
},
"lotties": {
"lottie_animation_1": "assets/ios/lotties/lottie_animation_1.json"
},
"gifs": {
"mf_play_icon": "assets/ios/mf_play_icon.gif"
}
},
"android":{
"images": {
"share_logo": "assets/android/assets/ios/images/local_share_logo@3x.png",
"quick_news_red_dot": "assets/android/images/quick_news_red_dot@3x.png",
"special_topic_arrow": {
"day": "assets/android/images/special_topic_arrow@3x.png",
"night": "assets/android/images/local_share_logo@3x.png"
}
},
"lotties": {
"lottie_animation_1": "assets/android/lotties/lottie_animation_1.json"
},
"gifs": {
"mf_play_icon": "assets/android/mf_play_icon.gif"
}
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment