[5.0.0]
[taxi乘客无人化]
1
OCH/taxi/taxi-unmanned-passenger/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
66
OCH/taxi/taxi-unmanned-passenger/build.gradle
Normal file
@@ -0,0 +1,66 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.android.compileSdkVersion
|
||||
// buildToolsVersion rootProject.ext.android.buildToolsVersion
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.android.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.android.targetSdkVersion
|
||||
versionCode Integer.valueOf(VERSION_CODE)
|
||||
versionName getValueFromRootProperties("${project.name.replace("-", "_").toUpperCase()}_VERSION")
|
||||
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
arguments = [AROUTER_MODULE_NAME: project.getName()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
debug {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation rootProject.ext.dependencies.kotlinstdlibjdk7
|
||||
implementation rootProject.ext.dependencies.androidxappcompat
|
||||
implementation rootProject.ext.dependencies.arouter
|
||||
implementation rootProject.ext.dependencies.androidxrecyclerview
|
||||
implementation rootProject.ext.dependencies.material
|
||||
implementation rootProject.ext.dependencies.flexbox
|
||||
annotationProcessor rootProject.ext.dependencies.aroutercompiler
|
||||
implementation rootProject.ext.dependencies.rxandroid
|
||||
implementation rootProject.ext.dependencies.androidxconstraintlayout
|
||||
implementation rootProject.ext.dependencies.amapnavi3dmap
|
||||
implementation rootProject.ext.dependencies.amapsearch
|
||||
|
||||
implementation project(":OCH:mogo-och-common-module")
|
||||
compileOnly project(":libraries:mogo-map")
|
||||
implementation project(':core:mogo-core-res')
|
||||
|
||||
}
|
||||
|
||||
apply from: new File(rootProject.rootDir, "gradle/upload.gradle").toString()
|
||||
0
OCH/taxi/taxi-unmanned-passenger/consumer-rules.pro
Normal file
3
OCH/taxi/taxi-unmanned-passenger/gradle.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
GROUP=com.mogo.och
|
||||
POM_ARTIFACT_ID=och-taxi-passenger
|
||||
VERSION_CODE=1
|
||||
21
OCH/taxi/taxi-unmanned-passenger/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# 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
|
||||
@@ -0,0 +1,13 @@
|
||||
<?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.mogo.och.taxi.passenger">
|
||||
|
||||
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
|
||||
<uses-permission android:name="android.permission.WRITE_SETTINGS"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
<application>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
BIN
OCH/taxi/taxi-unmanned-passenger/src/main/assets/style.data
Executable file
BIN
OCH/taxi/taxi-unmanned-passenger/src/main/assets/style_extra.data
Executable file
@@ -0,0 +1,24 @@
|
||||
package com.mogo.och.taxi.passenger;
|
||||
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.mogo.eagle.core.function.api.base.IMoGoFunctionProvider;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author congtaowang
|
||||
* @since 2021/1/15
|
||||
*
|
||||
* 网约车抽象接口
|
||||
*/
|
||||
public interface IMogoOCH extends IMoGoFunctionProvider {
|
||||
|
||||
/**
|
||||
* 初始化网约车容器
|
||||
*
|
||||
* @param activity
|
||||
* @param containerId 容器ID
|
||||
*/
|
||||
void createCoverage(FragmentActivity activity, @IdRes int containerId);
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.mogo.och.taxi.passenger;
|
||||
|
||||
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_TAXI_P;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import com.alibaba.android.arouter.facade.annotation.Route;
|
||||
import com.mogo.eagle.core.function.call.setting.CallerMoGoUiSettingManager;
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
|
||||
import com.mogo.och.taxi.passenger.constant.TaxiPassengerConst;
|
||||
import com.mogo.och.taxi.passenger.ui.TaxiPassengerBaseFragment;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author congtaowang
|
||||
* @since 2021/1/15
|
||||
* <p>
|
||||
* 网约车-出租车-乘客端
|
||||
*/
|
||||
@Route(path = TaxiPassengerConst.PATH)
|
||||
public class MogoOCHTaxiPassenger implements IMogoOCH {
|
||||
|
||||
private static final String TAG = "MogoOCHTaxiPassenger";
|
||||
private TaxiPassengerBaseFragment ochTaxiPassengerFragment;
|
||||
private FragmentActivity mActivity;
|
||||
private int mContainerId;
|
||||
|
||||
@Override
|
||||
public void init(Context context) {
|
||||
CallerLogger.INSTANCE.d(M_TAXI_P + TAG, "init");
|
||||
}
|
||||
|
||||
/**
|
||||
* 进入鹰眼模式,设置手势缩放地图失效
|
||||
*/
|
||||
private void stepIntoVrMode() {
|
||||
CallerLogger.INSTANCE.d(M_TAXI_P + TAG, "进入vr模式");
|
||||
CallerMoGoUiSettingManager.INSTANCE.stepInNightMode();//夜间模式 状态栏字体颜色变黑
|
||||
}
|
||||
|
||||
private void showFragment() {
|
||||
FragmentManager supportFragmentManager = mActivity.getSupportFragmentManager();
|
||||
if (ochTaxiPassengerFragment == null) {
|
||||
CallerLogger.INSTANCE.d(M_TAXI_P + TAG, "准备add fragment======");
|
||||
Fragment fragmentByTag = supportFragmentManager.findFragmentByTag(TaxiPassengerBaseFragment.TAG);
|
||||
if (fragmentByTag instanceof TaxiPassengerBaseFragment){
|
||||
ochTaxiPassengerFragment = (TaxiPassengerBaseFragment) fragmentByTag;
|
||||
}else {
|
||||
ochTaxiPassengerFragment = new TaxiPassengerBaseFragment();
|
||||
}
|
||||
if (!ochTaxiPassengerFragment.isAdded()){
|
||||
supportFragmentManager.beginTransaction().add(mContainerId, ochTaxiPassengerFragment
|
||||
,TaxiPassengerBaseFragment.TAG).commitAllowingStateLoss();
|
||||
}
|
||||
return;
|
||||
}
|
||||
CallerLogger.INSTANCE.d(M_TAXI_P + TAG, "准备show fragment");
|
||||
supportFragmentManager.beginTransaction().show(ochTaxiPassengerFragment).commitAllowingStateLoss();
|
||||
}
|
||||
|
||||
private void hideFragment() {
|
||||
if (ochTaxiPassengerFragment != null) {
|
||||
mActivity.getSupportFragmentManager().beginTransaction().hide(ochTaxiPassengerFragment).commitAllowingStateLoss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createCoverage(FragmentActivity activity, int containerId) {
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFunctionName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Fragment createCoverage(@Nullable FragmentActivity fragmentActivity, @Nullable Integer integer) {
|
||||
this.mActivity = fragmentActivity;
|
||||
this.mContainerId = integer;
|
||||
showFragment();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
// 若不调用finish, 设置中打开关闭UITouch,会造成och fragment 重叠
|
||||
if (mActivity == null) return;
|
||||
mActivity.finish();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.mogo.och.taxi.passenger.bean;
|
||||
|
||||
import com.mogo.eagle.core.data.BaseData;
|
||||
|
||||
/**
|
||||
* Created by pangfan on 2021/8/19
|
||||
* 查询订单返回数据结构
|
||||
*/
|
||||
public class TaxiPassengerBaseRespBean extends BaseData {
|
||||
public Object data;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mogo.och.taxi.passenger.bean;
|
||||
|
||||
/**
|
||||
* Created by pangfan on 2021/8/19
|
||||
* 验证手机号后四位同时流转订单状态
|
||||
*/
|
||||
public class TaxiPassengerCheckPhoneUpdateOrderReqBean {
|
||||
|
||||
public String orderNo;
|
||||
public String phone;
|
||||
|
||||
public TaxiPassengerCheckPhoneUpdateOrderReqBean(String orderNo,String phone) {
|
||||
this.orderNo = orderNo;
|
||||
this.phone = phone;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mogo.och.taxi.passenger.bean;
|
||||
|
||||
/**
|
||||
* Created by pangfan on 2021/8/19
|
||||
* 查询订单信息请求数据结构
|
||||
*/
|
||||
public class TaxiPassengerOrderQueryReqBean {
|
||||
|
||||
public String driverSn;
|
||||
public String orderNo;
|
||||
|
||||
public TaxiPassengerOrderQueryReqBean(String driverSn, String orderNo) {
|
||||
this.driverSn = driverSn;
|
||||
this.orderNo = orderNo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.mogo.och.taxi.passenger.bean;
|
||||
|
||||
import com.mogo.eagle.core.data.BaseData;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Created by pangfan on 2021/8/19
|
||||
* 查询订单返回数据结构
|
||||
*/
|
||||
public class TaxiPassengerOrderQueryRespBean extends BaseData {
|
||||
public Result data;
|
||||
|
||||
public static class Result implements Comparable<Result>{
|
||||
// 订单no
|
||||
public String orderNo;
|
||||
// 订单类型
|
||||
public int orderType; //1即时单 2预约单
|
||||
// 订单状态
|
||||
public int orderStatus;
|
||||
// 订单运营类型 (9出租车,10小巴)
|
||||
public int businessType;
|
||||
// 起始站点id
|
||||
public int startSiteId;
|
||||
// 起始站点名称
|
||||
public String startSiteAddr;
|
||||
// 起始站点坐标
|
||||
public List<Double> startSitePoint; //wgs坐标,用于自动驾驶 [lon,lat]
|
||||
public List<Double> startSiteGcjPoint; //高德坐标,用于本地计算距离 [lon,lat]
|
||||
// 终点站点id
|
||||
public int endSiteId;
|
||||
// 终点站点名称
|
||||
public String endSiteAddr;
|
||||
// 终点站点坐标
|
||||
public List<Double> endSitePoint; //wgs坐标,用于自动驾驶 [lon,lat]
|
||||
public List<Double> endSiteGcjPoint; //高德坐标,用于计算距离 [lon,lat]
|
||||
|
||||
// 车牌号
|
||||
public String carNumber;
|
||||
//订单创建时间戳
|
||||
public long createTime;
|
||||
//开始服务时间戳:司机点击'开始服务'后订单状态更新成功的时间
|
||||
public long startTime;
|
||||
//预计用车时间:预约单=下单时的预约用车时间;即时单=派单成功的时间+预估的达到上车点的时间
|
||||
public long bookingTime;
|
||||
//乘客手机号
|
||||
public String passengerPhone;
|
||||
//订单多少乘客
|
||||
public String passengerNum;
|
||||
|
||||
public long lineId = -1; //路线id,默认-1
|
||||
public String csvFileUrl = ""; //轨迹文件下载的cos url,默认“”
|
||||
public String csvFileMd5 = ""; //轨迹文件md5,默认“”
|
||||
public String txtFileUrl = ""; //打点文件下载的cos url,默认“”
|
||||
public String txtFileMd5 = ""; //轨迹文件md5,默认“”
|
||||
public long contrailSaveTime; //上传轨迹完成时间戳ms:用于MEC本地手动导入轨迹验证时不会被云端轨迹覆盖
|
||||
public String carModel = ""; //[optional] 车型号(如红旗H9),默认“”,暂不加入校验逻辑、用于人工排查问题
|
||||
public String csvFileUrlDPQP = ""; //轨迹文件下载的cos url,默认“”
|
||||
public String csvFileMd5DPQP = ""; //轨迹文件md5,默认“”
|
||||
public String txtFileUrlDPQP = ""; //打点文件下载的cos url,默认“”
|
||||
public String txtFileMd5DPQP = ""; //轨迹文件md5,默认“”
|
||||
public long contrailSaveTimeDPQP; //上传轨迹完成时间戳ms:用于MEC本地手动导入轨迹验证时不会被云端轨迹覆盖
|
||||
|
||||
@Override
|
||||
public int compareTo(Result o) {
|
||||
boolean isEqual = this.orderNo.equals(o.orderNo);
|
||||
return isEqual ? 0 : 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Result result = (Result) o;
|
||||
return Objects.equals(orderNo, result.orderNo) &&
|
||||
orderType == result.orderType &&
|
||||
orderStatus == result.orderStatus &&
|
||||
businessType == result.businessType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(orderNo, orderType, orderStatus, businessType, startSiteId,
|
||||
startSiteAddr, startSitePoint, startSiteGcjPoint, endSiteId, endSiteAddr,
|
||||
endSitePoint, endSiteGcjPoint, carNumber, createTime, startTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.mogo.och.taxi.passenger.bean;
|
||||
|
||||
import com.mogo.eagle.core.data.BaseData;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created on 2021/9/8
|
||||
* 查询全部服务中/待服务订单的返回数据
|
||||
*/
|
||||
public class TaxiPassengerOrdersInServiceQueryRespBean extends BaseData {
|
||||
public Result data;
|
||||
|
||||
public static class Result {
|
||||
public List<TaxiPassengerOrderQueryRespBean.Result> servicing; //服务中订单
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.mogo.och.taxi.passenger.bean;
|
||||
|
||||
/**
|
||||
* Created by pangfan on 2021/8/19
|
||||
* 司机端准备好或者乘客已验证上车请求参数
|
||||
*/
|
||||
public class TaxiPassengerStartReqBean {
|
||||
|
||||
public String orderNo;
|
||||
public String sn;
|
||||
public TaxiPassengerStartReqBean.Result loc;
|
||||
|
||||
public static class Result {
|
||||
public Double lat;
|
||||
public Double lon;
|
||||
}
|
||||
|
||||
public TaxiPassengerStartReqBean(String sn, String orderNo, TaxiPassengerStartReqBean.Result point) {
|
||||
this.sn = sn;
|
||||
this.orderNo = orderNo;
|
||||
this.loc = point;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.mogo.och.taxi.passenger.bean
|
||||
|
||||
class TaxiPassengerVideoPlay(var url: String, var imageUrl: String, var title: String)
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.mogo.och.taxi.passenger.callback
|
||||
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrderQueryRespBean
|
||||
|
||||
/**
|
||||
* Created on 2021/9/8
|
||||
*
|
||||
* Model->Presenter回调:订单相关(进行中/待服务单变更,当前进行单状态变更,新到预约单,抢单,抢单结果状态等等)
|
||||
*/
|
||||
interface IOCHTaxiPassengerOrderStatusCallback {
|
||||
// 当前进行单状态变更:新到进行中订单、进行中单状态变更
|
||||
fun onCurrentOrderStatusChanged(order: TaxiPassengerOrderQueryRespBean.Result?){}
|
||||
|
||||
// 当前位置距离上车点的距离(米)、预估时间(秒)
|
||||
fun onCurrentOrderDistToEndChanged(meters: Long, timeInSecond: Long,stationDistance:Int){}
|
||||
|
||||
// 司机已确认开启自动驾驶环境
|
||||
fun onDriverHasCheckedPilotCondition(isBoarded: Boolean){}
|
||||
|
||||
fun onMessageGo2OverMapview(){}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.mogo.och.taxi.passenger.callback;
|
||||
|
||||
public interface ITaxiPassengerCommonCallback {
|
||||
void onCommonCallback();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.mogo.och.taxi.passenger.callback;
|
||||
|
||||
public interface ITaxiPassengerCommonValueCallback<T> {
|
||||
void onCommonCallback(T t);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.mogo.och.taxi.passenger.constant
|
||||
|
||||
/**
|
||||
* Created on 2021/12/6
|
||||
*/
|
||||
class TaxiPassengerConst {
|
||||
companion object {
|
||||
|
||||
// OCH arouter 路由path
|
||||
const val PATH = "/passenger/api"
|
||||
|
||||
// 开始服务启动自动驾驶等待时间(埋点上传)
|
||||
const val LOOP_PERIOD_15S = 15 * 1000L
|
||||
|
||||
const val TAXI_AVERAGE_SPEED = 38
|
||||
|
||||
|
||||
// 埋点key:接管后点击'自动驾驶'按钮启动
|
||||
const val EVENT_KEY_RESTART_AUTOPILOT = "event_key_och_taxi_restart_autopilot"
|
||||
// 埋点key:开始服务开启自动驾驶(成功/失败)
|
||||
const val EVENT_KEY_START_SERVICE = "event_key_och_taxi_start_service"
|
||||
const val EVENT_PARAM_SN = "sn"
|
||||
const val EVENT_PARAM_TIME = "time"
|
||||
const val EVENT_PARAM_START_NAME = "start_name"
|
||||
const val EVENT_PARAM_END_NAME = "end_name"
|
||||
const val EVENT_PARAM_ORDER_NUMBER = "order_num"
|
||||
const val EVENT_PARAM_START_RESULT = "start_autopilot" // true/false
|
||||
const val EVENT_PARAM_START_FAILURE_CODE = "start_autopilot_failure_code" // 启动自驾失败code
|
||||
const val EVENT_PARAM_START_FAILURE_MSG = "start_autopilot_failure_msg" // 启动自驾失败原因
|
||||
const val EVENT_PARAM_PLATE_NUM = "plate_number" // 车牌号
|
||||
const val EVENT_PARAM_ENV_ONLINE = "env_online" // 是否线上环境:true/false
|
||||
|
||||
// 埋点key:开启自动驾驶前已识别的异常,会导致无法开启自驾
|
||||
const val EVENT_KEY_AP_UNABLE_START_REASON = "event_key_och_taxi_ap_unable_start_reason"
|
||||
const val EVENT_PARAM_UNABLE_START_REASON = "unable_start_reason";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.mogo.och.taxi.passenger.constant
|
||||
|
||||
/**
|
||||
* Created on 2021/12/7
|
||||
*
|
||||
* * Old code:START
|
||||
* 未派单 0
|
||||
* 去往上车站点 1
|
||||
* 车辆已到达上车站点 2
|
||||
* 乘客已到达上车站点 3
|
||||
* 去往下车站点 4
|
||||
* 到达下车站点 5
|
||||
* 已完成 6
|
||||
* 已取消 7
|
||||
* Old code:END
|
||||
*
|
||||
* 0 订单创建(为派单),
|
||||
* 10 已派上司机(司机去往上车点),
|
||||
* 20 司机到达上车点,
|
||||
* 30 乘客到达上车点,
|
||||
* 40 服务中(去往目的地),
|
||||
* 50 到达目的地,
|
||||
* 60 已完成,
|
||||
* 70 已取消
|
||||
*/
|
||||
enum class TaxiPassengerOrderStatusEnum(val code: Int) {
|
||||
None( 0 ),
|
||||
OnTheWayToStart( 10),
|
||||
ArriveAtStart( 20),
|
||||
UserArriveAtStart( 30),
|
||||
OnTheWayToEnd( 40),
|
||||
ArriveAtEnd( 50),
|
||||
JourneyCompleted(60),//行程完成
|
||||
Cancel( 70);
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun valueOf(code: Int): TaxiPassengerOrderStatusEnum {
|
||||
for (value in values()) {
|
||||
if (value.code == code) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package com.mogo.och.taxi.passenger.model
|
||||
|
||||
import com.elegant.network.utils.GsonUtil
|
||||
import com.mogo.commons.voice.AIAssist
|
||||
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters
|
||||
import com.mogo.eagle.core.data.config.FunctionBuildConfig
|
||||
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationGCJ02ListenerManager
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
|
||||
import com.mogo.eagle.core.utilcode.util.ToastUtils
|
||||
import com.mogo.och.common.module.biz.network.OchCommonServiceCallback
|
||||
import com.mogo.och.common.module.manager.OCHAdasAbilityManager
|
||||
import com.mogo.och.common.module.utils.PinYinUtil
|
||||
import com.mogo.och.common.module.voice.VoiceNotice
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerBaseRespBean
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerStartReqBean
|
||||
import com.mogo.och.taxi.passenger.constant.TaxiPassengerOrderStatusEnum
|
||||
import com.mogo.och.taxi.passenger.network.TaxiPassengerServiceManager
|
||||
import com.mogo.och.taxi.passenger.utils.TaxiPassengerAnalyticsManager
|
||||
|
||||
object AutopilotManager : IMoGoAutopilotStatusListener {
|
||||
|
||||
private const val TAG = "AutopilotManager"
|
||||
|
||||
init {
|
||||
CallerAutoPilotStatusListenerManager.addListener(TAG, this)
|
||||
}
|
||||
//检测当前订单
|
||||
private fun checkCurrentOCHOrder(): Boolean {
|
||||
return TaxiPassengerModel.currentOCHOrder != null && TaxiPassengerModel.currentOCHOrder!!.startSiteGcjPoint != null && TaxiPassengerModel.currentOCHOrder!!.endSiteGcjPoint != null
|
||||
}
|
||||
|
||||
|
||||
fun startAutopilot() {
|
||||
if (!checkCurrentOCHOrder()) {
|
||||
CallerLogger.e(
|
||||
SceneConstant.M_TAXI_P + TAG,
|
||||
"no order or order is empty."
|
||||
)
|
||||
ToastUtils.showShort("当前订单不存在或异常!")
|
||||
return
|
||||
}
|
||||
if (TaxiPassengerModel.currentOCHOrder!!.orderStatus == TaxiPassengerOrderStatusEnum.UserArriveAtStart.code) {
|
||||
startServicePilotDone()
|
||||
}
|
||||
if(CallerAutoPilotStatusListenerManager.getState()
|
||||
== IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING){
|
||||
ToastUtils.showShort("自驾中、请勿重复启动")
|
||||
return
|
||||
}
|
||||
if (!FunctionBuildConfig.isDemoMode && !OCHAdasAbilityManager.getInstance().autopilotAbilityStatus) {
|
||||
ToastUtils.showLong(
|
||||
OCHAdasAbilityManager.getInstance().autopilotUnAbilityReason +
|
||||
", 请稍候重试"
|
||||
)
|
||||
TaxiPassengerAnalyticsManager.triggerUnableStartAPReasonEvent(
|
||||
TaxiPassengerModel.currentOCHOrder!!.startSiteAddr,
|
||||
TaxiPassengerModel.currentOCHOrder!!.endSiteAddr,
|
||||
TaxiPassengerModel.currentOCHOrder!!.orderNo,
|
||||
OCHAdasAbilityManager.getInstance().autopilotUnAbilityReason
|
||||
)
|
||||
return
|
||||
}
|
||||
val parameters = initAutopilotControlParameters()
|
||||
if (parameters == null) {
|
||||
CallerLogger.d(
|
||||
SceneConstant.M_TAXI_P + TAG,
|
||||
"AutopilotControlParameters is empty."
|
||||
)
|
||||
return
|
||||
}
|
||||
CallerAutoPilotControlManager.startAutoPilot(parameters)
|
||||
CallerLogger.d(
|
||||
SceneConstant.M_TAXI_P + TAG,
|
||||
"start autopilot with parameter: %s",
|
||||
GsonUtil.jsonFromObject(parameters)
|
||||
+ " ,startSiteName=" + TaxiPassengerModel.currentOCHOrder!!.startSiteAddr
|
||||
+ " ,endSiteName=" + TaxiPassengerModel.currentOCHOrder!!.endSiteAddr
|
||||
)
|
||||
TaxiPassengerAnalyticsManager.triggerStartAutopilotEvent(false, false, TaxiPassengerModel.currentOCHOrder!!.startSiteAddr, TaxiPassengerModel.currentOCHOrder!!.endSiteAddr, TaxiPassengerModel.currentOCHOrder!!.orderNo)
|
||||
}
|
||||
|
||||
private fun initAutopilotControlParameters(): AutopilotControlParameters? {
|
||||
if (!checkCurrentOCHOrder()) {
|
||||
CallerLogger.e(
|
||||
SceneConstant.M_TAXI_P + TAG,
|
||||
"no order or order is empty."
|
||||
)
|
||||
ToastUtils.showShort("当前订单不存在或异常!")
|
||||
return null
|
||||
}
|
||||
val parameters = AutopilotControlParameters()
|
||||
val startWgsLon = TaxiPassengerModel.currentOCHOrder!!.startSitePoint[0]
|
||||
val startWgsLat = TaxiPassengerModel.currentOCHOrder!!.startSitePoint[1]
|
||||
val endWgsLon = TaxiPassengerModel.currentOCHOrder!!.endSitePoint[0]
|
||||
val endWgsLat = TaxiPassengerModel.currentOCHOrder!!.endSitePoint[1]
|
||||
parameters.vehicleType = TaxiPassengerModel.currentOCHOrder!!.businessType
|
||||
parameters.startName =
|
||||
PinYinUtil.getPinYinHeadChar(TaxiPassengerModel.currentOCHOrder!!.startSiteAddr) // 起点名称拼音首字母大写:科学城B区2号门(KXCBQ2HM)
|
||||
parameters.endName =
|
||||
PinYinUtil.getPinYinHeadChar(TaxiPassengerModel.currentOCHOrder!!.endSiteAddr) // 终点名称拼音首字母大写:科学城C区三号门(KXCCQSHM)
|
||||
parameters.startLatLon =
|
||||
AutopilotControlParameters.AutoPilotLonLat(startWgsLat, startWgsLon)
|
||||
parameters.endLatLon = AutopilotControlParameters.AutoPilotLonLat(endWgsLat, endWgsLon)
|
||||
if (parameters.autoPilotLine == null) {
|
||||
parameters.autoPilotLine = AutopilotControlParameters.AutoPilotLine(
|
||||
TaxiPassengerModel.currentOCHOrder!!.lineId,
|
||||
TaxiPassengerModel.currentOCHOrder!!.csvFileUrl,
|
||||
TaxiPassengerModel.currentOCHOrder!!.csvFileMd5,
|
||||
TaxiPassengerModel.currentOCHOrder!!.txtFileUrl,
|
||||
TaxiPassengerModel.currentOCHOrder!!.txtFileMd5,
|
||||
TaxiPassengerModel.currentOCHOrder!!.contrailSaveTime,
|
||||
TaxiPassengerModel.currentOCHOrder!!.carModel,
|
||||
TaxiPassengerModel.currentOCHOrder!!.csvFileUrlDPQP,
|
||||
TaxiPassengerModel.currentOCHOrder!!.csvFileMd5DPQP,
|
||||
TaxiPassengerModel.currentOCHOrder!!.txtFileUrlDPQP,
|
||||
TaxiPassengerModel.currentOCHOrder!!.txtFileMd5DPQP,
|
||||
TaxiPassengerModel.currentOCHOrder!!.contrailSaveTimeDPQP
|
||||
)
|
||||
}
|
||||
return parameters
|
||||
}
|
||||
|
||||
/**
|
||||
* 将业务订单信息保存,鹰眼可取用
|
||||
*/
|
||||
fun updateAutopilotControlParameters() {
|
||||
val parameters = initAutopilotControlParameters()
|
||||
if (null == parameters) {
|
||||
CallerLogger.e(
|
||||
SceneConstant.M_TAXI_P + TAG,
|
||||
"AutopilotControlParameters is empty."
|
||||
)
|
||||
return
|
||||
}
|
||||
CallerLogger.d(
|
||||
SceneConstant.M_TAXI_P + TAG,
|
||||
"AutopilotControlParameters is update."
|
||||
)
|
||||
CallerAutoPilotStatusListenerManager.updateAutopilotControlParameters(parameters)
|
||||
}
|
||||
|
||||
fun clearAutopilotControlParameters() {
|
||||
CallerLogger.d(
|
||||
SceneConstant.M_TAXI_P + TAG,
|
||||
"AutopilotControlParameters is clear."
|
||||
)
|
||||
CallerAutoPilotStatusListenerManager.updateAutopilotControlParameters(null)
|
||||
}
|
||||
|
||||
override fun onAutopilotStatusResponse(state: Int) {
|
||||
// 启动自驾成功
|
||||
when (state) {
|
||||
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING -> {
|
||||
if (TaxiPassengerModel.currentOCHOrder != null && TaxiPassengerModel.curOrderStatus === TaxiPassengerOrderStatusEnum.UserArriveAtStart) {
|
||||
TaxiPassengerAnalyticsManager.triggerStartAutopilotEvent(
|
||||
false,
|
||||
true,
|
||||
TaxiPassengerModel.currentOCHOrder!!.startSiteAddr,
|
||||
TaxiPassengerModel.currentOCHOrder!!.endSiteAddr,
|
||||
TaxiPassengerModel.currentOCHOrder!!.orderNo
|
||||
)
|
||||
startServicePilotDone()
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 乘客屏启动自动驾驶成功
|
||||
*/
|
||||
fun startServicePilotDone() {
|
||||
if (TaxiPassengerModel.currentOCHOrder == null) return
|
||||
val result = TaxiPassengerStartReqBean.Result()
|
||||
val currentLocation = CallerChassisLocationGCJ02ListenerManager.getChassisLocationGCJ02()
|
||||
result.lat = currentLocation.latitude
|
||||
result.lon = currentLocation.longitude
|
||||
TaxiPassengerServiceManager.startServicePilotDone(
|
||||
TaxiPassengerModel.currentOCHOrder!!.orderNo, result,
|
||||
object : OchCommonServiceCallback<TaxiPassengerBaseRespBean> {
|
||||
override fun onSuccess(data: TaxiPassengerBaseRespBean) {
|
||||
VoiceNotice.showNotice("坐稳扶好,我们出发咯!", AIAssist.LEVEL2)
|
||||
}
|
||||
override fun onFail(code: Int, msg: String) {}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
package com.mogo.och.taxi.passenger.model
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import com.mogo.aicloud.services.socket.MogoAiCloudSocketManager
|
||||
import com.mogo.commons.module.intent.IMogoIntentListener
|
||||
import com.mogo.commons.module.intent.IntentManager
|
||||
import com.mogo.commons.voice.AIAssist
|
||||
import com.mogo.eagle.core.data.map.MogoLocation
|
||||
import com.mogo.eagle.core.function.api.datacenter.msgbox.IMsgBoxEventListener
|
||||
import com.mogo.eagle.core.function.call.biz.CallerFuncBizManager
|
||||
import com.mogo.eagle.core.function.call.msgbox.CallerMsgBoxEventListenerManager
|
||||
import com.mogo.eagle.core.function.call.order.CallerOrderListenerManager
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.e
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_TAXI_P
|
||||
import com.mogo.eagle.core.utilcode.util.NetworkUtils
|
||||
import com.mogo.eagle.core.utilcode.util.ToastUtils
|
||||
import com.mogo.och.common.module.biz.network.OchCommonServiceCallback
|
||||
import com.mogo.och.common.module.callback.OchAdasStartFailureCallback
|
||||
import com.mogo.och.common.module.manager.AbnormalFactorsLoopManager
|
||||
import com.mogo.och.common.module.manager.OCHAdasAbilityManager
|
||||
import com.mogo.och.common.module.manager.distancemamager.IDistanceListener
|
||||
import com.mogo.och.common.module.manager.distancemamager.TrajectoryAndDistanceManager
|
||||
import com.mogo.och.common.module.manager.loopmanager.BizLoopManager
|
||||
import com.mogo.och.common.module.manager.loopmanager.LoopInfo
|
||||
import com.mogo.och.common.module.utils.RxUtils
|
||||
import com.mogo.och.common.module.voice.VoiceNotice
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerBaseRespBean
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrderQueryRespBean
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrdersInServiceQueryRespBean
|
||||
import com.mogo.och.taxi.passenger.callback.IOCHTaxiPassengerOrderStatusCallback
|
||||
import com.mogo.och.taxi.passenger.callback.ITaxiPassengerCommonCallback
|
||||
import com.mogo.och.taxi.passenger.constant.TaxiPassengerConst
|
||||
import com.mogo.och.taxi.passenger.constant.TaxiPassengerOrderStatusEnum
|
||||
import com.mogo.och.taxi.passenger.constant.TaxiPassengerOrderStatusEnum.Companion.valueOf
|
||||
import com.mogo.och.taxi.passenger.network.TaxiPassengerServiceManager
|
||||
import com.mogo.och.taxi.passenger.utils.TaxiPassengerAnalyticsManager
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* Created by pangfan on 2021/8/19
|
||||
*
|
||||
* 网约车 - 出租车业务逻辑处理
|
||||
*/
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object TaxiPassengerModel {
|
||||
|
||||
private var mContext: Context? = null
|
||||
|
||||
private val TAG = TaxiPassengerModel::class.java.simpleName
|
||||
|
||||
private const val STARTREADYTOAUTOPILOT = "startReadyToAutopilot"
|
||||
private const val MINANDWAITSERVICE = "mInAndWaitService"
|
||||
|
||||
// 获取当前订单
|
||||
@Volatile
|
||||
var currentOCHOrder: TaxiPassengerOrderQueryRespBean.Result? = null//当前订单
|
||||
|
||||
private val mOrderStatusCallbackMap: MutableMap<String, IOCHTaxiPassengerOrderStatusCallback> = ConcurrentHashMap()
|
||||
|
||||
fun setOrderStatusCallback(tag: String?, callback: IOCHTaxiPassengerOrderStatusCallback?) {
|
||||
if (tag == null || "" == tag) return
|
||||
if (callback == null) {
|
||||
mOrderStatusCallbackMap.remove(tag)
|
||||
return
|
||||
}
|
||||
mOrderStatusCallbackMap[tag] = callback
|
||||
}
|
||||
|
||||
fun init(context: Context) {
|
||||
mContext = context.applicationContext
|
||||
initListeners()
|
||||
RxUtils.errCatch()
|
||||
startOrStopOrderLoop()
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单轮询 初始化主Fragment的Presenter init 调用
|
||||
*/
|
||||
private fun startOrStopOrderLoop() {
|
||||
if (NetworkUtils.isConnected(mContext)) {
|
||||
startOrStopOrderLoop(true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭订单轮训 页面摧毁时
|
||||
*/
|
||||
fun release() {
|
||||
startOrStopOrderLoop(false)
|
||||
//startOrStopQueryOrderRemaining(false)
|
||||
releaseListeners()
|
||||
}
|
||||
|
||||
private fun initListeners() {
|
||||
|
||||
// 2021.11.1重构自动驾驶 实现接口 IMoGoAutopilotStatusListener 注册监听 替换IMogoAdasOCHCallback接口
|
||||
IntentManager.getInstance().registerIntentListener(ConnectivityManager.CONNECTIVITY_ACTION, mNetWorkIntentListener)
|
||||
|
||||
AbnormalFactorsLoopManager.startLoopAbnormalFactors(mContext!!)
|
||||
|
||||
//开启自驾后 异常信息返回
|
||||
OCHAdasAbilityManager.getInstance().setAdasStartFailureCallback(mAdasStartFailureListener)
|
||||
CallerMsgBoxEventListenerManager.addListener(TAG, iMsgBoxEventListener)
|
||||
TrajectoryAndDistanceManager.addDistanceListener(TAG, distanceListener)
|
||||
}
|
||||
|
||||
private fun releaseListeners() {
|
||||
MogoAiCloudSocketManager.getInstance(mContext).unregisterLifecycleListener(10010)
|
||||
AbnormalFactorsLoopManager.stopLoopAbnormalFactors()
|
||||
OCHAdasAbilityManager.getInstance().setAdasStartFailureCallback(null)
|
||||
CallerMsgBoxEventListenerManager.removeListener(iMsgBoxEventListener)
|
||||
TrajectoryAndDistanceManager.removeListener(TAG)
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单轮训
|
||||
* @param start true 开启订单轮训
|
||||
* false 关闭订单轮训
|
||||
*/
|
||||
private fun startOrStopOrderLoop(start: Boolean) {
|
||||
d(M_TAXI_P + TAG, "startOrStopOrderLoop() $start")
|
||||
if (start) {
|
||||
BizLoopManager.setLoopFunction(MINANDWAITSERVICE,LoopInfo(2, TaxiPassengerModel::queryInAndWaitOrders))
|
||||
} else {
|
||||
BizLoopManager.removeLoopFunction(MINANDWAITSERVICE)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询订单状态:进行中/待服务(轮询防止因crash导致应用重启、断网没收到推送等)
|
||||
*
|
||||
* 注:只有在本地缓存mCurrentOCHOrder为null时(已完成or已取消有明确结果)或id相同且status不同时,
|
||||
* 才更新最新进行中单到本地
|
||||
*/
|
||||
private fun queryInAndWaitOrders() {
|
||||
TaxiPassengerServiceManager.queryOrdersInAndWaitService(
|
||||
object : OchCommonServiceCallback<TaxiPassengerOrdersInServiceQueryRespBean> {
|
||||
override fun onSuccess(data: TaxiPassengerOrdersInServiceQueryRespBean) {
|
||||
if (data.data == null) {
|
||||
if(currentOCHOrder!=null){
|
||||
queryCurOrderStatus()
|
||||
}
|
||||
return
|
||||
}
|
||||
//1. 处理进行中订单
|
||||
if (data.data.servicing != null && data.data.servicing.isNotEmpty()) {
|
||||
// 1.1. 当存在进行中单时:对本地currentOrder进行更新
|
||||
val currentOrder = data.data.servicing[0]
|
||||
if(currentOCHOrder==null||currentOCHOrder?.orderStatus!=currentOrder.orderStatus){
|
||||
currentOCHOrder = currentOrder
|
||||
orderStatusChange()
|
||||
}else {
|
||||
currentOCHOrder = currentOrder
|
||||
}
|
||||
}else{
|
||||
if(currentOCHOrder!=null){
|
||||
queryCurOrderStatus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError() {
|
||||
e(M_TAXI_P + TAG, "queryInAndWaitOrders onError")
|
||||
}
|
||||
override fun onFail(code: Int, msg: String) {
|
||||
e(M_TAXI_P + TAG, "queryInAndWaitOrders$code$msg")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//仅用于轮询时查到本地有mCurrentOCHOrder但请求结果无进行中单or orderId不一致是复查本地currentOrder
|
||||
private fun queryCurOrderStatus() {
|
||||
currentOCHOrder?.orderNo?.let {
|
||||
TaxiPassengerServiceManager.queryOrderById(
|
||||
mContext!!, it,
|
||||
object : OchCommonServiceCallback<TaxiPassengerOrderQueryRespBean> {
|
||||
override fun onSuccess(data: TaxiPassengerOrderQueryRespBean) {
|
||||
if (data.data != null && currentOCHOrder != null && currentOCHOrder!!.orderNo == data.data.orderNo) {
|
||||
if (data.data.orderStatus == TaxiPassengerOrderStatusEnum.Cancel.code || data.data.orderStatus == TaxiPassengerOrderStatusEnum.JourneyCompleted.code || data.data.orderStatus == TaxiPassengerOrderStatusEnum.None.code) {
|
||||
currentOCHOrder = data.data
|
||||
orderStatusChange()
|
||||
currentOCHOrder = null
|
||||
} else {
|
||||
currentOCHOrder = data.data
|
||||
orderStatusChange()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFail(code: Int, msg: String) {}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前订单状态
|
||||
val curOrderStatus: TaxiPassengerOrderStatusEnum
|
||||
get() {
|
||||
val order: TaxiPassengerOrderQueryRespBean.Result =
|
||||
currentOCHOrder
|
||||
?: return TaxiPassengerOrderStatusEnum.None
|
||||
return valueOf(order.orderStatus)
|
||||
}
|
||||
|
||||
//监听网络变化,避免启动机器时无网导致无法更新订单信息
|
||||
private val distanceListener: IDistanceListener = object : IDistanceListener {
|
||||
var allDistance = 0f
|
||||
override fun stationDistanceCallback(stationDistance: Float) {
|
||||
allDistance = stationDistance
|
||||
}
|
||||
|
||||
override fun distanceCallback(distance: Float) {
|
||||
|
||||
val lastTime: Double = distance / TaxiPassengerConst.TAXI_AVERAGE_SPEED * 3.6 //秒
|
||||
|
||||
for (callback in mOrderStatusCallbackMap.values) {
|
||||
callback.onCurrentOrderDistToEndChanged(distance.toLong(),lastTime.toLong(),allDistance.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
private val mNetWorkIntentListener = IMogoIntentListener { intentStr, intent ->
|
||||
d(M_TAXI_P + TAG, "onIntentReceived = %s", intentStr)
|
||||
if (ConnectivityManager.CONNECTIVITY_ACTION == intentStr) {
|
||||
if (NetworkUtils.isConnected(mContext)) {
|
||||
startOrStopOrderLoop(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val mAdasStartFailureListener: OchAdasStartFailureCallback = object : OchAdasStartFailureCallback {
|
||||
override fun onStartAutopilotFailure(startFailedCode: String, startFailedMessage: String) {
|
||||
TaxiPassengerAnalyticsManager.triggerStartAutopilotFailureEventByAdas(startFailedCode, startFailedMessage)
|
||||
}
|
||||
}
|
||||
private val iMsgBoxEventListener: IMsgBoxEventListener = object : IMsgBoxEventListener {
|
||||
override fun onSummaryClickEvent() {
|
||||
if (currentOCHOrder == null) {
|
||||
ToastUtils.showLong("行程已结束")
|
||||
} else {
|
||||
for (callback in mOrderStatusCallbackMap.values) {
|
||||
callback.onMessageGo2OverMapview()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun checkPhoneAndUpdateStatus(
|
||||
phoneTail: String?,
|
||||
commonCallback: ITaxiPassengerCommonCallback?
|
||||
) {
|
||||
if (currentOCHOrder == null) return
|
||||
TaxiPassengerServiceManager.checkPhoneAndUpdateOrderStatus(
|
||||
currentOCHOrder!!.orderNo,
|
||||
phoneTail, object : OchCommonServiceCallback<TaxiPassengerBaseRespBean> {
|
||||
override fun onSuccess(data: TaxiPassengerBaseRespBean) {
|
||||
if (data.code == 0 && currentOCHOrder != null) {
|
||||
currentOCHOrder!!.orderStatus = TaxiPassengerOrderStatusEnum.UserArriveAtStart.code
|
||||
//乘客验证成功,更新订单状态为 "乘客已上车", 立马弹出乘客开始行程页面,不再等待轮询
|
||||
orderStatusChange()
|
||||
VoiceNotice.showNotice("验证成功!关闭车门并佩戴安全带后开启行程吧!", AIAssist.LEVEL2)
|
||||
}
|
||||
commonCallback?.onCommonCallback()
|
||||
}
|
||||
|
||||
override fun onFail(code: Int, msg: String) {
|
||||
ToastUtils.showLong("当前网络异常,请重新验证;若始终异常,请您在手机端取消行程,给您带来不便,十分抱歉!")
|
||||
e(
|
||||
M_TAXI_P + TAG,
|
||||
"提交用户输入的手机后4位、并进行状态扭转 后台结果错误$code$msg"
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun orderStatusChange(){
|
||||
orderStatusChangeInner()
|
||||
if (mOrderStatusCallbackMap.isNotEmpty()) {
|
||||
d(M_TAXI_P + TAG, "最新的状态${curOrderStatus}")
|
||||
for (callback in mOrderStatusCallbackMap.values) {
|
||||
callback.onCurrentOrderStatusChanged(currentOCHOrder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun orderStatusChangeInner() {
|
||||
when (curOrderStatus) {
|
||||
TaxiPassengerOrderStatusEnum.OnTheWayToStart -> {
|
||||
}
|
||||
TaxiPassengerOrderStatusEnum.ArriveAtStart -> {
|
||||
}
|
||||
TaxiPassengerOrderStatusEnum.UserArriveAtStart -> {
|
||||
//开启轮询司机是否已准备好开启自动驾驶的环境
|
||||
setStation()
|
||||
}
|
||||
TaxiPassengerOrderStatusEnum.OnTheWayToEnd -> {
|
||||
CallerFuncBizManager.bizProvider.queryV2XEvents() //全览模式的V2X事件轮询开始
|
||||
//startOrStopQueryOrderRemaining(true)
|
||||
AutopilotManager.updateAutopilotControlParameters()
|
||||
startOrStopReadyToAutopilotLoop(false)
|
||||
setStation()
|
||||
CallerOrderListenerManager.invokeOrderStatus(true)
|
||||
}
|
||||
TaxiPassengerOrderStatusEnum.ArriveAtEnd -> {
|
||||
AutopilotManager.clearAutopilotControlParameters()
|
||||
//startOrStopQueryOrderRemaining(false)
|
||||
CallerOrderListenerManager.invokeOrderStatus(false)
|
||||
cleanStation()
|
||||
}
|
||||
TaxiPassengerOrderStatusEnum.JourneyCompleted -> {
|
||||
AutopilotManager.clearAutopilotControlParameters()
|
||||
//startOrStopQueryOrderRemaining(false)
|
||||
cleanStation()
|
||||
}
|
||||
TaxiPassengerOrderStatusEnum.Cancel -> {
|
||||
AutopilotManager.clearAutopilotControlParameters()
|
||||
//startOrStopQueryOrderRemaining(false)
|
||||
startOrStopReadyToAutopilotLoop(false)
|
||||
cleanStation()
|
||||
}
|
||||
TaxiPassengerOrderStatusEnum.None -> TODO()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询司机是否已确认可开启自动驾驶
|
||||
*/
|
||||
private fun loopQueryPilotStatus() {
|
||||
if (currentOCHOrder == null) return
|
||||
TaxiPassengerServiceManager.queryPilotStatus(
|
||||
currentOCHOrder!!.orderNo,
|
||||
object : OchCommonServiceCallback<TaxiPassengerBaseRespBean> {
|
||||
override fun onSuccess(data: TaxiPassengerBaseRespBean) {
|
||||
if (data.code == 0 && data.data == true) {
|
||||
updateAutopilotStatus(true)
|
||||
startOrStopReadyToAutopilotLoop(false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFail(code: Int, msg: String) {
|
||||
updateAutopilotStatus(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun updateAutopilotStatus(isBoarded: Boolean) {
|
||||
if (mOrderStatusCallbackMap.isNotEmpty()) {
|
||||
for (callback in mOrderStatusCallbackMap.values) {
|
||||
callback.onDriverHasCheckedPilotCondition(isBoarded)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun startOrStopReadyToAutopilotLoop(isStart: Boolean) {
|
||||
if (isStart) {
|
||||
BizLoopManager.setLoopFunction(STARTREADYTOAUTOPILOT, LoopInfo(1, TaxiPassengerModel::loopQueryPilotStatus))
|
||||
CallerLogger.i(M_TAXI_P + TAG, "startReadyToAutopilot()")
|
||||
} else {
|
||||
BizLoopManager.removeLoopFunction(STARTREADYTOAUTOPILOT)
|
||||
CallerLogger.i(M_TAXI_P + TAG, "stopReadyToAutopilot()")
|
||||
}
|
||||
}
|
||||
|
||||
fun setStation() {
|
||||
if (currentOCHOrder != null) {
|
||||
val startStation = MogoLocation()
|
||||
startStation.longitude = currentOCHOrder!!.startSiteGcjPoint[0]
|
||||
startStation.latitude = currentOCHOrder!!.startSiteGcjPoint[1]
|
||||
val endStation = MogoLocation()
|
||||
endStation.longitude = currentOCHOrder!!.endSiteGcjPoint[0]
|
||||
endStation.latitude = currentOCHOrder!!.endSiteGcjPoint[1]
|
||||
TrajectoryAndDistanceManager.setStationPoint(startStation, endStation, currentOCHOrder!!.lineId)
|
||||
}
|
||||
}
|
||||
|
||||
fun cleanStation() {
|
||||
TrajectoryAndDistanceManager.setStationPoint(null, null, -1L)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.mogo.och.taxi.passenger.network
|
||||
|
||||
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerBaseRespBean
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerCheckPhoneUpdateOrderReqBean
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrderQueryReqBean
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrderQueryRespBean
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrdersInServiceQueryRespBean
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerStartReqBean
|
||||
import io.reactivex.Observable
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.Headers
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Query
|
||||
|
||||
/**
|
||||
* Created by pangfan on 2021/8/19
|
||||
*
|
||||
* 网约车-出租车接口定义
|
||||
*/
|
||||
internal interface TaxiPassengerServiceApi {
|
||||
/**
|
||||
* 查询全部服务中/待服务订单(没有的时候返回code 0,空列表)
|
||||
* @param driverSn
|
||||
* @return
|
||||
*/
|
||||
@Headers("Content-type:application/json;charset=UTF-8")
|
||||
@GET("/autopilot-car-hailing/order/v2/driver/taxi/passenger/orderInService/query")
|
||||
fun queryOrdersInAndWaitService(
|
||||
@Header("appId") appId: String = MoGoAiCloudClientConfig.getInstance().serviceAppId,
|
||||
@Header("ticket") ticket: String=MoGoAiCloudClientConfig.getInstance().token,
|
||||
@Query("driverSn") driverSn: String
|
||||
): Observable<TaxiPassengerOrdersInServiceQueryRespBean>
|
||||
|
||||
/**
|
||||
* 通过orderNo查询订单信息(用于本地已经有orderNo时)
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
@Headers("Content-type:application/json;charset=UTF-8")
|
||||
@POST("/autopilot-car-hailing/order/v2/driver/taxi/passenger/queryOrderById")
|
||||
fun queryOrderById(
|
||||
@Header("appId") appId: String = MoGoAiCloudClientConfig.getInstance().serviceAppId,
|
||||
@Header("ticket") ticket: String=MoGoAiCloudClientConfig.getInstance().token,
|
||||
@Body data: TaxiPassengerOrderQueryReqBean
|
||||
): Observable<TaxiPassengerOrderQueryRespBean>
|
||||
|
||||
/**
|
||||
* 提交用户输入的手机后4位、并进行状态扭转
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
@Headers("Content-type:application/json;charset=UTF-8")
|
||||
@POST("/autopilot-car-hailing/cab/flow/v1/driver/taxi/passenger/verification/phone")
|
||||
fun checkPhoneAndUpdateOrderStatus(
|
||||
@Header("appId") appId: String = MoGoAiCloudClientConfig.getInstance().serviceAppId,
|
||||
@Header("ticket") ticket: String=MoGoAiCloudClientConfig.getInstance().token,
|
||||
@Body data: TaxiPassengerCheckPhoneUpdateOrderReqBean?
|
||||
): Observable<TaxiPassengerBaseRespBean>
|
||||
|
||||
/**
|
||||
* 查询司机是否已确认可开启自动驾驶
|
||||
* @param appId
|
||||
* @param ticket
|
||||
* @param orderNo
|
||||
* @return
|
||||
*/
|
||||
@Headers("Content-type:application/json;charset=UTF-8")
|
||||
@GET("/autopilot-car-hailing/cab/flow/v1/driver/taxi/pilot/status")
|
||||
fun queryPilotStatus(
|
||||
@Header("appId") appId: String = MoGoAiCloudClientConfig.getInstance().serviceAppId,
|
||||
@Header("ticket") ticket: String=MoGoAiCloudClientConfig.getInstance().token,
|
||||
@Query("orderNo") orderNo: String
|
||||
): Observable<TaxiPassengerBaseRespBean>
|
||||
|
||||
/**
|
||||
* 乘客屏启动自动驾驶成功
|
||||
* @param appId
|
||||
* @param ticket
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
@Headers("Content-type:application/json;charset=UTF-8")
|
||||
@POST("/autopilot-car-hailing/cab/flow/v1/driver/taxi/passenger/startServicePilot")
|
||||
fun startServicePilotDone(
|
||||
@Header("appId") appId: String = MoGoAiCloudClientConfig.getInstance().serviceAppId,
|
||||
@Header("ticket") ticket: String= MoGoAiCloudClientConfig.getInstance().token,
|
||||
@Body data: TaxiPassengerStartReqBean
|
||||
): Observable<TaxiPassengerBaseRespBean>
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.mogo.och.taxi.passenger.network
|
||||
|
||||
import android.content.Context
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrdersInServiceQueryRespBean
|
||||
import com.mogo.commons.AbsMogoApplication
|
||||
import com.mogo.eagle.core.function.call.telematic.CallerTelematicManager
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerBaseRespBean
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerCheckPhoneUpdateOrderReqBean
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerStartReqBean
|
||||
import com.mogo.eagle.core.network.MoGoRetrofitFactory
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_TAXI_P
|
||||
import com.mogo.och.common.module.biz.constant.OchCommonConst
|
||||
import com.mogo.och.common.module.biz.network.OchCommonServiceCallback
|
||||
import com.mogo.och.common.module.biz.network.OchCommonSubscribeImpl
|
||||
import com.mogo.och.common.module.biz.network.interceptor.transformTry
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrderQueryReqBean
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrderQueryRespBean
|
||||
|
||||
/**
|
||||
* Created by pangfan on 2021/8/19
|
||||
*/
|
||||
object TaxiPassengerServiceManager {
|
||||
|
||||
private val mOCHTaxiServiceApi: TaxiPassengerServiceApi =
|
||||
MoGoRetrofitFactory.getInstance(OchCommonConst.getBaseUrl()).create(TaxiPassengerServiceApi::class.java)
|
||||
|
||||
private const val TAG = "TaxiPassengerServiceManager"
|
||||
|
||||
private var draiverSnCacher = ""
|
||||
|
||||
/**
|
||||
* 获取Bus司机端的sn
|
||||
* @return
|
||||
*/
|
||||
val draiverSn: String
|
||||
get(){
|
||||
val serverToken = CallerTelematicManager.getServerToken()
|
||||
if (serverToken != draiverSnCacher && serverToken.isNotEmpty()) {
|
||||
draiverSnCacher = serverToken
|
||||
}
|
||||
return draiverSnCacher
|
||||
}
|
||||
val context:Context
|
||||
get() {
|
||||
return AbsMogoApplication.getApp()
|
||||
}
|
||||
|
||||
private fun beforeNet():Boolean{
|
||||
if (draiverSn.isBlank()) {
|
||||
CallerLogger.e(M_TAXI_P + TAG, "没有司机屏sn 请稍等在请求")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
/**
|
||||
* 查询全部服务中/待服务订单列表
|
||||
* @param callback
|
||||
*/
|
||||
@JvmStatic
|
||||
fun queryOrdersInAndWaitService(
|
||||
callback: OchCommonServiceCallback<TaxiPassengerOrdersInServiceQueryRespBean>?
|
||||
) {
|
||||
if(beforeNet()){
|
||||
return
|
||||
}
|
||||
mOCHTaxiServiceApi.queryOrdersInAndWaitService(driverSn = draiverSn) //获取到司机端的sn
|
||||
.transformTry()
|
||||
.subscribe(OchCommonSubscribeImpl(context, callback, "queryOrdersInAndWaitService"))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun checkPhoneAndUpdateOrderStatus(
|
||||
orderNo: String?,
|
||||
phone: String?,
|
||||
callback: OchCommonServiceCallback<TaxiPassengerBaseRespBean>?
|
||||
) {
|
||||
mOCHTaxiServiceApi.checkPhoneAndUpdateOrderStatus(data= TaxiPassengerCheckPhoneUpdateOrderReqBean(orderNo, phone))
|
||||
.transformTry()
|
||||
.subscribe(OchCommonSubscribeImpl(context, callback, "checkPhoneAndUpdateOrderStatus"))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun queryPilotStatus(
|
||||
orderNo: String,
|
||||
callback: OchCommonServiceCallback<TaxiPassengerBaseRespBean>?
|
||||
) {
|
||||
mOCHTaxiServiceApi.queryPilotStatus(orderNo = orderNo)
|
||||
.transformTry()
|
||||
.subscribe(OchCommonSubscribeImpl(context, callback, "queryPilotStatus"))
|
||||
}
|
||||
@JvmStatic
|
||||
fun startServicePilotDone(
|
||||
orderNo: String?, loc: TaxiPassengerStartReqBean.Result?,
|
||||
callback: OchCommonServiceCallback<TaxiPassengerBaseRespBean>?
|
||||
) {
|
||||
if(beforeNet()){
|
||||
return
|
||||
}
|
||||
mOCHTaxiServiceApi.startServicePilotDone(data = TaxiPassengerStartReqBean(draiverSn, orderNo, loc))
|
||||
.transformTry()
|
||||
.subscribe(OchCommonSubscribeImpl(context, callback, "startServicePilotDone"))
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过orderId查询订单信息(用于本地已经有orderId时)
|
||||
* @param context
|
||||
* @param orderNo
|
||||
* @param callback
|
||||
*/
|
||||
@JvmStatic
|
||||
fun queryOrderById(
|
||||
context: Context, orderNo: String?,
|
||||
callback: OchCommonServiceCallback<TaxiPassengerOrderQueryRespBean>?
|
||||
) {
|
||||
if(beforeNet()){
|
||||
return
|
||||
}
|
||||
mOCHTaxiServiceApi.queryOrderById(
|
||||
data=TaxiPassengerOrderQueryReqBean(draiverSn, orderNo)
|
||||
)
|
||||
.transformTry()
|
||||
.subscribe(OchCommonSubscribeImpl(context, callback, "queryOrderById"))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.mogo.och.taxi.passenger.presenter
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.mogo.commons.AbsMogoApplication
|
||||
import com.mogo.commons.mvp.Presenter
|
||||
import com.mogo.eagle.core.function.call.biz.CallerFuncBizManager
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
|
||||
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
|
||||
import com.mogo.och.common.module.manager.OCHAdasAbilityManager
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrderQueryRespBean
|
||||
import com.mogo.och.taxi.passenger.callback.IOCHTaxiPassengerOrderStatusCallback
|
||||
import com.mogo.och.taxi.passenger.constant.TaxiPassengerOrderStatusEnum
|
||||
import com.mogo.och.taxi.passenger.model.AutopilotManager
|
||||
import com.mogo.och.taxi.passenger.model.TaxiPassengerModel
|
||||
import com.mogo.och.taxi.passenger.ui.TaxiPassengerBaseFragment
|
||||
|
||||
/**
|
||||
* @author: wangmingjun
|
||||
* @date: 2022/3/4
|
||||
*/
|
||||
class BaseTaxiPassengerPresenter(view: TaxiPassengerBaseFragment?) :
|
||||
Presenter<TaxiPassengerBaseFragment?>(view), IOCHTaxiPassengerOrderStatusCallback {
|
||||
|
||||
init {
|
||||
TaxiPassengerModel.init(AbsMogoApplication.getApp())
|
||||
OCHAdasAbilityManager.getInstance().init(AbsMogoApplication.getApp())
|
||||
initListeners()
|
||||
}
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
super.onCreate(owner)
|
||||
d(SceneConstant.M_TAXI_P + TAG, "网约车-出租车拿到订单")
|
||||
}
|
||||
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
super.onDestroy(owner)
|
||||
releaseListeners()
|
||||
TaxiPassengerModel.release()
|
||||
OCHAdasAbilityManager.getInstance().release()
|
||||
}
|
||||
|
||||
private fun initListeners() {
|
||||
TaxiPassengerModel.setOrderStatusCallback("BaseTaxiPassengerPresenter", this)
|
||||
}
|
||||
|
||||
private fun releaseListeners() {
|
||||
TaxiPassengerModel.setOrderStatusCallback("BaseTaxiPassengerPresenter", null)
|
||||
}
|
||||
|
||||
override fun onCurrentOrderStatusChanged(order: TaxiPassengerOrderQueryRespBean.Result?) {
|
||||
order?.let {
|
||||
updateOrderView(order)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCurrentOrderDistToEndChanged(
|
||||
meters: Long,
|
||||
timeInSecond: Long,
|
||||
stationDistance: Int
|
||||
) {
|
||||
}
|
||||
|
||||
private fun updateOrderView(order: TaxiPassengerOrderQueryRespBean.Result?) {
|
||||
order?.let {
|
||||
setItineraryVisibility()
|
||||
when (TaxiPassengerModel.curOrderStatus) {
|
||||
TaxiPassengerOrderStatusEnum.ArriveAtStart -> {
|
||||
// 20 司机到达上车点
|
||||
mView?.showOrHideArrivedEndLayout(isShow = false)
|
||||
mView?.showOrHidePressengerCheckPager(true, order.startSiteAddr,
|
||||
order.endSiteAddr, order.passengerNum, order.passengerPhone)
|
||||
mView?.showOrHideStartAutopilotView(isShow = false)
|
||||
}
|
||||
|
||||
TaxiPassengerOrderStatusEnum.UserArriveAtStart -> {
|
||||
// 30 乘客到达上车点
|
||||
mView?.showOrHideArrivedEndLayout(isShow = false)
|
||||
mView?.showOrHidePressengerCheckPager(isShow = false)
|
||||
mView?.showOrHideStartAutopilotView(isShow = true)
|
||||
}
|
||||
|
||||
TaxiPassengerOrderStatusEnum.OnTheWayToEnd -> {
|
||||
// 服务中(去往目的地)
|
||||
mView?.showOrHideArrivedEndLayout(isShow = false)
|
||||
mView?.showOrHidePressengerCheckPager(isShow = false)
|
||||
mView?.showOrHideStartAutopilotView(isShow = false)
|
||||
overMapViewShow()
|
||||
}
|
||||
|
||||
TaxiPassengerOrderStatusEnum.ArriveAtEnd -> {
|
||||
// 50 到达终点 乘客可以评价
|
||||
mView?.showOrHideArrivedEndLayout(true)
|
||||
mView?.showOrHidePressengerCheckPager(isShow = false)
|
||||
mView?.showOrHideStartAutopilotView(isShow = false)
|
||||
overMapViewClear()
|
||||
}
|
||||
TaxiPassengerOrderStatusEnum.JourneyCompleted -> {
|
||||
// 60 行程完成
|
||||
mView?.showOrHideStartAutopilotView(isShow = false)
|
||||
mView?.showOrHidePressengerCheckPager(isShow = false)
|
||||
mView?.showOrHideArrivedEndLayout(false)
|
||||
overMapViewClear()
|
||||
}
|
||||
TaxiPassengerOrderStatusEnum.Cancel -> {
|
||||
// 70 取消订单
|
||||
mView?.showOrHideStartAutopilotView(isShow = false)
|
||||
mView?.showOrHidePressengerCheckPager(isShow = false)
|
||||
mView?.showOrHideArrivedEndLayout(isShow = false)
|
||||
overMapViewClear()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun overMapViewShow(){
|
||||
CallerFuncBizManager.bizProvider.getAllV2XEvents()
|
||||
}
|
||||
private fun overMapViewClear(){
|
||||
CallerFuncBizManager.bizProvider.stopQueryV2XEvents()
|
||||
mView?.showOrHideOverMapView()
|
||||
}
|
||||
|
||||
fun checkAndUpdateStatus(phone: String?) {
|
||||
TaxiPassengerModel.checkPhoneAndUpdateStatus(phone) {
|
||||
mView?.showOrHidePressengerCheckPager(isShow = false)
|
||||
}
|
||||
}
|
||||
|
||||
fun setItineraryVisibility() {
|
||||
UiThreadHandler.post {
|
||||
when (TaxiPassengerModel.curOrderStatus) {
|
||||
TaxiPassengerOrderStatusEnum.None,
|
||||
TaxiPassengerOrderStatusEnum.OnTheWayToStart,
|
||||
TaxiPassengerOrderStatusEnum.ArriveAtStart,
|
||||
TaxiPassengerOrderStatusEnum.JourneyCompleted,
|
||||
TaxiPassengerOrderStatusEnum.ArriveAtEnd,
|
||||
TaxiPassengerOrderStatusEnum.Cancel -> mView?.showOrHideServingOrderFragment(false)
|
||||
TaxiPassengerOrderStatusEnum.UserArriveAtStart,
|
||||
TaxiPassengerOrderStatusEnum.OnTheWayToEnd -> mView?.showOrHideServingOrderFragment(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMessageGo2OverMapview() {
|
||||
mView?.showOverMapView()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = BaseTaxiPassengerPresenter::class.java.simpleName
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.mogo.och.taxi.passenger.provider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.alibaba.android.arouter.facade.annotation.Route;
|
||||
import com.mogo.eagle.core.data.constants.MogoServicePaths;
|
||||
import com.mogo.eagle.core.function.api.hmi.view.IStatusViewLayout;
|
||||
import com.mogo.och.taxi.passenger.ui.statusview.StatusBarView;
|
||||
|
||||
/**
|
||||
* @author congtaowang
|
||||
* @since 2020-01-06
|
||||
* <p>
|
||||
* 根据优先级控制显示 window view.
|
||||
*/
|
||||
@Route( path = MogoServicePaths.PATH_STATUS_VIEW_MANAGER )
|
||||
public class StatusViewManager implements IStatusViewLayout {
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getStatusView(Context context) {
|
||||
return new StatusBarView(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Context context) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package com.mogo.och.taxi.passenger.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.ClipDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.media.AudioManager
|
||||
import android.provider.Settings
|
||||
import android.text.TextUtils
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.SeekBar
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.mogo.commons.module.intent.IMogoIntentListener
|
||||
import com.mogo.commons.module.intent.IntentManager
|
||||
import com.mogo.commons.module.receiver.MogoReceiver
|
||||
import com.mogo.commons.module.receiver.MogoReceiver.ACTION_VOLUME_CHANGE
|
||||
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
|
||||
import com.mogo.eagle.core.function.call.setting.CallerRequestActivityHandleManager
|
||||
import com.mogo.eagle.core.utilcode.util.BrightnessUtils
|
||||
import com.mogo.eagle.core.utilcode.util.ThreadUtils
|
||||
import com.mogo.och.common.module.wigets.MineGradientDrawable
|
||||
import com.mogo.och.taxi.passenger.R
|
||||
import kotlinx.android.synthetic.main.taxi_p_setting_view.view.*
|
||||
import me.jessyan.autosize.utils.AutoSizeUtils
|
||||
|
||||
class TaxiPSettingView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr), IMoGoAutopilotStatusListener,
|
||||
IMogoIntentListener {
|
||||
|
||||
companion object {
|
||||
const val TAG = "TaxiPSettingView"
|
||||
}
|
||||
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.taxi_p_setting_view, this, true)
|
||||
initView()
|
||||
}
|
||||
|
||||
private var mAudioManager: AudioManager? = null
|
||||
private var mMaxVolume: Int? = 15
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private fun initView() {
|
||||
sb_light_bar.setProgressDrawableTiled(getDrawable())
|
||||
sb_light_bar.max = 100
|
||||
sb_light_bar.min = 5
|
||||
sb_light_bar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
|
||||
tvSoundPer.text = "$progress%"
|
||||
if (fromUser) {
|
||||
if (!Settings.System.canWrite(context)) {
|
||||
CallerRequestActivityHandleManager.requestPermission(
|
||||
TAG,
|
||||
Settings.ACTION_MANAGE_WRITE_SETTINGS
|
||||
)
|
||||
return
|
||||
}
|
||||
if (BrightnessUtils.isAutoBrightnessEnabled()) {
|
||||
BrightnessUtils.setBrightness(((progress.toFloat() / 100) * 255).toInt())
|
||||
} else {
|
||||
BrightnessUtils.setAutoBrightnessEnabled(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
|
||||
})
|
||||
sb_light_bar.progress = (BrightnessUtils.getBrightness() * 100) / 255
|
||||
|
||||
sb_voice_bar.setProgressDrawableTiled(getDrawable())
|
||||
sb_voice_bar.max = 100
|
||||
sb_voice_bar.min = 5
|
||||
sb_voice_bar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
|
||||
tvVoicePer.text = "$progress%"
|
||||
if (fromUser) {
|
||||
mMaxVolume?.let {
|
||||
var currentValue = ((progress.toFloat() / 100) * it).toInt()
|
||||
if (currentValue <= 0) {
|
||||
currentValue = 1
|
||||
}
|
||||
mAudioManager?.setStreamVolume(
|
||||
AudioManager.STREAM_MUSIC,
|
||||
currentValue,
|
||||
AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
|
||||
})
|
||||
|
||||
context?.let {
|
||||
mAudioManager = it.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
mMaxVolume = mAudioManager?.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
||||
updateVolume()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateVolume() {
|
||||
val mCurrentVolume = mAudioManager?.getStreamVolume(AudioManager.STREAM_MUSIC)
|
||||
mMaxVolume?.let { max ->
|
||||
mCurrentVolume?.let { current ->
|
||||
if (current == 1) {
|
||||
sb_voice_bar.progress = 5
|
||||
tvVoicePer.text = "5%"
|
||||
} else {
|
||||
val fl = current.toFloat() / max * 100
|
||||
sb_voice_bar.progress = fl.toInt()
|
||||
tvVoicePer.text = "${fl.toInt()}%"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDrawable(): Drawable {
|
||||
val dp2px = AutoSizeUtils.dp2px(context, 26f)//进度条高度
|
||||
val color2CBFFC = ContextCompat.getColor(context, R.color.taxi_p_2CBFFC)
|
||||
val color1060FF = ContextCompat.getColor(context, R.color.taxi_p_1060ff)
|
||||
val color96A5C2 = ContextCompat.getColor(context, R.color.taxi_p_96a5c2)
|
||||
val temp03 = MineGradientDrawable(color2CBFFC, color1060FF, dp2px)
|
||||
val clipDrawable3 = ClipDrawable(temp03, Gravity.START, ClipDrawable.HORIZONTAL)
|
||||
val temp01 = MineGradientDrawable(color96A5C2, color96A5C2, dp2px)
|
||||
val arr = arrayOf(temp01, clipDrawable3)
|
||||
val ld = LayerDrawable(arr)
|
||||
ld.setDrawableByLayerId(android.R.id.background, temp01)
|
||||
ld.setDrawableByLayerId(android.R.id.progress, clipDrawable3)
|
||||
return ld
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
IntentManager.getInstance().registerIntentListener(ACTION_VOLUME_CHANGE, this)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
IntentManager.getInstance().unregisterIntentListener(ACTION_VOLUME_CHANGE, this)
|
||||
}
|
||||
|
||||
override fun onWindowVisibilityChanged(visibility: Int) {
|
||||
super.onWindowVisibilityChanged(visibility)
|
||||
if (visibility == View.VISIBLE) {
|
||||
sb_light_bar.progress = (BrightnessUtils.getBrightness() * 100) / 255
|
||||
}
|
||||
}
|
||||
|
||||
override fun onWindowFocusChanged(hasWindowFocus: Boolean) {
|
||||
super.onWindowFocusChanged(hasWindowFocus)
|
||||
if (hasWindowFocus) {
|
||||
sb_light_bar.progress = (BrightnessUtils.getBrightness() * 100) / 255
|
||||
}
|
||||
}
|
||||
|
||||
override fun onIntentReceived(intentStr: String?, intent: Intent?) {
|
||||
if (TextUtils.equals(ACTION_VOLUME_CHANGE, intentStr)) {
|
||||
if (intent!!.getIntExtra(
|
||||
MogoReceiver.EXTRA_VOLUME_STREAM_TYPE, -1
|
||||
) == AudioManager.STREAM_MUSIC
|
||||
) {
|
||||
ThreadUtils.runOnUiThread {
|
||||
updateVolume()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
package com.mogo.och.taxi.passenger.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.mogo.commons.mvp.MvpFragment
|
||||
import com.mogo.commons.voice.AIAssist
|
||||
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
|
||||
import com.mogo.eagle.core.function.call.map.CallerMapUIServiceManager
|
||||
import com.mogo.eagle.core.utilcode.kotlin.onClick
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_TAXI_P
|
||||
import com.mogo.eagle.core.utilcode.util.DeviceUtils
|
||||
import com.mogo.eagle.core.utilcode.util.OverlayViewUtils
|
||||
import com.mogo.map.listener.IMogoMapListener
|
||||
import com.mogo.map.uicontroller.VisualAngleMode
|
||||
import com.mogo.och.common.module.utils.RxUtils
|
||||
import com.mogo.och.common.module.voice.VoiceNotice
|
||||
import com.mogo.och.taxi.passenger.R
|
||||
import com.mogo.och.taxi.passenger.callback.ITaxiPassengerCommonValueCallback
|
||||
import com.mogo.och.taxi.passenger.presenter.BaseTaxiPassengerPresenter
|
||||
import com.mogo.och.taxi.passenger.ui.arrived.ArrivedView
|
||||
import com.mogo.och.taxi.passenger.ui.bottom.BottomBar
|
||||
import com.mogo.och.taxi.passenger.ui.check.TaxiPassengerCheckView
|
||||
import com.mogo.och.taxi.passenger.ui.startautopilot.StartAutopilotView
|
||||
import com.mogo.och.taxi.passenger.widget.animutils.AnimationsContainer
|
||||
import kotlinx.android.synthetic.main.taxi_p_base_fragment.*
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
* 网约车基础Fragment,主要负责布局通用界面,处理站点面板和通话面板互斥情况
|
||||
*
|
||||
*
|
||||
* 部分业务放在了此处处理
|
||||
*
|
||||
* @author tongchenfei
|
||||
*/
|
||||
class TaxiPassengerBaseFragment() :
|
||||
MvpFragment<TaxiPassengerBaseFragment?, BaseTaxiPassengerPresenter?>(), IMogoMapListener,
|
||||
TaxiPassengerTaxiView {
|
||||
|
||||
/**
|
||||
* 到达目的地
|
||||
*/
|
||||
private var mArrivedEndView: WeakReference<ArrivedView?>? = null
|
||||
|
||||
/**
|
||||
* 手机号后四位验证
|
||||
*/
|
||||
private var mArrivedCheckView: WeakReference<TaxiPassengerCheckView?>? = null
|
||||
|
||||
/**
|
||||
* 启动自驾页面
|
||||
*/
|
||||
private var mStartAutopilotView: WeakReference<StartAutopilotView?>? = null
|
||||
|
||||
private var createProgressDialogAnim: AnimationsContainer?=null
|
||||
|
||||
override fun getLayoutId(): Int {
|
||||
return R.layout.taxi_p_base_fragment
|
||||
}
|
||||
|
||||
override fun getTagName(): String {
|
||||
return "BaseOchTaxiPassengerFragment"
|
||||
}
|
||||
|
||||
override fun initViews() {
|
||||
initListener()
|
||||
}
|
||||
|
||||
override fun initViews(savedInstanceState: Bundle?) {
|
||||
super.initViews(savedInstanceState)
|
||||
mapBizView!!.onCreate(savedInstanceState)
|
||||
overMapView.onCreateView(savedInstanceState)
|
||||
overMapView.hideResetView()
|
||||
|
||||
createProgressDialogAnim = AnimationsContainer(R.array.xiaozhi_normal, 20,aciv_xiaozhi_normal)
|
||||
createProgressDialogAnim?.setOnAnimStopListener(object :AnimationsContainer.OnAnimationStoppedListener{
|
||||
override fun AnimationStopped() {
|
||||
CallerLogger.d(M_TAXI_P + TAG, "动画暂停")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun initListener() {
|
||||
ck_setting.isChecked = false
|
||||
ck_setting.setOnCheckedChangeListener { _, isChecked ->
|
||||
clSettingView.visibility = if(isChecked) View.VISIBLE else View.GONE
|
||||
}
|
||||
bottom.setOverMapApplyClick(object : BottomBar.ApplyClickLintener{
|
||||
override fun onApplyClick(selectItem: BottomBar.SelectView) {
|
||||
when (selectItem) {
|
||||
BottomBar.SelectView.PRECISIONMAP -> {
|
||||
overMapView.visibility = View.GONE
|
||||
mapBizView.visibility = View.VISIBLE
|
||||
presenter?.setItineraryVisibility()
|
||||
ck_setting.visibility = View.VISIBLE
|
||||
if (DeviceUtils.isLenovoModel() || DeviceUtils.isEB5Model()) {
|
||||
romaPView.visibility = View.VISIBLE
|
||||
} else {
|
||||
romaPView.visibility = View.GONE
|
||||
}
|
||||
rv_location_center.visibility = View.VISIBLE
|
||||
pcnActionView.visibility = View.VISIBLE
|
||||
CallerHmiManager.showTrafficLightView()
|
||||
infoVideoView.visibility = View.GONE
|
||||
CallerHmiManager.showTurnLightView()
|
||||
}
|
||||
BottomBar.SelectView.OVERMAPVIEW -> {
|
||||
overMapView.visibility = View.VISIBLE
|
||||
mapBizView.visibility = View.GONE
|
||||
presenter?.setItineraryVisibility()
|
||||
ck_setting.visibility = View.VISIBLE
|
||||
romaPView.visibility = View.GONE
|
||||
rv_location_center.visibility = View.VISIBLE
|
||||
pcnActionView.visibility = View.VISIBLE
|
||||
CallerHmiManager.showTrafficLightView()
|
||||
infoVideoView.visibility = View.GONE
|
||||
CallerHmiManager.showTurnLightView()
|
||||
}
|
||||
BottomBar.SelectView.VIDEO -> {
|
||||
overMapView.visibility = View.GONE
|
||||
mapBizView.visibility = View.GONE
|
||||
presenter?.setItineraryVisibility()
|
||||
ck_setting.visibility = View.GONE
|
||||
ck_setting.isChecked = false
|
||||
romaPView.visibility = View.GONE
|
||||
rv_location_center.visibility = View.GONE
|
||||
pcnActionView.visibility = View.GONE
|
||||
CallerHmiManager.hideTrafficLightView()
|
||||
infoVideoView.visibility = View.VISIBLE
|
||||
CallerHmiManager.hideTurnLightView()
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
rv_location_center.onClick {
|
||||
when (bottom.getCurrentPage()) {
|
||||
BottomBar.SelectView.PRECISIONMAP -> {
|
||||
val controller = CallerMapUIServiceManager.getMapUIController()
|
||||
if (controller != null) {
|
||||
//切换到地图中间
|
||||
controller.changeMapVisualAngle(VisualAngleMode.MODE_MEDIUM_SIGHT, null)
|
||||
// 切换缩放到中视角
|
||||
controller.changeZoom2(0.8f)
|
||||
}
|
||||
}
|
||||
BottomBar.SelectView.OVERMAPVIEW -> {
|
||||
overMapView.displayCustomOverView()
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
|
||||
}
|
||||
view?.viewTreeObserver?.addOnWindowFocusChangeListener {
|
||||
if(it){
|
||||
CallerLogger.d(M_TAXI_P + TAG, "windows获取焦点")
|
||||
createProgressDialogAnim?.start()
|
||||
}else{
|
||||
CallerLogger.d(M_TAXI_P + TAG, "window失去焦点")
|
||||
createProgressDialogAnim?.stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initCheckView() {
|
||||
mArrivedCheckView = WeakReference(TaxiPassengerCheckView(context))
|
||||
mArrivedCheckView!!.get()!!.iTaxiPassengerCommonValueCallback =
|
||||
ITaxiPassengerCommonValueCallback { phoneTail: String? ->
|
||||
getPresenter()!!.checkAndUpdateStatus(phoneTail)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
mapBizView!!.onResume()
|
||||
overMapView.onResume()
|
||||
CallerLogger.d(M_TAXI_P + TAG, "onResume")
|
||||
|
||||
createProgressDialogAnim?.start()
|
||||
}
|
||||
|
||||
override fun createPresenter(): BaseTaxiPassengerPresenter {
|
||||
return BaseTaxiPassengerPresenter(this)
|
||||
}
|
||||
|
||||
override fun onLowMemory() {
|
||||
super.onLowMemory()
|
||||
mapBizView!!.onLowMemory()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
mapBizView!!.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
mapBizView!!.onPause()
|
||||
overMapView?.onPause()
|
||||
CallerLogger.d(M_TAXI_P + TAG, "onPause")
|
||||
createProgressDialogAnim?.stop()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
mapBizView!!.onDestroy()
|
||||
overMapView?.onDestroy()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示或隐藏订单信息
|
||||
*
|
||||
* @param isShow
|
||||
*/
|
||||
fun showOrHideServingOrderFragment(isShow: Boolean) {
|
||||
when (bottom.getCurrentPage()) {
|
||||
BottomBar.SelectView.OVERMAPVIEW,BottomBar.SelectView.PRECISIONMAP -> {
|
||||
if (isShow) {
|
||||
if(itinerary.visibility!=View.VISIBLE) {
|
||||
itinerary.visibility = View.VISIBLE
|
||||
}
|
||||
} else {
|
||||
if(itinerary.visibility!=View.GONE) {
|
||||
itinerary.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
BottomBar.SelectView.VIDEO,BottomBar.SelectView.NONE -> {
|
||||
if(itinerary.visibility!=View.GONE) {
|
||||
itinerary.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示或者隐藏乘客可点击自动驾驶页面
|
||||
* 乘客验证成功,页面显示,按钮置于不可点击
|
||||
* 司机端确认可点击开启自动驾驶, 按钮置为可点击
|
||||
* 订单前往目的地,页面消失
|
||||
*
|
||||
* @param isShow
|
||||
*/
|
||||
fun showOrHideStartAutopilotView(isShow: Boolean) {
|
||||
if (isShow) {
|
||||
exitFullVideoScreen(false)
|
||||
if (mStartAutopilotView == null || mStartAutopilotView!!.get() == null) {
|
||||
mStartAutopilotView = WeakReference(StartAutopilotView(requireContext()))
|
||||
}
|
||||
mStartAutopilotView?.get()?.let {
|
||||
OverlayViewUtils.showOverlayView(activity, it)
|
||||
it.handleStartAutopilotBtnStatus(false)
|
||||
}
|
||||
} else {
|
||||
mStartAutopilotView?.get()?.closeAllAnimsAndView()
|
||||
mStartAutopilotView = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示或者隐藏到达乘客站点的洁面
|
||||
* ① 取消订单 可有可无
|
||||
* ② 到达上车点 隐藏到达终点的页面(上一个订单没有评价)
|
||||
* ③ 到达目的地 显示到达终点的页面
|
||||
* ④ debug 使用
|
||||
*
|
||||
* @param isShow true 展示 false 隐藏
|
||||
*/
|
||||
fun showOrHideArrivedEndLayout(isShow: Boolean) {
|
||||
if (isShow) {
|
||||
exitFullVideoScreen(true)
|
||||
if (mArrivedEndView == null || mArrivedEndView!!.get() == null) {
|
||||
mArrivedEndView = WeakReference(ArrivedView(context))
|
||||
}
|
||||
mArrivedEndView?.get()?.let {
|
||||
OverlayViewUtils.showOverlayView(activity, it, R.style.och_window_anim_alpha)
|
||||
RxUtils.createSubscribe(500) {
|
||||
it.setDataAndStartAnimation()
|
||||
VoiceNotice.showNotice("已到达目的地,带好随身物品,右侧下车更安全!期待下次再见", AIAssist.LEVEL2)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mArrivedEndView?.get()?.let {
|
||||
OverlayViewUtils.dismissOverlayView(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun exitFullVideoScreen(resetVideoPlayer: Boolean) {
|
||||
infoVideoView.exitFullScreenMode(resetVideoPlayer)
|
||||
}
|
||||
|
||||
fun showOrHideOverMapView(){
|
||||
overMapView?.clearV2XMarkers()
|
||||
overMapView?.clearCustomPolyline()
|
||||
}
|
||||
|
||||
/**
|
||||
* ① 取消订单 到达上车点后乘客取消订单 隐藏乘客验证页面
|
||||
* ② 司机到达上车点 到达上车点 展示乘客验证页面
|
||||
* ③ 乘客到达上车点 手机号验证成功后 隐藏乘客验证页面
|
||||
* ④ debug 使用
|
||||
*/
|
||||
fun showOrHidePressengerCheckPager(
|
||||
isShow: Boolean,
|
||||
startSiteAddr: String? = "",
|
||||
endSiteAddr: String? = "",
|
||||
passengerNum: String? = "",
|
||||
phone: String? = ""
|
||||
) {
|
||||
try {
|
||||
if (isShow) {
|
||||
exitFullVideoScreen(false)
|
||||
if (mArrivedCheckView == null || mArrivedCheckView!!.get() == null) {
|
||||
initCheckView()
|
||||
}
|
||||
mArrivedCheckView!!.get()!!
|
||||
.setData(startSiteAddr, endSiteAddr, passengerNum, phone)
|
||||
OverlayViewUtils.showOverlayView(activity, mArrivedCheckView!!.get())
|
||||
} else {
|
||||
if (mArrivedCheckView == null || mArrivedCheckView!!.get() == null) {
|
||||
return
|
||||
}
|
||||
OverlayViewUtils.dismissOverlayView(mArrivedCheckView!!.get())
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun showOverMapView() {
|
||||
bottom.setCheckIndex(BottomBar.SelectView.OVERMAPVIEW)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val TAG = "TaxiPassengerBaseFragment"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.mogo.och.taxi.passenger.ui;
|
||||
|
||||
import com.mogo.commons.mvp.IView;
|
||||
|
||||
|
||||
/**
|
||||
* @author congtaowang
|
||||
* @since 2021/1/18
|
||||
*
|
||||
* 描述
|
||||
*/
|
||||
public interface TaxiPassengerTaxiView extends IView {
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package com.mogo.och.taxi.passenger.ui.arrived
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.mogo.commons.AbsMogoApplication
|
||||
import com.mogo.eagle.core.utilcode.kotlin.onClick
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
|
||||
import com.mogo.eagle.core.utilcode.util.OverlayViewUtils
|
||||
import com.mogo.och.common.module.utils.RxUtils
|
||||
import com.mogo.och.taxi.passenger.R
|
||||
import com.mogo.och.taxi.passenger.widget.WindowRelativeLayout
|
||||
import com.mogo.och.taxi.passenger.widget.animutils.AnimationsContainer
|
||||
import com.shuyu.gsyvideoplayer.GSYVideoManager
|
||||
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
|
||||
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
|
||||
import io.reactivex.disposables.Disposable
|
||||
import kotlinx.android.synthetic.main.taxi_p_arrived_end_panel.view.aciv_close
|
||||
import kotlinx.android.synthetic.main.taxi_p_arrived_end_panel.view.actv_endstation
|
||||
import kotlinx.android.synthetic.main.taxi_p_arrived_end_panel.view.iv_xiaozhi_belt
|
||||
import kotlinx.android.synthetic.main.taxi_p_arrived_end_panel.view.svp_frame
|
||||
import kotlinx.android.synthetic.main.taxi_p_arrived_end_panel.view.v_video_right_rear_view
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* 评价View
|
||||
* Created on 2022/5/16
|
||||
*/
|
||||
class ArrivedView : WindowRelativeLayout, ArrivedViewModel.ArrivedViewCallback {
|
||||
|
||||
constructor(context: Context?) : super(context)
|
||||
|
||||
constructor(context: Context?, attributeSet: AttributeSet) : super(context, attributeSet)
|
||||
|
||||
constructor(context: Context?, attributeSet: AttributeSet, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr)
|
||||
|
||||
constructor(context: Context?, attributeSet: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attributeSet, defStyleAttr, defStyleRes)
|
||||
|
||||
private var subscribe: Disposable?=null
|
||||
|
||||
private val gsyVideoOptionBuilder = GSYVideoOptionBuilder()
|
||||
|
||||
private var taxiPxiaozhiLove: AnimationsContainer?=null
|
||||
|
||||
private fun initView() {
|
||||
d(SceneConstant.M_TAXI_P + TAG, "initView")
|
||||
LayoutInflater.from(context).inflate(R.layout.taxi_p_arrived_end_panel, this, true)
|
||||
svp_frame.setBackgroundResource(R.drawable.tail_ani_0000)
|
||||
svp_frame.setIsTouchWiget(false)
|
||||
svp_frame.setIsTouchWigetFull(false)
|
||||
svp_frame.enableshowProgressDialog = false
|
||||
svp_frame.enableDoubleClick = false
|
||||
GSYVideoManager.instance().enableRawPlay(AbsMogoApplication.getApp())
|
||||
val url = "android.resource://" + context.packageName + "/" + R.raw.end_video
|
||||
gsyVideoOptionBuilder.setUrl(url)
|
||||
.setCacheWithPlay(false)
|
||||
.setPlayTag("TaxiPassengerArrivedView")
|
||||
.build(svp_frame)
|
||||
|
||||
aciv_close.onClick {
|
||||
OverlayViewUtils.dismissOverlayView(this)
|
||||
}
|
||||
|
||||
taxiPxiaozhiLove = AnimationsContainer(R.array.xiaozhi_love, 20,iv_xiaozhi_belt)
|
||||
taxiPxiaozhiLove?.setOnAnimStopListener(object :AnimationsContainer.OnAnimationStoppedListener{
|
||||
override fun AnimationStopped() {
|
||||
d(SceneConstant.M_TAXI_P + TAG, "动画暂停")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
val viewModel = ViewModelProvider(this).get(ArrivedViewModel::class.java)
|
||||
viewModel.setViewCallback(this)
|
||||
taxiPxiaozhiLove?.start()
|
||||
v_video_right_rear_view.resetView()
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
svp_frame.setBackgroundResource(R.drawable.tail_ani_0000)
|
||||
svp_frame.setVideoAllCallBack(null)
|
||||
svp_frame.onVideoReset()
|
||||
svp_frame.release()
|
||||
taxiPxiaozhiLove?.stop()
|
||||
v_video_right_rear_view.resetView()
|
||||
|
||||
super.onDetachedFromWindow()
|
||||
subscribe?.let {
|
||||
if (!it.isDisposed) {
|
||||
it.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置目的地重置星星状态
|
||||
*/
|
||||
fun setDataAndStartAnimation() {
|
||||
svp_frame.setVideoAllCallBack(object : GSYSampleCallBack() {
|
||||
override fun onAutoComplete(url: String?, vararg objects: Any?) {
|
||||
svp_frame.setBackgroundResource(R.drawable.tail_ani_0090)
|
||||
}
|
||||
})
|
||||
svp_frame.startPlayLogic()
|
||||
RxUtils.createSubscribe(60_000) {
|
||||
OverlayViewUtils.dismissOverlayView(this@ArrivedView)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "TaxiPassengerArrivedView"
|
||||
}
|
||||
|
||||
init {
|
||||
try {
|
||||
initView()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setEndStation(stationName: String) {
|
||||
actv_endstation.text = stationName
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.mogo.och.taxi.passenger.ui.arrived
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.mogo.och.taxi.passenger.model.TaxiPassengerModel
|
||||
|
||||
class ArrivedViewModel: ViewModel() {
|
||||
|
||||
private val TAG = ArrivedViewModel::class.java.simpleName
|
||||
|
||||
private var viewCallback:ArrivedViewCallback?=null
|
||||
|
||||
init {
|
||||
|
||||
}
|
||||
|
||||
fun setViewCallback(viewCallback: ArrivedViewCallback){
|
||||
this.viewCallback = viewCallback
|
||||
TaxiPassengerModel.currentOCHOrder?.endSiteAddr?.let {
|
||||
this.viewCallback?.setEndStation(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
this.viewCallback = null
|
||||
}
|
||||
|
||||
|
||||
interface ArrivedViewCallback{
|
||||
fun setEndStation(stationName:String)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package com.mogo.och.taxi.passenger.ui.arrived
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.mogo.eagle.core.function.api.autopilot.IMoGoBackCameraVideoListener
|
||||
import com.mogo.eagle.core.function.api.autopilot.IMoGoRoboBusJinlvM1StitchedVideoListener
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager.setIsSubscribeBackCameraVideoVideo
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerBackCameraVideoListenerManager
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerRoboBusJinlvM1StitchedVideoListenerManager
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
|
||||
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
|
||||
import com.mogo.eagle.core.widget.media.video.TextureVideoViewOutlineProvider
|
||||
import com.mogo.och.taxi.passenger.R
|
||||
import kotlinx.android.synthetic.main.taxi_p_right_rear_cam.view.actv_cam_position_group
|
||||
import kotlinx.android.synthetic.main.taxi_p_right_rear_cam.view.v_video_right_rear
|
||||
|
||||
/**
|
||||
*
|
||||
* 评价View
|
||||
* Created on 2022/5/16
|
||||
*/
|
||||
class RightRearCamView : ConstraintLayout , IMoGoBackCameraVideoListener,
|
||||
IMoGoRoboBusJinlvM1StitchedVideoListener,Runnable {
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)
|
||||
|
||||
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr)
|
||||
|
||||
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attributeSet, defStyleAttr, defStyleRes)
|
||||
|
||||
private var mBitmap: Bitmap? = null
|
||||
|
||||
private var mBitmapOptions: BitmapFactory.Options? = null //Bitmap管理类,可有效减少Bitmap的OOM问题
|
||||
|
||||
|
||||
private fun initView() {
|
||||
d(SceneConstant.M_TAXI_P + TAG, "initView")
|
||||
LayoutInflater.from(context).inflate(R.layout.taxi_p_right_rear_cam, this, true)
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
setIsSubscribeBackCameraVideoVideo(1, true)
|
||||
CallerBackCameraVideoListenerManager.addListener(TAG, this)
|
||||
CallerRoboBusJinlvM1StitchedVideoListenerManager.addListener(TAG, this)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
setIsSubscribeBackCameraVideoVideo(1, false)
|
||||
CallerBackCameraVideoListenerManager.removeListener(this)
|
||||
CallerRoboBusJinlvM1StitchedVideoListenerManager.removeListener(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "RightRearCamView"
|
||||
}
|
||||
|
||||
init {
|
||||
try {
|
||||
initView()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun resetView(){
|
||||
actv_cam_position_group.visibility = GONE
|
||||
v_video_right_rear.setImageResource(R.drawable.taxi_p_right_rear_cam)
|
||||
}
|
||||
|
||||
override fun onBackCameraVideo(data: ByteArray) {
|
||||
decodeData(data)
|
||||
}
|
||||
|
||||
override fun onRoboBusJinlvM1StitchedVideo(data: ByteArray) {
|
||||
decodeData(data)
|
||||
}
|
||||
|
||||
var preTime :Long=System.currentTimeMillis()
|
||||
|
||||
@Synchronized
|
||||
private fun decodeData(data: ByteArray){
|
||||
val currentTimeMillis = System.currentTimeMillis()
|
||||
val dexTime = currentTimeMillis - preTime
|
||||
preTime = currentTimeMillis
|
||||
if(dexTime<20){
|
||||
return
|
||||
}
|
||||
d(SceneConstant.M_TAXI_P + TAG, "图片频率:$dexTime")
|
||||
if (mBitmapOptions == null) {
|
||||
val bmp = (v_video_right_rear.drawable as BitmapDrawable).bitmap
|
||||
val width = bmp.width
|
||||
val height = bmp.height
|
||||
val config = bmp.config
|
||||
mBitmap = Bitmap.createBitmap(width, height, config)
|
||||
mBitmapOptions = BitmapFactory.Options()
|
||||
//设置Bitmap内存复用
|
||||
mBitmapOptions!!.inBitmap = mBitmap //Bitmap复用内存块,类似对象池,避免不必要的内存分配和回收
|
||||
mBitmapOptions!!.inMutable = true //解码时返回可变Bitmap
|
||||
|
||||
val options = BitmapFactory.Options()
|
||||
options.inJustDecodeBounds = true
|
||||
BitmapFactory.decodeByteArray(data, 0, data.size, options)
|
||||
mBitmapOptions!!.inSampleSize = calculateInSampleSize(options, width, height)
|
||||
}
|
||||
mBitmapOptions?.let {
|
||||
try {
|
||||
val preTime = System.currentTimeMillis()
|
||||
BitmapFactory.decodeByteArray(data, 0, data.size, mBitmapOptions)
|
||||
d(SceneConstant.M_TAXI_P + TAG, "decode时间:${System.currentTimeMillis()-preTime}")
|
||||
UiThreadHandler.post(this)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
|
||||
val width = options.outWidth
|
||||
val height = options.outHeight
|
||||
Log.i(TAG, "calculateInSampleSize: out width and height is $width height $height")
|
||||
var inSampleWidth = 1
|
||||
if (height > reqHeight || width > reqWidth) {
|
||||
val halfHeight = height / 2
|
||||
val halfWidth = width / 2
|
||||
// 采样率设置为2的指数
|
||||
while (halfHeight / inSampleWidth >= reqHeight && halfWidth / inSampleWidth >= reqWidth) {
|
||||
inSampleWidth *= 2
|
||||
}
|
||||
}
|
||||
|
||||
while (width/inSampleWidth>reqWidth||height/inSampleWidth>reqHeight){
|
||||
inSampleWidth++
|
||||
}
|
||||
|
||||
return inSampleWidth
|
||||
}
|
||||
|
||||
|
||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||
super.onSizeChanged(w, h, oldw, oldh)
|
||||
outlineProvider = TextureVideoViewOutlineProvider(36f)
|
||||
clipToOutline = true
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
if(actv_cam_position_group?.visibility == GONE) {
|
||||
actv_cam_position_group?.visibility = VISIBLE
|
||||
}
|
||||
v_video_right_rear?.setImageBitmap(mBitmap)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.mogo.och.taxi.passenger.ui.bottom
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.mogo.och.taxi.passenger.R
|
||||
import kotlinx.android.synthetic.main.taxi_p_bottom_bar.view.actv_overmap
|
||||
import kotlinx.android.synthetic.main.taxi_p_bottom_bar.view.actv_precisionmap
|
||||
import kotlinx.android.synthetic.main.taxi_p_bottom_bar.view.actv_video
|
||||
|
||||
class BottomBar @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private var checkIndex = SelectView.NONE
|
||||
private var overMapViewApply:ApplyClickLintener?=null
|
||||
|
||||
init {
|
||||
isClickable = true
|
||||
LayoutInflater.from(context).inflate(R.layout.taxi_p_bottom_bar, this, true)
|
||||
setBackgroundResource(R.drawable.taxi_p_bottom_bar_bg)
|
||||
actv_precisionmap.setOnClickListener {
|
||||
setCheckIndex(SelectView.PRECISIONMAP)
|
||||
}
|
||||
actv_overmap.setOnClickListener {
|
||||
setCheckIndex(SelectView.OVERMAPVIEW)
|
||||
}
|
||||
actv_video.setOnClickListener {
|
||||
setCheckIndex(SelectView.VIDEO)
|
||||
}
|
||||
}
|
||||
|
||||
fun getCurrentPage():SelectView{
|
||||
return checkIndex
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
setCheckIndex(SelectView.PRECISIONMAP)
|
||||
}
|
||||
|
||||
fun setOverMapApplyClick(overMapViewApply:ApplyClickLintener){
|
||||
this.overMapViewApply = overMapViewApply
|
||||
}
|
||||
|
||||
fun setCheckIndex(index: SelectView){
|
||||
if(checkIndex==index){
|
||||
return
|
||||
}else{
|
||||
checkIndex = index
|
||||
}
|
||||
overMapViewApply?.onApplyClick(checkIndex)
|
||||
if(checkIndex == SelectView.OVERMAPVIEW){
|
||||
actv_overmap.setCheckItem(true)
|
||||
}else{
|
||||
actv_overmap.setCheckItem(false)
|
||||
}
|
||||
if(checkIndex == SelectView.VIDEO){
|
||||
actv_video.setCheckItem(true)
|
||||
}else{
|
||||
actv_video.setCheckItem(false)
|
||||
}
|
||||
if(checkIndex == SelectView.PRECISIONMAP){
|
||||
actv_precisionmap.setCheckItem(true)
|
||||
}else{
|
||||
actv_precisionmap.setCheckItem(false)
|
||||
}
|
||||
}
|
||||
|
||||
enum class SelectView{
|
||||
NONE,PRECISIONMAP,OVERMAPVIEW,VIDEO
|
||||
}
|
||||
|
||||
interface ApplyClickLintener{
|
||||
fun onApplyClick(selectItem:SelectView)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.mogo.och.bus.passenger.ui.view.bottom
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.mogo.och.taxi.passenger.R
|
||||
import kotlinx.android.synthetic.main.m1_bottom_check.view.aciv_center_image
|
||||
import kotlinx.android.synthetic.main.m1_bottom_check.view.actv_title
|
||||
|
||||
open class BottomCheckView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "StopSiteView"
|
||||
}
|
||||
|
||||
private var backageViewId: Int = -1
|
||||
private var bottomTitle: String = ""
|
||||
private var selectedDrawable: Int = -1
|
||||
private var normalDrawable: Int = -1
|
||||
private var bottomTitleNormalColor:Int = -1
|
||||
private var bottomTitleCheckedColor:Int = -1
|
||||
private var backageView: View? = null
|
||||
|
||||
private var isCheck = false
|
||||
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.m1_bottom_check, this, true)
|
||||
try {
|
||||
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.BottomSelectView)
|
||||
backageViewId = typedArray.getResourceId(R.styleable.BottomSelectView_backageViewId, -1)
|
||||
bottomTitle = typedArray.getString(R.styleable.BottomSelectView_bottomTitle) ?: ""
|
||||
selectedDrawable = typedArray.getResourceId(R.styleable.BottomSelectView_selectedDrawable, -1)
|
||||
normalDrawable = typedArray.getResourceId(R.styleable.BottomSelectView_normalDrawable, -1)
|
||||
bottomTitleNormalColor = typedArray.getColor(R.styleable.BottomSelectView_bottomTitleNormalColor,
|
||||
ContextCompat.getColor(context,R.color.white))
|
||||
bottomTitleCheckedColor = typedArray.getColor(R.styleable.BottomSelectView_bottomTitleCheckedColor,
|
||||
ContextCompat.getColor(context,R.color.white))
|
||||
typedArray.recycle()
|
||||
initView(context)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun initView(context: Context) {
|
||||
if (selectedDrawable > 0) {
|
||||
aciv_center_image.setImageResource(normalDrawable)
|
||||
}
|
||||
actv_title.text = bottomTitle
|
||||
}
|
||||
|
||||
fun setCheckItem(isCheck: Boolean) {
|
||||
if (isCheck != this.isCheck) {
|
||||
this.isCheck = isCheck
|
||||
notifiBackageView()
|
||||
}
|
||||
}
|
||||
|
||||
private fun notifiBackageView() {
|
||||
if (isCheck) {
|
||||
backageView?.visibility = View.VISIBLE
|
||||
aciv_center_image.setImageResource(selectedDrawable)
|
||||
actv_title.setTextColor(bottomTitleCheckedColor)
|
||||
} else {
|
||||
backageView?.visibility = View.GONE
|
||||
aciv_center_image.setImageResource(normalDrawable)
|
||||
actv_title.setTextColor(bottomTitleNormalColor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
parent?.let {
|
||||
if (parent is ConstraintLayout) {
|
||||
if (backageViewId > 0) {
|
||||
backageView = (parent as ConstraintLayout).findViewById(backageViewId)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isCheck) {
|
||||
backageView?.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
package com.mogo.och.taxi.passenger.ui.check
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.TextAppearanceSpan
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.mogo.commons.voice.AIAssist
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
|
||||
import com.mogo.eagle.core.utilcode.util.ToastUtils
|
||||
import com.mogo.och.common.module.voice.VoiceNotice
|
||||
import com.mogo.och.taxi.passenger.R
|
||||
import com.mogo.och.taxi.passenger.callback.ITaxiPassengerCommonValueCallback
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_passenger_count
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_passenger_end
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_passenger_start
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_taxi_passenger_number_back
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_taxi_passenger_number_eight
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_taxi_passenger_number_first
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_taxi_passenger_number_five
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_taxi_passenger_number_four
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_taxi_passenger_number_fourth
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_taxi_passenger_number_nine
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_taxi_passenger_number_one
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_taxi_passenger_number_second
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_taxi_passenger_number_seven
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_taxi_passenger_number_six
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_taxi_passenger_number_submit
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_taxi_passenger_number_third
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_taxi_passenger_number_three
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_taxi_passenger_number_two
|
||||
import kotlinx.android.synthetic.main.taxi_p_passenger_check_panel.view.tv_taxi_passenger_number_zero
|
||||
|
||||
/**
|
||||
* V2X预警事件view:通过FloatWindow呈现,无需加入到自定义layout中
|
||||
*
|
||||
* Created on 2022/3/16
|
||||
*/
|
||||
class TaxiPassengerCheckView :RelativeLayout, View.OnClickListener {
|
||||
|
||||
constructor(context: Context?) : super(context)
|
||||
|
||||
constructor(context: Context?, attributeSet: AttributeSet) : super(context, attributeSet)
|
||||
|
||||
constructor(context: Context?, attributeSet: AttributeSet, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr)
|
||||
|
||||
constructor(context: Context?, attributeSet: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attributeSet, defStyleAttr, defStyleRes)
|
||||
|
||||
var iTaxiPassengerCommonValueCallback: ITaxiPassengerCommonValueCallback<String>?=null
|
||||
|
||||
|
||||
private var index = 0
|
||||
private var phone = ""
|
||||
private val numSelect = arrayOfNulls<Int>(4)
|
||||
private val numSelectTextView = arrayOfNulls<TextView>(4)
|
||||
|
||||
private fun initView(context: Context) {
|
||||
d(SceneConstant.M_TAXI_P + TAG, "initView")
|
||||
LayoutInflater.from(context).inflate(R.layout.taxi_p_passenger_check_panel, this, true)
|
||||
keyBoardLogic()
|
||||
numSelectTextView[0] = tv_taxi_passenger_number_first
|
||||
numSelectTextView[1] = tv_taxi_passenger_number_second
|
||||
numSelectTextView[2] = tv_taxi_passenger_number_third
|
||||
numSelectTextView[3] = tv_taxi_passenger_number_fourth
|
||||
}
|
||||
|
||||
private fun keyBoardLogic() {
|
||||
tv_taxi_passenger_number_one.setOnClickListener(this)
|
||||
tv_taxi_passenger_number_two.setOnClickListener(this)
|
||||
tv_taxi_passenger_number_three.setOnClickListener(this)
|
||||
tv_taxi_passenger_number_four.setOnClickListener(this)
|
||||
tv_taxi_passenger_number_five.setOnClickListener(this)
|
||||
tv_taxi_passenger_number_six.setOnClickListener(this)
|
||||
tv_taxi_passenger_number_seven.setOnClickListener(this)
|
||||
tv_taxi_passenger_number_eight.setOnClickListener(this)
|
||||
tv_taxi_passenger_number_nine.setOnClickListener(this)
|
||||
tv_taxi_passenger_number_zero.setOnClickListener(this)
|
||||
tv_taxi_passenger_number_back.setOnClickListener(this)
|
||||
tv_taxi_passenger_number_submit.setOnClickListener(this)
|
||||
|
||||
tv_taxi_passenger_number_first.setOnClickListener(this)
|
||||
tv_taxi_passenger_number_second.setOnClickListener(this)
|
||||
tv_taxi_passenger_number_third.setOnClickListener(this)
|
||||
tv_taxi_passenger_number_fourth.setOnClickListener(this)
|
||||
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
when (v?.id) {
|
||||
R.id.tv_taxi_passenger_number_one -> {showNumver(1)}
|
||||
R.id.tv_taxi_passenger_number_two -> {showNumver(2)}
|
||||
R.id.tv_taxi_passenger_number_three-> {showNumver(3)}
|
||||
R.id.tv_taxi_passenger_number_four-> {showNumver(4)}
|
||||
R.id.tv_taxi_passenger_number_five -> {showNumver(5)}
|
||||
R.id.tv_taxi_passenger_number_six -> {showNumver(6)}
|
||||
R.id.tv_taxi_passenger_number_seven -> {showNumver(7)}
|
||||
R.id.tv_taxi_passenger_number_eight -> {showNumver(8)}
|
||||
R.id.tv_taxi_passenger_number_nine -> {showNumver(9)}
|
||||
R.id.tv_taxi_passenger_number_zero -> {showNumver(0)}
|
||||
R.id.tv_taxi_passenger_number_back -> {deleteNumver()}
|
||||
R.id.tv_taxi_passenger_number_first -> {selectIndex(0)}
|
||||
R.id.tv_taxi_passenger_number_second -> {selectIndex(1)}
|
||||
R.id.tv_taxi_passenger_number_third -> {selectIndex(2)}
|
||||
R.id.tv_taxi_passenger_number_fourth -> {selectIndex(3)}
|
||||
R.id.tv_taxi_passenger_number_submit -> {clearNumber()}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkAndCommit() {
|
||||
val numberStr = "${numSelect[0]}${numSelect[1]}${numSelect[2]}${numSelect[3]}"
|
||||
if(!phone.endsWith(numberStr)){
|
||||
ToastUtils.showLong("请输入正确的手机尾号")
|
||||
VoiceNotice.showNotice("验证失败!再检查一下吧~", AIAssist.LEVEL2)
|
||||
return
|
||||
}
|
||||
iTaxiPassengerCommonValueCallback?.onCommonCallback(numberStr)
|
||||
}
|
||||
|
||||
private fun selectIndex(i: Int) {
|
||||
index = i
|
||||
changeStyle()
|
||||
}
|
||||
|
||||
private fun showNumver(number: Int) {
|
||||
if (index in 0..3) {
|
||||
numSelect[index] = number
|
||||
numSelectTextView[index]!!.text = number.toString()
|
||||
if(index!=3){
|
||||
index++
|
||||
}
|
||||
changeStyle()
|
||||
numSelect.forEach {
|
||||
if(it==null){
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
checkAndCommit()
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearNumber(){
|
||||
for(i in numSelect.indices){
|
||||
numSelect[i] = null
|
||||
}
|
||||
numSelectTextView.forEach {
|
||||
it?.text = ""
|
||||
}
|
||||
index = 0
|
||||
changeStyle()
|
||||
}
|
||||
|
||||
private fun deleteNumver() {
|
||||
if (index in 0..3) {
|
||||
if(numSelect[index]==null){
|
||||
if(index!=0){
|
||||
index--
|
||||
}
|
||||
changeStyle()
|
||||
//return
|
||||
}
|
||||
numSelect[index] = null
|
||||
numSelectTextView[index]!!.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeStyle() {
|
||||
numSelectTextView.forEachIndexed { indexIn, textView ->
|
||||
if(indexIn==index){
|
||||
numSelectTextView[index]!!.setBackgroundResource(R.drawable.bg_taxi_p_checked_input_background)
|
||||
|
||||
numSelectTextView[index]!!.setTextColor(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.taxi_p_check_keyboard_input_field_checked
|
||||
)
|
||||
)
|
||||
numSelectTextView[index]!!.setShadowLayer(
|
||||
0f, 0f, 0f,
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.taxi_p_check_keyboard_input_field_checked_text_shadow
|
||||
)
|
||||
)
|
||||
}else{
|
||||
numSelectTextView[indexIn]!!.setBackgroundResource(R.drawable.bg_taxi_p_check_input_background)
|
||||
numSelectTextView[indexIn]!!.setTextColor(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.taxi_p_check_keyboard_input_field
|
||||
)
|
||||
)
|
||||
numSelectTextView[indexIn]!!.setShadowLayer(
|
||||
20f,
|
||||
0f,
|
||||
2f,
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.taxi_p_check_keyboard_input_field_checked_text_shadow
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun setData(
|
||||
startSiteAddr: String?,
|
||||
endSiteAddr: String?,
|
||||
passengerNum: String?,
|
||||
phone: String?
|
||||
) {
|
||||
this.phone = phone?:""
|
||||
val sb = SpannableStringBuilder("乘客数:$passengerNum 位") // 包装字体内容
|
||||
sb.setSpan(
|
||||
TextAppearanceSpan("default",
|
||||
Typeface.NORMAL,100,
|
||||
ContextCompat.getColorStateList(context,R.color.taxi_p_check_passenger_number) ,null ),
|
||||
4, 5, Spannable.SPAN_INCLUSIVE_INCLUSIVE)
|
||||
tv_passenger_count.text = sb
|
||||
tv_passenger_start.text = "起 点 : $startSiteAddr"
|
||||
tv_passenger_end.text = "终 点 : $endSiteAddr"
|
||||
for(i in numSelect.indices){
|
||||
numSelect[i] = null
|
||||
}
|
||||
numSelectTextView.forEach {
|
||||
it?.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "TaxiPassengerCheckView"
|
||||
}
|
||||
|
||||
init {
|
||||
try {
|
||||
initView(context)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.mogo.och.taxi.passenger.ui.debug
|
||||
|
||||
class DebugEvent {
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.mogo.och.taxi.passenger.ui.debug
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
|
||||
import com.mogo.eagle.core.utilcode.kotlin.onClick
|
||||
import com.mogo.eagle.core.utilcode.util.ActivityUtils
|
||||
import com.mogo.och.taxi.passenger.R
|
||||
import com.mogo.och.taxi.passenger.ui.TaxiPassengerBaseFragment
|
||||
import kotlinx.android.synthetic.main.taxi_p_debug.view.tv_show_arrive
|
||||
import kotlinx.android.synthetic.main.taxi_p_debug.view.tv_show_phone_check
|
||||
import kotlinx.android.synthetic.main.taxi_p_debug.view.tv_show_start_autopilot
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
class DebugView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr), IMoGoAutopilotStatusListener {
|
||||
|
||||
companion object {
|
||||
const val TAG = "DebugView"
|
||||
}
|
||||
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.taxi_p_debug, this, true)
|
||||
visibility = GONE
|
||||
}
|
||||
|
||||
private var fragment:TaxiPassengerBaseFragment?=null
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
|
||||
EventBus.getDefault().register(this)
|
||||
|
||||
val activityByContext = ActivityUtils.getActivityByContext(context)
|
||||
if(activityByContext is FragmentActivity){
|
||||
val fragment =
|
||||
activityByContext.supportFragmentManager.findFragmentByTag(TaxiPassengerBaseFragment.TAG)
|
||||
if(fragment is TaxiPassengerBaseFragment){
|
||||
this.fragment = fragment
|
||||
}
|
||||
}
|
||||
|
||||
tv_show_arrive.onClick {
|
||||
fragment?.showOrHideArrivedEndLayout(true)
|
||||
}
|
||||
tv_show_phone_check.onClick {
|
||||
fragment?.showOrHidePressengerCheckPager(isShow = true,"13号路口终(鹰眼专用)","13号路口终(鹰眼专用)","2","18811539480")
|
||||
}
|
||||
tv_show_start_autopilot.onClick {
|
||||
fragment?.showOrHideStartAutopilotView(true)
|
||||
}
|
||||
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun changeOverview(debugEvent: DebugEvent) {
|
||||
if(visibility== VISIBLE){
|
||||
visibility = GONE
|
||||
}else{
|
||||
visibility = VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
package com.mogo.och.taxi.passenger.ui.orderinfo
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.graphics.drawable.ScaleDrawable
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.findViewTreeViewModelStoreOwner
|
||||
import com.mogo.och.taxi.passenger.R
|
||||
import kotlinx.android.synthetic.main.taxi_p_itinerary.view.actv_arrived_time
|
||||
import kotlinx.android.synthetic.main.taxi_p_itinerary.view.actv_distance
|
||||
import kotlinx.android.synthetic.main.taxi_p_itinerary.view.actv_distance_unit
|
||||
import kotlinx.android.synthetic.main.taxi_p_itinerary.view.actv_endstation
|
||||
import kotlinx.android.synthetic.main.taxi_p_itinerary.view.actv_speed_value
|
||||
import kotlinx.android.synthetic.main.taxi_p_itinerary.view.actv_surplus_time
|
||||
import kotlinx.android.synthetic.main.taxi_p_itinerary.view.actv_surplus_time_unit
|
||||
import kotlinx.android.synthetic.main.taxi_p_itinerary.view.progress_distance
|
||||
import me.jessyan.autosize.utils.AutoSizeUtils
|
||||
|
||||
class ItineraryView : ConstraintLayout, OrderInfoViewModel.ItineraryViewCallback {
|
||||
|
||||
private val TAG = "ItineraryView"
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)
|
||||
|
||||
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr)
|
||||
|
||||
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attributeSet, defStyleAttr, defStyleRes)
|
||||
|
||||
private fun initView() {
|
||||
LayoutInflater.from(context).inflate(R.layout.taxi_p_itinerary, this, true)
|
||||
setDrawable(true)
|
||||
|
||||
progress_distance.progress = 0
|
||||
progress_distance.max = 100
|
||||
|
||||
}
|
||||
|
||||
private fun setDrawable(normal:Boolean) {
|
||||
val gradientDrawable = GradientDrawable()
|
||||
gradientDrawable.shape = GradientDrawable.RECTANGLE
|
||||
val corner = AutoSizeUtils.dp2px(context, 40f).toFloat()
|
||||
val cornerTop = AutoSizeUtils.dp2px(context, 20f).toFloat()
|
||||
if(normal) {
|
||||
gradientDrawable.cornerRadii =
|
||||
floatArrayOf(0f, 0f, cornerTop, cornerTop, cornerTop, cornerTop, corner, corner)
|
||||
}else {
|
||||
gradientDrawable.cornerRadii =
|
||||
floatArrayOf(0f, 0f, 0f, 0f, corner, corner, corner, corner)
|
||||
}
|
||||
val firstColor = ContextCompat.getColor(context, R.color.taxi_p_OF5FFF)
|
||||
val setondColor = ContextCompat.getColor(context, R.color.taxi_p_44C8FF)
|
||||
val thirdColor = ContextCompat.getColor(context, R.color.taxi_p_8AE4ED)
|
||||
val fourceColor = ContextCompat.getColor(context, R.color.taxi_p_C8F3F4)
|
||||
val bottomColor = ContextCompat.getColor(context, R.color.taxi_p_66476FBE)
|
||||
gradientDrawable.colors = intArrayOf(firstColor, setondColor, thirdColor, fourceColor)
|
||||
gradientDrawable.orientation = GradientDrawable.Orientation.LEFT_RIGHT
|
||||
|
||||
val temp01 = GradientDrawable()
|
||||
temp01.cornerRadii = floatArrayOf(0f, 0f, 0f, 0f, corner, corner, corner, corner)
|
||||
temp01.colors = intArrayOf(bottomColor, bottomColor)
|
||||
|
||||
val scaleDrawable3 = ScaleDrawable(gradientDrawable, Gravity.START, 1f, -1f)
|
||||
val arr = arrayOf(temp01, scaleDrawable3)
|
||||
val ld = LayerDrawable(arr)
|
||||
ld.setDrawableByLayerId(android.R.id.background, temp01)
|
||||
ld.setDrawableByLayerId(android.R.id.progress, scaleDrawable3)
|
||||
progress_distance.setProgressDrawableTiled(ld)
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
|
||||
val viewModel = findViewTreeViewModelStoreOwner()?.let {
|
||||
ViewModelProvider(it).get(OrderInfoViewModel::class.java)
|
||||
}
|
||||
|
||||
viewModel?.setDistanceCallback(this)
|
||||
}
|
||||
|
||||
override fun setSpeed(speedValue:String){
|
||||
actv_speed_value.text = speedValue
|
||||
}
|
||||
override fun setEndStation(endStation:String){
|
||||
if(endStation.length>9){
|
||||
actv_endstation.text = "${endStation.subSequence(0,9)}…"
|
||||
}else {
|
||||
actv_endstation.text = endStation
|
||||
}
|
||||
}
|
||||
|
||||
var prePercentage = 0f
|
||||
val needChangeStyleNumber = 0.99
|
||||
|
||||
override fun setDistanceInfo(surplusdistance:String,distanceUnit:String,
|
||||
surplusTime:String,surplusTimeUnit:String,
|
||||
arrivedTime:String,alreadyGone:Int,stationDistance:Int
|
||||
){
|
||||
actv_distance.text = surplusdistance
|
||||
actv_distance_unit.text = distanceUnit
|
||||
actv_surplus_time .text= surplusTime
|
||||
actv_surplus_time_unit.text = surplusTimeUnit
|
||||
actv_arrived_time.text= arrivedTime
|
||||
|
||||
if(stationDistance>0&&alreadyGone<stationDistance){
|
||||
|
||||
if (progress_distance.max != stationDistance) {
|
||||
progress_distance.max = stationDistance
|
||||
}
|
||||
|
||||
val currentPercentage = alreadyGone.toFloat() / stationDistance
|
||||
if((prePercentage>needChangeStyleNumber) xor (currentPercentage>needChangeStyleNumber)){
|
||||
if(currentPercentage>needChangeStyleNumber){
|
||||
setDrawable(false)
|
||||
}else{
|
||||
setDrawable(true)
|
||||
}
|
||||
progress_distance.progress = alreadyGone
|
||||
}else{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
progress_distance.setProgress(alreadyGone,true)
|
||||
}else{
|
||||
progress_distance.progress = alreadyGone
|
||||
}
|
||||
}
|
||||
prePercentage = currentPercentage
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
init {
|
||||
try {
|
||||
initView()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.mogo.och.taxi.passenger.ui.orderinfo
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.mogo.commons.AbsMogoApplication
|
||||
import com.mogo.eagle.core.data.map.MogoLocation
|
||||
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisLocationGCJ02Listener
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationGCJ02ListenerManager
|
||||
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
|
||||
import com.mogo.och.common.module.utils.DateTimeUtil
|
||||
import com.mogo.och.common.module.utils.NumberFormatUtil
|
||||
import com.mogo.och.taxi.passenger.R
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrderQueryRespBean
|
||||
import com.mogo.och.taxi.passenger.callback.IOCHTaxiPassengerOrderStatusCallback
|
||||
import com.mogo.och.taxi.passenger.constant.TaxiPassengerOrderStatusEnum
|
||||
import com.mogo.och.taxi.passenger.model.TaxiPassengerModel
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.ceil
|
||||
|
||||
class OrderInfoViewModel: ViewModel(), IMoGoChassisLocationGCJ02Listener,
|
||||
IOCHTaxiPassengerOrderStatusCallback {
|
||||
|
||||
private val TAG = OrderInfoViewModel::class.java.simpleName
|
||||
|
||||
private var viewCallback:ItineraryViewCallback?=null
|
||||
|
||||
private var disUnit:String
|
||||
private var surplusTimeUnit:String
|
||||
|
||||
init {
|
||||
disUnit = AbsMogoApplication.getApp().getString(R.string.taxi_p_distance_unit_km)
|
||||
surplusTimeUnit = AbsMogoApplication.getApp().getString(R.string.taxi_p_surplustime)
|
||||
// 设置起点和终点marker和实时车辆位置
|
||||
CallerChassisLocationGCJ02ListenerManager.addListener(TAG, 4, this)
|
||||
TaxiPassengerModel.setOrderStatusCallback(TAG,this)
|
||||
}
|
||||
|
||||
fun setDistanceCallback(viewCallback:ItineraryViewCallback){
|
||||
this.viewCallback = viewCallback
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
this.viewCallback = null
|
||||
TaxiPassengerModel.setOrderStatusCallback(TAG,null)
|
||||
}
|
||||
|
||||
override fun onChassisLocationGCJ02(mogoLocation: MogoLocation?) {
|
||||
mogoLocation?.let {
|
||||
UiThreadHandler.post {
|
||||
val speedKM = (abs(it.gnssSpeed) * 3.6f).toInt()
|
||||
viewCallback?.setSpeed(speedKM.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ItineraryViewCallback{
|
||||
fun setDistanceInfo(surplusdistance:String,distanceUnit:String,
|
||||
surplusTime:String,surplusTimeUnit:String,
|
||||
arrivedTime:String,alreadyGone:Int,distance:Int
|
||||
)
|
||||
fun setEndStation(endStation:String)
|
||||
fun setSpeed(speedValue:String)
|
||||
}
|
||||
|
||||
override fun onCurrentOrderStatusChanged(order: TaxiPassengerOrderQueryRespBean.Result?) {
|
||||
order?.endSiteAddr?.let {
|
||||
UiThreadHandler.post {
|
||||
viewCallback?.setEndStation(it)
|
||||
}
|
||||
}
|
||||
when (TaxiPassengerModel.curOrderStatus) {
|
||||
TaxiPassengerOrderStatusEnum.OnTheWayToEnd -> {
|
||||
|
||||
}
|
||||
else ->{
|
||||
UiThreadHandler.post {
|
||||
viewCallback?.setDistanceInfo(
|
||||
"--", disUnit, "--", surplusTimeUnit, "--", 0, 100
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCurrentOrderDistToEndChanged(meters: Long, timeInSecond: Long,stationDistance:Int) {
|
||||
var dis: String? = "0"
|
||||
var disUnit = "KM"
|
||||
if (meters > 0) {
|
||||
if (meters / 1000 < 1) {
|
||||
disUnit = AbsMogoApplication.getApp().getString(R.string.taxi_p_distance_unit_m)
|
||||
dis = Math.round(meters.toFloat()).toString()
|
||||
} else {
|
||||
disUnit = AbsMogoApplication.getApp().getString(R.string.taxi_p_distance_unit_km)
|
||||
dis = NumberFormatUtil.formatLong(meters.toDouble() / 1000)
|
||||
}
|
||||
}
|
||||
val time = ceil(timeInSecond / 60f).toInt()
|
||||
val arriveTime = DateTimeUtil.getAfterSecondTime(timeInSecond.toInt())
|
||||
|
||||
UiThreadHandler.post {
|
||||
viewCallback?.setDistanceInfo(
|
||||
dis!!,disUnit,time.toString(),surplusTimeUnit,arriveTime,stationDistance-meters.toInt(),stationDistance)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
package com.mogo.och.taxi.passenger.ui.startautopilot
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.elegant.utils.UiThreadHandler
|
||||
import com.mogo.eagle.core.utilcode.kotlin.onClick
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
|
||||
import com.mogo.eagle.core.utilcode.util.OverlayViewUtils
|
||||
import com.mogo.eagle.core.utilcode.util.ToastUtils
|
||||
import com.mogo.och.taxi.passenger.R
|
||||
import com.mogo.och.taxi.passenger.widget.WindowRelativeLayout
|
||||
import com.mogo.och.taxi.passenger.widget.animutils.AnimationsContainer
|
||||
import kotlinx.android.synthetic.main.taxi_p_start_autopilot_view.view.actv_front_left_door
|
||||
import kotlinx.android.synthetic.main.taxi_p_start_autopilot_view.view.actv_front_right_door
|
||||
import kotlinx.android.synthetic.main.taxi_p_start_autopilot_view.view.actv_orderinfo
|
||||
import kotlinx.android.synthetic.main.taxi_p_start_autopilot_view.view.actv_rear_left_door
|
||||
import kotlinx.android.synthetic.main.taxi_p_start_autopilot_view.view.actv_rear_right_door
|
||||
import kotlinx.android.synthetic.main.taxi_p_start_autopilot_view.view.iv_xiaozhi_belt
|
||||
import kotlinx.android.synthetic.main.taxi_p_start_autopilot_view.view.starting_autopilot_view_close
|
||||
import kotlinx.android.synthetic.main.taxi_p_start_autopilot_view.view.taxi_p_autopilot_btn_bg
|
||||
import kotlinx.android.synthetic.main.taxi_p_start_autopilot_view.view.taxi_p_autopilot_starting
|
||||
import kotlinx.android.synthetic.main.taxi_p_start_autopilot_view.view.taxi_p_start_autopilot
|
||||
|
||||
/**
|
||||
* @author: wangmingjun
|
||||
* @date: 2022/6/14
|
||||
*/
|
||||
class StartAutopilotView : WindowRelativeLayout, StartAutopilotViewModel.StartAutopilotCallback{
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)
|
||||
|
||||
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr)
|
||||
|
||||
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attributeSet, defStyleAttr, defStyleRes)
|
||||
|
||||
companion object {
|
||||
private val TAG = StartAutopilotView::class.java.simpleName
|
||||
private const val TIMER_START_AUTOPILOT_INTERVAL = 20 * 1000L
|
||||
}
|
||||
|
||||
var isStarting = false
|
||||
|
||||
private var taxiPStartAutopilot: AnimationsContainer?=null
|
||||
private var taxiPStartAutopilotCar: AnimationsContainer?=null
|
||||
private var taxiPXiaozhiBelt: AnimationsContainer?=null
|
||||
|
||||
|
||||
init {
|
||||
initView()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
LayoutInflater.from(context).inflate(R.layout.taxi_p_start_autopilot_view, this, true)
|
||||
taxiPStartAutopilotCar = AnimationsContainer(R.array.taxi_p_start_autopilot_car, 20,taxi_p_autopilot_starting)
|
||||
taxiPStartAutopilotCar?.setOnAnimStopListener(object :AnimationsContainer.OnAnimationStoppedListener{
|
||||
override fun AnimationStopped() {
|
||||
CallerLogger.d(SceneConstant.M_TAXI_P + TAG, "动画暂停")
|
||||
}
|
||||
})
|
||||
taxiPStartAutopilot = AnimationsContainer(R.array.taxi_p_start_autopilot, 15,taxi_p_autopilot_btn_bg)
|
||||
taxiPStartAutopilot?.setOnAnimStopListener(object :AnimationsContainer.OnAnimationStoppedListener{
|
||||
override fun AnimationStopped() {
|
||||
CallerLogger.d(SceneConstant.M_TAXI_P + TAG, "动画暂停")
|
||||
}
|
||||
})
|
||||
taxiPXiaozhiBelt = AnimationsContainer(R.array.xiaozhi_belt, 15,iv_xiaozhi_belt)
|
||||
taxiPXiaozhiBelt?.setOnAnimStopListener(object :AnimationsContainer.OnAnimationStoppedListener{
|
||||
override fun AnimationStopped() {
|
||||
CallerLogger.d(SceneConstant.M_TAXI_P + TAG, "动画暂停")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun startAutopilotBgAnimatorDrawable(isStart: Boolean) {
|
||||
if (isStart) {
|
||||
taxiPStartAutopilot?.start()
|
||||
} else {
|
||||
taxiPStartAutopilot?.stop()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("UseCompatLoadingForDrawables")
|
||||
override fun handleStartAutopilotBtnStatus(isBoarded: Boolean) {
|
||||
taxi_p_autopilot_starting?.setImageResource(R.drawable.light_00003)
|
||||
updateStartAutopilotBtnStatus(isBoarded)
|
||||
if (isBoarded) { //高亮可点击状态下动画一直进行
|
||||
startAutopilotBgAnimatorDrawable(true)
|
||||
} else { // 置灰色可点击状态下动画停止
|
||||
startAutopilotBgAnimatorDrawable(false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setOrderInfo(show: String) {
|
||||
actv_orderinfo.text = show
|
||||
}
|
||||
|
||||
override fun setDoorStatus(
|
||||
doorPosition: StartAutopilotViewModel.DoorPosition,
|
||||
isOpen: Boolean
|
||||
) {
|
||||
when (doorPosition) {
|
||||
StartAutopilotViewModel.DoorPosition.FRONT_LEFT -> {
|
||||
if(isOpen){
|
||||
actv_front_left_door.visibility = VISIBLE
|
||||
}else{
|
||||
actv_front_left_door.visibility = GONE
|
||||
}
|
||||
}
|
||||
StartAutopilotViewModel.DoorPosition.FRONT_RIGHT -> {
|
||||
if(isOpen){
|
||||
actv_front_right_door.visibility = VISIBLE
|
||||
}else{
|
||||
actv_front_right_door.visibility = GONE
|
||||
}
|
||||
}
|
||||
StartAutopilotViewModel.DoorPosition.REAR_LEFT -> {
|
||||
if(isOpen){
|
||||
actv_rear_left_door.visibility = VISIBLE
|
||||
}else{
|
||||
actv_rear_left_door.visibility = GONE
|
||||
}
|
||||
}
|
||||
StartAutopilotViewModel.DoorPosition.REAR_RIGHT -> {
|
||||
if(isOpen){
|
||||
actv_rear_right_door.visibility = VISIBLE
|
||||
}else{
|
||||
actv_rear_right_door.visibility = GONE
|
||||
}
|
||||
}
|
||||
else ->{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun closeAllAnimsAndView() {
|
||||
isStarting = false
|
||||
clearStartingAnimFrame()
|
||||
clearBgAnimDrawable()
|
||||
OverlayViewUtils.dismissOverlayView(this)
|
||||
}
|
||||
|
||||
fun updateStartAutopilotBtnStatus(isBoarded: Boolean) {
|
||||
taxi_p_start_autopilot?.let {
|
||||
if (isBoarded) {
|
||||
it.setTextColor(ContextCompat.getColor(context,R.color.taxi_p_start_autopilot_txt_color))
|
||||
it.background = null
|
||||
} else {
|
||||
it.background = ResourcesCompat.getDrawable(resources,R.drawable.taxi_p_start_autopilot_txt_btn_bg,null)
|
||||
taxi_p_autopilot_btn_bg!!.background = null
|
||||
it.setTextColor( ContextCompat.getColor(context,R.color.taxi_p_start_autopilot_txt_un_color))
|
||||
}
|
||||
it.tag = isBoarded
|
||||
it.text = resources.getString(R.string.taxi_p_start_autopilot_txt)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun startingCarBgAnimatorDrawable(isStart: Boolean) {
|
||||
if (isStart) {
|
||||
taxi_p_autopilot_starting!!.setImageResource(0)
|
||||
taxiPStartAutopilotCar?.start()
|
||||
} else {
|
||||
taxiPStartAutopilotCar?.stop()
|
||||
taxi_p_autopilot_starting!!.setImageResource(R.drawable.light_00003)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startOrStopLoadingAnim(start: Boolean) {
|
||||
startingCarBgAnimatorDrawable(start)
|
||||
if (start) {
|
||||
isStarting = true
|
||||
taxi_p_start_autopilot?.text = resources.getString(R.string.taxi_p_start_autopilot_loading)
|
||||
taxi_p_start_autopilot?.setTextColor(ContextCompat.getColor(context,R.color.taxi_p_start_autopilot_txt_color))
|
||||
startingAutopilotCountDown()
|
||||
} else {
|
||||
clearBgAnimDrawable()
|
||||
isStarting = false
|
||||
handleStartAutopilotBtnStatus(true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearBgAnimDrawable() {
|
||||
taxiPStartAutopilot?.stop()
|
||||
}
|
||||
|
||||
fun clearStartingAnimFrame() {
|
||||
taxiPStartAutopilotCar?.stop()
|
||||
}
|
||||
|
||||
private fun startingAutopilotCountDown() {
|
||||
UiThreadHandler.postDelayed({
|
||||
//未启动成功20s后做处理
|
||||
if (isStarting) { //判断动画是否在进行
|
||||
ToastUtils.showLong(R.string.taxi_p_start_autopilot_fail_10s_tip)
|
||||
updateStatusCountDownOver()
|
||||
}
|
||||
}, TIMER_START_AUTOPILOT_INTERVAL)
|
||||
}
|
||||
|
||||
private fun updateStatusCountDownOver() {
|
||||
isStarting = false
|
||||
startingCarBgAnimatorDrawable(false)
|
||||
taxi_p_start_autopilot?.text = resources.getString(R.string.taxi_p_start_autopilot_txt)
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
val viewModel = ViewModelProvider(this).get(StartAutopilotViewModel::class.java)
|
||||
viewModel.setStartAutopilotCallback(this)
|
||||
taxi_p_start_autopilot.onClick {
|
||||
//开启动画和自动驾驶
|
||||
if (!(taxi_p_start_autopilot!!.tag as Boolean)) {
|
||||
ToastUtils.showLong(R.string.taxi_p_start_autopilot_un_click_tip)
|
||||
return@onClick
|
||||
}
|
||||
if (!isStarting) {
|
||||
startOrStopLoadingAnim(true)
|
||||
viewModel.startAutopilot()
|
||||
}
|
||||
}
|
||||
starting_autopilot_view_close.onClick {
|
||||
closeAllAnimsAndView()
|
||||
}
|
||||
taxiPXiaozhiBelt?.start()
|
||||
}
|
||||
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
isStarting = false
|
||||
clearStartingAnimFrame()
|
||||
clearBgAnimDrawable()
|
||||
taxiPXiaozhiBelt?.stop()
|
||||
super.onDetachedFromWindow()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.mogo.och.taxi.passenger.ui.startautopilot
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import chassis.Chassis.DoorNumber
|
||||
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisDoorStateListener
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerChassisDoorStateListenerManager
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
|
||||
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
|
||||
import com.mogo.och.taxi.passenger.callback.IOCHTaxiPassengerOrderStatusCallback
|
||||
import com.mogo.och.taxi.passenger.model.AutopilotManager
|
||||
import com.mogo.och.taxi.passenger.model.TaxiPassengerModel
|
||||
|
||||
class StartAutopilotViewModel : ViewModel(), IOCHTaxiPassengerOrderStatusCallback,
|
||||
IMoGoChassisDoorStateListener {
|
||||
|
||||
private val TAG = StartAutopilotViewModel::class.java.simpleName
|
||||
|
||||
private var viewCallback: StartAutopilotCallback? = null
|
||||
|
||||
init {
|
||||
TaxiPassengerModel.setOrderStatusCallback(TAG, this)
|
||||
CallerChassisDoorStateListenerManager.addListener(TAG, this)
|
||||
}
|
||||
|
||||
fun setStartAutopilotCallback(viewCallback: StartAutopilotCallback) {
|
||||
this.viewCallback = viewCallback
|
||||
TaxiPassengerModel.startOrStopReadyToAutopilotLoop(true)
|
||||
setOrderInfo()
|
||||
setDoorInfo()
|
||||
}
|
||||
|
||||
private fun setDoorInfo() {
|
||||
val doorList = CallerChassisDoorStateListenerManager.getDoorList()
|
||||
CallerLogger.d(SceneConstant.M_TAXI_P + TAG, "门太变化初始化:${doorList}")
|
||||
doorList?.forEach {
|
||||
exchangeEnum(it.number,it.status==1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setOrderInfo() {
|
||||
val currentOCHOrder = TaxiPassengerModel.currentOCHOrder
|
||||
currentOCHOrder?.let {
|
||||
val phone = it.passengerPhone
|
||||
val show = if (phone.length > 8) {
|
||||
//截取电话号码前三位
|
||||
val phoneNumPre = phone.substring(0, 3)
|
||||
//截取电话号码后四位
|
||||
val phoneNumFix = phone.substring(7)
|
||||
|
||||
"用户:$phoneNumPre****$phoneNumFix 目的地:${it.endSiteAddr}"
|
||||
} else {
|
||||
"用户:${phone} 目的地:${it.endSiteAddr}"
|
||||
}
|
||||
viewCallback?.setOrderInfo(show)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
this.viewCallback = null
|
||||
TaxiPassengerModel.setOrderStatusCallback(TAG, null)
|
||||
CallerChassisDoorStateListenerManager.removeListener(TAG)
|
||||
}
|
||||
|
||||
override fun onDriverHasCheckedPilotCondition(isBoarded: Boolean) {
|
||||
viewCallback?.handleStartAutopilotBtnStatus(isBoarded)
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启自动驾驶
|
||||
*/
|
||||
fun startAutopilot() {
|
||||
AutopilotManager.startAutopilot()
|
||||
}
|
||||
|
||||
override fun onAutopilotSingleDoorState(num: DoorNumber, open: Boolean) {
|
||||
super.onAutopilotSingleDoorState(num, open)
|
||||
CallerLogger.d(SceneConstant.M_TAXI_P + TAG, "门太变化:${num}--${open}")
|
||||
exchangeEnum(num,open)
|
||||
}
|
||||
|
||||
private fun exchangeEnum(num: DoorNumber,open: Boolean){
|
||||
when (num) {
|
||||
DoorNumber.FRONT_LEFT -> {
|
||||
runMain(DoorPosition.FRONT_LEFT,open)
|
||||
}
|
||||
|
||||
DoorNumber.FRONT_RIGHT -> {
|
||||
runMain(DoorPosition.FRONT_RIGHT,open)
|
||||
}
|
||||
|
||||
DoorNumber.REAR_LEFT -> {
|
||||
runMain(DoorPosition.REAR_LEFT,open)
|
||||
}
|
||||
|
||||
DoorNumber.REAR_RIGHT -> {
|
||||
runMain(DoorPosition.REAR_RIGHT,open)
|
||||
}
|
||||
|
||||
DoorNumber.MIDDLE -> {
|
||||
runMain(DoorPosition.MIDDLE,open)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun runMain(posttion:DoorPosition,isOpen: Boolean){
|
||||
UiThreadHandler.post {
|
||||
viewCallback?.setDoorStatus(posttion,isOpen)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface StartAutopilotCallback {
|
||||
fun handleStartAutopilotBtnStatus(isBoarded: Boolean)
|
||||
fun setOrderInfo(show: String)
|
||||
fun setDoorStatus(doorPosition: DoorPosition,isOpen:Boolean)
|
||||
}
|
||||
|
||||
enum class DoorPosition {
|
||||
FRONT_LEFT, FRONT_RIGHT, REAR_LEFT, REAR_RIGHT, MIDDLE
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.mogo.och.taxi.passenger.ui.statusview
|
||||
|
||||
import android.content.Context
|
||||
import android.os.SystemClock
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
|
||||
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager
|
||||
import com.mogo.eagle.core.function.call.hmi.CallerHmiViewControlListenerManager
|
||||
import com.mogo.eagle.core.function.call.setting.CallerSkinModeListenerManager
|
||||
import com.mogo.eagle.core.function.hmi.ui.setting.ToggleDebugView
|
||||
import com.mogo.eagle.core.utilcode.util.ActivityUtils
|
||||
import com.mogo.och.common.module.manager.debug.DebugViewWatchDogFragment
|
||||
import com.mogo.och.taxi.passenger.R
|
||||
import com.mogo.och.taxi.passenger.ui.debug.DebugEvent
|
||||
import kotlinx.android.synthetic.main.taxi_p_statusview.view.iv_biz_icon
|
||||
import kotlinx.android.synthetic.main.taxi_p_statusview.view.vShowDebugView
|
||||
import me.jessyan.autosize.utils.AutoSizeUtils
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class StatusBarView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr), IMoGoAutopilotStatusListener {
|
||||
|
||||
companion object {
|
||||
const val TAG = "StatusBarView"
|
||||
private const val COUNTS = 4 // 点击次数
|
||||
private const val DURATION: Long = 1000 // 规定有效时间
|
||||
}
|
||||
|
||||
private var debugViewWatchDogFragment: WeakReference<DebugViewWatchDogFragment>? = null
|
||||
|
||||
private var mHits = LongArray(COUNTS)
|
||||
|
||||
private fun continuousClick() {
|
||||
if (ToggleDebugView.toggleDebugView.isShowIng()) {
|
||||
ToggleDebugView.toggleDebugView.dismiss()
|
||||
return
|
||||
}
|
||||
//每次点击时,数组向前移动一位
|
||||
System.arraycopy(mHits, 1, mHits, 0, mHits.size - 1)
|
||||
//为数组最后一位赋值
|
||||
mHits[mHits.size - 1] = SystemClock.uptimeMillis()
|
||||
if (mHits[0] >= (SystemClock.uptimeMillis() - DURATION)) {
|
||||
mHits = LongArray(COUNTS) //重新初始化数组
|
||||
showDebugView()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.taxi_p_statusview, this, true)
|
||||
setBackgroundResource(R.drawable.taxi_p_status_bg)
|
||||
isClickable = true
|
||||
isFocusable = true
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
post {
|
||||
val params: ViewGroup.LayoutParams = layoutParams
|
||||
params.height = AutoSizeUtils.dp2px(context,120f)
|
||||
layoutParams = params
|
||||
}
|
||||
|
||||
CallerAutoPilotStatusListenerManager.addListener(TAG, this)
|
||||
|
||||
iv_biz_icon.setOnClickListener { continuousClick() }
|
||||
vShowDebugView.setOnLongClickListener {
|
||||
EventBus.getDefault().post(DebugEvent())
|
||||
false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun showDebugView() {
|
||||
if (debugViewWatchDogFragment?.get() == null) {
|
||||
debugViewWatchDogFragment = WeakReference(DebugViewWatchDogFragment.newInstance())
|
||||
}
|
||||
val debugViewFragment = debugViewWatchDogFragment?.get()
|
||||
if (ActivityUtils.getTopActivity() is FragmentActivity) {
|
||||
val fragmentActivity = ActivityUtils.getTopActivity() as FragmentActivity
|
||||
DebugViewWatchDogFragment.showDebugView(fragmentActivity.supportFragmentManager,fragmentActivity.supportFragmentManager,debugViewFragment)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
CallerHmiViewControlListenerManager.removeListener(TAG)
|
||||
CallerSkinModeListenerManager.removeListener(TAG)
|
||||
CallerDevaToolsManager.hideStatusBar()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.mogo.och.taxi.passenger.ui.statusview
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.RelativeLayout
|
||||
import com.mogo.eagle.core.function.api.devatools.mofang.IMoGoMoFangProvider
|
||||
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager
|
||||
import com.mogo.och.taxi.passenger.R
|
||||
import kotlinx.android.synthetic.main.taxi_p_blue_tooth.view.mofangView
|
||||
|
||||
/**
|
||||
* 魔戒蓝牙控件
|
||||
* 放置于StatusBar右侧位置
|
||||
* todo arrow
|
||||
*/
|
||||
class TaxiPBlueToothView : RelativeLayout, IMoGoMoFangProvider.OnMoFangStatusListener {
|
||||
|
||||
companion object{
|
||||
const val TAG = "TaxiPBlueToothView"
|
||||
}
|
||||
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : this(context, attrs, defStyleAttr, 0)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
|
||||
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.taxi_p_blue_tooth, this, true)
|
||||
}
|
||||
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
CallerDevaToolsManager.mofang()?.registerMoFangStatusListener(TAG, this)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
CallerDevaToolsManager.mofang()?.unRegisterMoFangStatusListener(this)
|
||||
}
|
||||
|
||||
override fun onMoFangConnected() {
|
||||
mofangView.setImageResource(R.drawable.taxi_p_blue_tooth_close)
|
||||
}
|
||||
|
||||
override fun onMoFangDisconnected() {
|
||||
mofangView.setImageResource(R.drawable.taxi_p_blue_tooth_open)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onMoFangBatteryChanged(battery: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onMoFangClicked(keyCode: Int) {}
|
||||
|
||||
override fun onMoFangLongClicked(keyCode: Int) {}
|
||||
|
||||
override fun onMoFangCombineClicked(vararg keyCodes: Int) {}
|
||||
|
||||
override fun onMoFangStatusError(msg: String) {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.mogo.och.taxi.passenger.ui.video
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import com.mogo.och.taxi.passenger.widget.ConsultVideoPlayer
|
||||
import com.shuyu.gsyvideoplayer.GSYVideoManager
|
||||
import java.lang.Exception
|
||||
|
||||
/**
|
||||
* 视频全屏播放
|
||||
*
|
||||
* @author yangyakun
|
||||
*/
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object FullVideoUtils {
|
||||
private const val TAG = "FullVideoUtils"
|
||||
private var windowManager: WindowManager? = null
|
||||
|
||||
@Volatile
|
||||
private var isShowing = false
|
||||
|
||||
/**
|
||||
* 记录上一次的View
|
||||
*/
|
||||
private var lastOverlayView: View? = null
|
||||
|
||||
/**
|
||||
* 添加覆盖View在Activity上面
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun showOverlayView(context: Activity, overlayView: View, ani: Int = -1) {
|
||||
if (windowManager == null) {
|
||||
windowManager = context.windowManager
|
||||
}
|
||||
|
||||
// 设置View显示模式,沉浸式的侵入到状态栏,导航栏
|
||||
overlayView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
|
||||
val params = WindowManager.LayoutParams()
|
||||
params.width = WindowManager.LayoutParams.MATCH_PARENT
|
||||
params.height = WindowManager.LayoutParams.MATCH_PARENT
|
||||
params.alpha = 1.0f
|
||||
// 设置窗口类型为应用子窗口,和PopupWindow同类型
|
||||
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL
|
||||
// 没有边界限制,允许窗口扩展到屏幕外
|
||||
params.flags = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
|
||||
if (ani != -1) {
|
||||
params.windowAnimations = ani
|
||||
}
|
||||
try {
|
||||
// 后门逻辑,长时间触摸消失
|
||||
lastOverlayView = overlayView
|
||||
windowManager!!.addView(overlayView, params)
|
||||
isShowing = true
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除覆盖View在Activity上面
|
||||
*/
|
||||
fun dismissOverlayView(needReleas:Boolean) {
|
||||
if (!isShowing) {
|
||||
return
|
||||
}
|
||||
val consultVideoPlayer =
|
||||
lastOverlayView?.findViewById<ConsultVideoPlayer>(GSYVideoManager.FULLSCREEN_ID)
|
||||
consultVideoPlayer?.let {
|
||||
if(needReleas){
|
||||
it.onVideoReset()
|
||||
it.setVideoAllCallBack(null)
|
||||
it.smalllPlayer?.clearFullscreenLayout(it)
|
||||
}
|
||||
consultVideoPlayer.removeAllViews()
|
||||
}
|
||||
try {
|
||||
if (windowManager != null) {
|
||||
windowManager!!.removeViewImmediate(lastOverlayView)
|
||||
windowManager = null
|
||||
}
|
||||
if (lastOverlayView != null) {
|
||||
lastOverlayView = null
|
||||
}
|
||||
isShowing = false
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package com.mogo.och.taxi.passenger.ui.video
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.mogo.och.taxi.passenger.R
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerVideoPlay
|
||||
import com.mogo.och.taxi.passenger.ui.video.layoutmanage.CarouselLayoutManager
|
||||
import com.mogo.och.taxi.passenger.ui.video.layoutmanage.CarouselZoomPostLayoutListener
|
||||
import com.mogo.och.taxi.passenger.ui.video.layoutmanage.CenterScrollListener
|
||||
import com.mogo.och.taxi.passenger.widget.ConsultVideoPlayer
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.IndicatorView
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorOrientation
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorSlideMode
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorStyle
|
||||
import com.shuyu.gsyvideoplayer.video.base.GSYVideoView
|
||||
import kotlin.math.floor
|
||||
|
||||
/**
|
||||
* @author ChenFufeng
|
||||
* 蘑菇资讯视频
|
||||
*/
|
||||
internal class InfoVideoView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : FrameLayout(
|
||||
context,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
) {
|
||||
|
||||
companion object{
|
||||
private const val TAG = "VideoView"
|
||||
}
|
||||
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.taxi_p_mogo_video_layout, this, true)
|
||||
initView()
|
||||
}
|
||||
|
||||
private var rvVideoPlaylist: RecyclerView? = null
|
||||
private lateinit var indicatorView: IndicatorView
|
||||
private lateinit var clContain: ConstraintLayout
|
||||
|
||||
private val arrayListOf by lazy {
|
||||
arrayListOf<TaxiPassengerVideoPlay>().apply {
|
||||
add(TaxiPassengerVideoPlay(
|
||||
"https://img.zhidaohulian.com/fileServer/online_car_hailing/1655708596763/全车型混剪增加红旗车队.m4v",
|
||||
"https://img.zhidaohulian.com/fileServer/online_car_hailing/1655969511280/车队.png",
|
||||
"蘑菇车联覆盖生活的方方面面"
|
||||
))
|
||||
add(TaxiPassengerVideoPlay(
|
||||
"https://img.zhidaohulian.com/fileServer/online_car_hailing/1655708554279/红旗车队.m4v",
|
||||
"https://img.zhidaohulian.com/fileServer/online_car_hailing/1655969553174/红旗重新排版.png",
|
||||
"蘑菇车联之红旗车队"
|
||||
))
|
||||
add(
|
||||
TaxiPassengerVideoPlay(
|
||||
"https://img.zhidaohulian.com/fileServer/online_car_hailing/1655708499497/大运会合作解说版.m4v",
|
||||
"https://img.zhidaohulian.com/fileServer/online_car_hailing/1655969536177/大运会.png",
|
||||
"蘑菇车联牵手成都大运会"
|
||||
)
|
||||
)
|
||||
add(
|
||||
TaxiPassengerVideoPlay(
|
||||
"https://img.zhidaohulian.com/fileServer/online_car_hailing/1655708409810/20210610重新排版3屏.m4v",
|
||||
"https://img.zhidaohulian.com/fileServer/online_car_hailing/1655969579713/三屏.png",
|
||||
"多视角体验蘑菇车联自动驾驶"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun exitFullScreenMode(resetVideoPlayer: Boolean) {
|
||||
val carouselLayoutManager = rvVideoPlaylist?.layoutManager as CarouselLayoutManager
|
||||
val (_: Int, player) = getPlayer(carouselLayoutManager)
|
||||
player?.let {
|
||||
it.exitFullScreenMode()
|
||||
it.onVideoPause()
|
||||
if(resetVideoPlayer) {
|
||||
it.onVideoReset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
configPage()
|
||||
}
|
||||
|
||||
override fun onVisibilityChanged(changedView: View, visibility: Int) {
|
||||
super.onVisibilityChanged(changedView, visibility)
|
||||
if(changedView!=this){
|
||||
return
|
||||
}
|
||||
val carouselLayoutManager = rvVideoPlaylist?.layoutManager as CarouselLayoutManager
|
||||
val (_: Int, player) = getPlayer(carouselLayoutManager)
|
||||
when (visibility) {
|
||||
View.VISIBLE -> {
|
||||
player?.let {
|
||||
if (!player.isIfCurrentIsFullscreen) {
|
||||
when (player.currentState) {
|
||||
GSYVideoView.CURRENT_STATE_PAUSE -> {
|
||||
//player.onVideoResume(false)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
player?.let {
|
||||
if (!player.isIfCurrentIsFullscreen) {
|
||||
player.onVideoPause()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
rvVideoPlaylist = findViewById(R.id.infoVideoPlaylist)
|
||||
indicatorView = findViewById(R.id.infoIndicatorView)
|
||||
clContain = findViewById(R.id.infoContainer)
|
||||
}
|
||||
|
||||
private fun configPage() {
|
||||
// FullVideoUtils.dismissOverlayView(true)
|
||||
initData()
|
||||
}
|
||||
|
||||
private fun initData() {
|
||||
val carouselLayoutManager = CarouselLayoutManager(CarouselLayoutManager.HORIZONTAL, true)
|
||||
carouselLayoutManager.setPostLayoutListener(CarouselZoomPostLayoutListener())
|
||||
carouselLayoutManager.maxVisibleItems = 1
|
||||
indicatorView.notifyDataChanged(arrayListOf.size)
|
||||
indicatorView.setSlideMode(IndicatorSlideMode.SCALE)
|
||||
indicatorView.setOrientation(IndicatorOrientation.INDICATOR_HORIZONTAL)
|
||||
indicatorView.setIndicatorStyle(IndicatorStyle.ROUND_RECT)
|
||||
indicatorView.setSliderColor(
|
||||
Color.parseColor("#80000000"), Color.parseColor("#3FACFD"),
|
||||
Color.parseColor("#3FACFD")
|
||||
)
|
||||
indicatorView.setSliderWidth(9f, 54f)
|
||||
indicatorView.setSliderHeight(9f)
|
||||
indicatorView.setSliderGap(36f)
|
||||
rvVideoPlaylist?.addOnScrollListener(object : CenterScrollListener() {
|
||||
var prePlayerPosition = 0
|
||||
override fun pageSelect(recyclerView: RecyclerView?, newState: Int) {
|
||||
//播放视频
|
||||
val (centerItemPosition: Int, player) = getPlayer(carouselLayoutManager)
|
||||
indicatorView.onPageSelected(centerItemPosition)
|
||||
if (player is ConsultVideoPlayer) {
|
||||
if (prePlayerPosition != centerItemPosition) {
|
||||
if (player.currentState == GSYVideoView.CURRENT_STATE_PAUSE) {
|
||||
player.onVideoReset()
|
||||
}
|
||||
val playerHolder =
|
||||
carouselLayoutManager.findViewByPosition(prePlayerPosition)
|
||||
val prePlayer =
|
||||
playerHolder?.findViewById<ConsultVideoPlayer>(R.id.video_item_player)
|
||||
prePlayer?.onVideoReset()
|
||||
} else {
|
||||
player.onVideoResume(false)
|
||||
}
|
||||
}
|
||||
prePlayerPosition = centerItemPosition
|
||||
}
|
||||
|
||||
override fun pageStop() {
|
||||
val (_: Int, player) = getPlayer(carouselLayoutManager)
|
||||
if (player is ConsultVideoPlayer) {
|
||||
player.onVideoPause()
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
carouselLayoutManager.addOnDargAutoDiffListener { adapterPosition, currentPosition ->
|
||||
val fl = adapterPosition - floor(adapterPosition)
|
||||
var currentIndex = currentPosition
|
||||
if (fl > 0.5) {
|
||||
if (currentPosition == 0) {
|
||||
currentIndex = rvVideoPlaylist?.adapter!!.itemCount - 1
|
||||
} else {
|
||||
currentIndex -= 1
|
||||
}
|
||||
}
|
||||
indicatorView.onPageScrolled(currentIndex, fl, 0)
|
||||
}
|
||||
val recyclerVideoAdapter = RecyclerVideoAdapter(context, arrayListOf, rvVideoPlaylist)
|
||||
recyclerVideoAdapter.setOnThumbImageClilckListener {
|
||||
val (_: Int, player) = getPlayer(carouselLayoutManager)
|
||||
if (player is ConsultVideoPlayer) {
|
||||
player.onVideoReset()
|
||||
player.thumbImageViewLayout.visibility = View.VISIBLE
|
||||
}
|
||||
rvVideoPlaylist?.smoothScrollToPosition(it)
|
||||
}
|
||||
rvVideoPlaylist?.layoutManager = carouselLayoutManager
|
||||
rvVideoPlaylist?.setHasFixedSize(true)
|
||||
rvVideoPlaylist?.adapter = recyclerVideoAdapter
|
||||
}
|
||||
|
||||
private fun getPlayer(carouselLayoutManager: CarouselLayoutManager): Pair<Int, ConsultVideoPlayer?> {
|
||||
val centerItemPosition: Int = carouselLayoutManager.centerItemPosition
|
||||
val playerHolder = carouselLayoutManager.findViewByPosition(centerItemPosition)
|
||||
val player = playerHolder?.findViewById<ConsultVideoPlayer>(R.id.video_item_player)
|
||||
return Pair(centerItemPosition, player)
|
||||
}
|
||||
|
||||
override fun onWindowFocusChanged(hasWindowFocus: Boolean) {
|
||||
super.onWindowFocusChanged(hasWindowFocus)
|
||||
val carouselLayoutManager = rvVideoPlaylist?.layoutManager as CarouselLayoutManager
|
||||
val (_: Int, player) = getPlayer(carouselLayoutManager)
|
||||
player?.let {
|
||||
if(it.isInPlayingState&&!it.isIfCurrentIsFullscreen&&!hasWindowFocus){
|
||||
player.onVideoPause()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.mogo.och.taxi.passenger.ui.video;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.mogo.och.taxi.passenger.R;
|
||||
import com.mogo.och.taxi.passenger.widget.ConsultVideoPlayer;
|
||||
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder;
|
||||
|
||||
public class RecyclerItemVideoHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public final static String TAG = "RecyclerView2List";
|
||||
|
||||
protected Context context;
|
||||
|
||||
public ConsultVideoPlayer gsyVideoPlayer;
|
||||
|
||||
GSYVideoOptionBuilder gsyVideoOptionBuilder;
|
||||
|
||||
public RecyclerItemVideoHolder(Context context, View v) {
|
||||
super(v);
|
||||
this.context = context;
|
||||
gsyVideoPlayer = v.findViewById(R.id.video_item_player);
|
||||
gsyVideoOptionBuilder = new GSYVideoOptionBuilder();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.mogo.och.taxi.passenger.ui.video;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.mogo.eagle.core.utilcode.util.ToastUtils;
|
||||
import com.mogo.och.taxi.passenger.R;
|
||||
import com.mogo.och.taxi.passenger.bean.TaxiPassengerVideoPlay;
|
||||
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import me.jessyan.autosize.AutoSizeCompat;
|
||||
|
||||
public class RecyclerVideoAdapter extends RecyclerView.Adapter<RecyclerItemVideoHolder> {
|
||||
|
||||
private final static String TAG = "RecyclerVideoAdapter";
|
||||
|
||||
private List<TaxiPassengerVideoPlay> itemDataList ;
|
||||
private final Context context;
|
||||
private OnThumbImageClilckListener onThumbImageClilckListener;
|
||||
private final RecyclerView recyclerView;
|
||||
|
||||
|
||||
public void setOnThumbImageClilckListener(OnThumbImageClilckListener onThumbImageClilckListener) {
|
||||
this.onThumbImageClilckListener = onThumbImageClilckListener;
|
||||
}
|
||||
|
||||
public RecyclerVideoAdapter(Context context, List<TaxiPassengerVideoPlay> itemDataList,RecyclerView recyclerView) {
|
||||
this.itemDataList = itemDataList;
|
||||
this.context = context;
|
||||
this.recyclerView = recyclerView;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerItemVideoHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(context).inflate(R.layout.list_video_item_light, parent, false);
|
||||
RecyclerItemVideoHolder recyclerItemVideoHolder = new RecyclerItemVideoHolder(context, v);
|
||||
recyclerItemVideoHolder.setIsRecyclable(false);
|
||||
return recyclerItemVideoHolder;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final RecyclerItemVideoHolder holder, int position) {
|
||||
final TaxiPassengerVideoPlay taxiPassengerVideoPlay = itemDataList.get(position);
|
||||
AutoSizeCompat.autoConvertDensityOfGlobal(holder.itemView.getResources());
|
||||
holder.gsyVideoOptionBuilder
|
||||
.setEnlargeImageRes(R.drawable.taxi_p_change_full)
|
||||
.setUrl(taxiPassengerVideoPlay.getUrl())
|
||||
.setCacheWithPlay(true)
|
||||
.setPlayTag(taxiPassengerVideoPlay.getImageUrl()+position)
|
||||
.setThumbPlay(false)
|
||||
.build(holder.gsyVideoPlayer);
|
||||
holder.gsyVideoPlayer.getTitleTextView().setText(taxiPassengerVideoPlay.getTitle());
|
||||
Glide.with(context)
|
||||
.load(taxiPassengerVideoPlay.getImageUrl())
|
||||
.apply(new RequestOptions().placeholder(R.drawable.taxi_p_video_holder).centerCrop())
|
||||
.into(holder.gsyVideoPlayer.coverImage);
|
||||
holder.gsyVideoPlayer.getThumbImageViewLayout().setOnClickListener(v -> {
|
||||
if(onThumbImageClilckListener!=null){
|
||||
onThumbImageClilckListener.onDxChanged(holder.getAbsoluteAdapterPosition());
|
||||
}
|
||||
});
|
||||
holder.gsyVideoPlayer.setVideoAllCallBack(new GSYSampleCallBack(){
|
||||
@Override
|
||||
public void onAutoComplete(String url, Object... objects) {
|
||||
holder.gsyVideoPlayer.onVideoReset();
|
||||
if(holder.getAbsoluteAdapterPosition()==getItemCount()-1){
|
||||
recyclerView.smoothScrollToPosition(0);
|
||||
}else {
|
||||
recyclerView.smoothScrollToPosition(holder.getAbsoluteAdapterPosition()+1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickBlank(String url, Object... objects) {
|
||||
super.onClickBlank(url, objects);
|
||||
recyclerView.smoothScrollToPosition(holder.getAbsoluteAdapterPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayError(String url, Object... objects) {
|
||||
ToastUtils.showLong("哎呀,出错了,看看其他视频吧");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickStartError(String url, Object... objects) {
|
||||
ToastUtils.showLong("哎呀,出错了,看看其他视频吧");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return itemDataList.size();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
public interface OnThumbImageClilckListener {
|
||||
void onDxChanged(int targetPosition);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,970 @@
|
||||
package com.mogo.och.taxi.passenger.ui.video.layoutmanage;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller;
|
||||
import androidx.recyclerview.widget.OrientationHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An implementation of {@link RecyclerView.LayoutManager} that layout items like carousel.
|
||||
* Generally there is one center item and bellow this item there are maximum {@link CarouselLayoutManager#getMaxVisibleItems()} items on each side of the center
|
||||
* item. By default {@link CarouselLayoutManager#getMaxVisibleItems()} is {@link CarouselLayoutManager#MAX_VISIBLE_ITEMS}.<br />
|
||||
* <br />
|
||||
* This LayoutManager supports only fixedSized adapter items.<br />
|
||||
* <br />
|
||||
* This LayoutManager supports {@link CarouselLayoutManager#HORIZONTAL} and {@link CarouselLayoutManager#VERTICAL} orientations. <br />
|
||||
* <br />
|
||||
* This LayoutManager supports circle layout. By default it if disabled. We don't recommend to use circle layout with adapter items count less then 3. <br />
|
||||
* <br />
|
||||
* Please be sure that layout_width of adapter item is a constant value and not {@link ViewGroup.LayoutParams#MATCH_PARENT}
|
||||
* for {@link #HORIZONTAL} orientation.
|
||||
* So like layout_height is not {@link ViewGroup.LayoutParams#MATCH_PARENT} for {@link CarouselLayoutManager#VERTICAL}<br />
|
||||
* <br />
|
||||
*/
|
||||
public class CarouselLayoutManager extends RecyclerView.LayoutManager implements RecyclerView.SmoothScroller.ScrollVectorProvider {
|
||||
|
||||
public static final int HORIZONTAL = OrientationHelper.HORIZONTAL;
|
||||
public static final int VERTICAL = OrientationHelper.VERTICAL;
|
||||
/**
|
||||
* 固定值一直不变
|
||||
*/
|
||||
public static final int INVALID_POSITION = -1;
|
||||
public static final int MAX_VISIBLE_ITEMS = 3;
|
||||
|
||||
private static final boolean CIRCLE_LAYOUT = false;
|
||||
|
||||
private boolean mDecoratedChildSizeInvalid;
|
||||
private Integer mDecoratedChildWidth;
|
||||
private Integer mDecoratedChildHeight;
|
||||
|
||||
private final int mOrientation;
|
||||
private boolean mCircleLayout;
|
||||
|
||||
private int mPendingScrollPosition;
|
||||
|
||||
private final LayoutHelper mLayoutHelper = new LayoutHelper(MAX_VISIBLE_ITEMS);
|
||||
|
||||
private PostLayoutListener mViewPostLayout;
|
||||
|
||||
private final List<OnCenterItemSelectionListener> mOnCenterItemSelectionListeners = new ArrayList<>();
|
||||
private final List<OnDargAutoDiffListener> onDargAutoDiffListeners = new ArrayList<>();
|
||||
private int mCenterItemPosition = INVALID_POSITION;
|
||||
private int mItemsCount;
|
||||
|
||||
@Nullable
|
||||
private CarouselSavedState mPendingCarouselSavedState;
|
||||
|
||||
/**
|
||||
* @param orientation should be {@link #VERTICAL} or {@link #HORIZONTAL}
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public CarouselLayoutManager(final int orientation) {
|
||||
this(orientation, CIRCLE_LAYOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* If circleLayout is true then all items will be in cycle. Scroll will be infinite on both sides.
|
||||
*
|
||||
* @param orientation should be {@link #VERTICAL} or {@link #HORIZONTAL}
|
||||
* @param circleLayout true for enabling circleLayout
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public CarouselLayoutManager(final int orientation, final boolean circleLayout) {
|
||||
if (HORIZONTAL != orientation && VERTICAL != orientation) {
|
||||
throw new IllegalArgumentException("orientation should be HORIZONTAL or VERTICAL");
|
||||
}
|
||||
mOrientation = orientation;
|
||||
mCircleLayout = circleLayout;
|
||||
mPendingScrollPosition = INVALID_POSITION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change circle layout type
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setCircleLayout(final boolean circleLayout) {
|
||||
if (mCircleLayout != circleLayout) {
|
||||
mCircleLayout = circleLayout;
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup {@link PostLayoutListener} for this LayoutManager.
|
||||
* Its methods will be called for each visible view item after general LayoutManager layout finishes. <br />
|
||||
* <br />
|
||||
* Generally this method should be used for scaling and translating view item for better (different) view presentation of layouting.
|
||||
*
|
||||
* @param postLayoutListener listener for item layout changes. Can be null.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setPostLayoutListener(@Nullable final PostLayoutListener postLayoutListener) {
|
||||
mViewPostLayout = postLayoutListener;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup maximum visible (layout) items on each side of the center item.
|
||||
* Basically during scrolling there can be more visible items (+1 item on each side), but in idle state this is the only reached maximum.
|
||||
*
|
||||
* @param maxVisibleItems should be great then 0, if bot an {@link IllegalAccessException} will be thrown
|
||||
*/
|
||||
@CallSuper
|
||||
@SuppressWarnings("unused")
|
||||
public void setMaxVisibleItems(final int maxVisibleItems) {
|
||||
if (0 > maxVisibleItems) {
|
||||
throw new IllegalArgumentException("maxVisibleItems can't be less then 0");
|
||||
}
|
||||
mLayoutHelper.mMaxVisibleItems = maxVisibleItems;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return current setup for maximum visible items.
|
||||
* @see #setMaxVisibleItems(int)
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public int getMaxVisibleItems() {
|
||||
return mLayoutHelper.mMaxVisibleItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
|
||||
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return current layout orientation
|
||||
* @see #VERTICAL
|
||||
* @see #HORIZONTAL
|
||||
*/
|
||||
public int getOrientation() {
|
||||
return mOrientation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canScrollHorizontally() {
|
||||
return 0 != getChildCount() && HORIZONTAL == mOrientation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canScrollVertically() {
|
||||
return 0 != getChildCount() && VERTICAL == mOrientation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return current layout center item
|
||||
*/
|
||||
public int getCenterItemPosition() {
|
||||
return mCenterItemPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param onCenterItemSelectionListener listener that will trigger when ItemSelectionChanges. can't be null
|
||||
*/
|
||||
public void addOnItemSelectionListener(@NonNull final OnCenterItemSelectionListener onCenterItemSelectionListener) {
|
||||
mOnCenterItemSelectionListeners.add(onCenterItemSelectionListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param onCenterItemSelectionListener listener that was previously added by {@link #addOnItemSelectionListener(OnCenterItemSelectionListener)}
|
||||
*/
|
||||
public void removeOnItemSelectionListener(@NonNull final OnCenterItemSelectionListener onCenterItemSelectionListener) {
|
||||
mOnCenterItemSelectionListeners.remove(onCenterItemSelectionListener);
|
||||
}
|
||||
|
||||
public void addOnDargAutoDiffListener(@NonNull final OnDargAutoDiffListener onDargAutoDiffListener) {
|
||||
onDargAutoDiffListeners.add(onDargAutoDiffListener);
|
||||
}
|
||||
|
||||
public void removeOnDargAutoDiffListener(@NonNull final OnDargAutoDiffListener onDargAutoDiffListener) {
|
||||
onDargAutoDiffListeners.remove(onDargAutoDiffListener);
|
||||
}
|
||||
|
||||
@SuppressWarnings("RefusedBequest")
|
||||
@Override
|
||||
public void scrollToPosition(final int position) {
|
||||
if (0 > position) {
|
||||
throw new IllegalArgumentException("position can't be less then 0. position is : " + position);
|
||||
}
|
||||
mPendingScrollPosition = position;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@SuppressWarnings("RefusedBequest")
|
||||
@Override
|
||||
public void smoothScrollToPosition(@NonNull final RecyclerView recyclerView, @NonNull final RecyclerView.State state, final int position) {
|
||||
final LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
|
||||
@Override
|
||||
public int calculateDyToMakeVisible(final View view, final int snapPreference) {
|
||||
if (!canScrollVertically()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return getOffsetForCurrentView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int calculateDxToMakeVisible(final View view, final int snapPreference) {
|
||||
if (!canScrollHorizontally()) {
|
||||
return 0;
|
||||
}
|
||||
return getOffsetForCurrentView(view);
|
||||
}
|
||||
};
|
||||
linearSmoothScroller.setTargetPosition(position);
|
||||
startSmoothScroll(linearSmoothScroller);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PointF computeScrollVectorForPosition(final int targetPosition) {
|
||||
if (0 == getChildCount()) {
|
||||
return null;
|
||||
}
|
||||
final float directionDistance = getScrollDirection(targetPosition);
|
||||
//noinspection NumericCastThatLosesPrecision
|
||||
final int direction = (int) -Math.signum(directionDistance);
|
||||
|
||||
if (HORIZONTAL == mOrientation) {
|
||||
return new PointF(direction, 0);
|
||||
} else {
|
||||
return new PointF(0, direction);
|
||||
}
|
||||
}
|
||||
|
||||
private float getScrollDirection(final int targetPosition) {
|
||||
final float currentScrollPosition = makeScrollPositionInRange0ToCount(getCurrentScrollPosition(), mItemsCount);
|
||||
|
||||
if (mCircleLayout) {
|
||||
final float t1 = currentScrollPosition - targetPosition;
|
||||
final float t2 = Math.abs(t1) - mItemsCount;
|
||||
if (Math.abs(t1) > Math.abs(t2)) {
|
||||
return Math.signum(t1) * t2;
|
||||
} else {
|
||||
return t1;
|
||||
}
|
||||
} else {
|
||||
return currentScrollPosition - targetPosition;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int scrollVerticallyBy(final int dy, @NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state) {
|
||||
if (HORIZONTAL == mOrientation) {
|
||||
return 0;
|
||||
}
|
||||
return scrollBy(dy, recycler, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int scrollHorizontallyBy(final int dx, final RecyclerView.Recycler recycler, final RecyclerView.State state) {
|
||||
if (VERTICAL == mOrientation) {
|
||||
return 0;
|
||||
}
|
||||
return scrollBy(dx, recycler, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from {@link #scrollHorizontallyBy(int, RecyclerView.Recycler, RecyclerView.State)} and
|
||||
* {@link #scrollVerticallyBy(int, RecyclerView.Recycler, RecyclerView.State)} to calculate needed scroll that is allowed. <br />
|
||||
* <br />
|
||||
* This method may do relayout work.
|
||||
*
|
||||
* @param diff 要滚动的距离
|
||||
* @param recycler 回收期
|
||||
* @param state Transient state of RecyclerView
|
||||
* @return distance that we actually scrolled by
|
||||
*/
|
||||
@CallSuper
|
||||
protected int scrollBy(final int diff, @NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state) {
|
||||
if (null == mDecoratedChildWidth || null == mDecoratedChildHeight) {
|
||||
return 0;
|
||||
}
|
||||
if (0 == getChildCount() || 0 == diff) {
|
||||
return 0;
|
||||
}
|
||||
final int resultScroll;
|
||||
if (mCircleLayout) {
|
||||
resultScroll = diff;
|
||||
|
||||
mLayoutHelper.mScrollOffset += resultScroll;
|
||||
|
||||
final int maxOffset = getScrollItemSize() * mItemsCount;
|
||||
while (0 > mLayoutHelper.mScrollOffset) {
|
||||
mLayoutHelper.mScrollOffset += maxOffset;
|
||||
}
|
||||
while (mLayoutHelper.mScrollOffset > maxOffset) {
|
||||
mLayoutHelper.mScrollOffset -= maxOffset;
|
||||
}
|
||||
|
||||
mLayoutHelper.mScrollOffset -= resultScroll;
|
||||
} else {
|
||||
final int maxOffset = getMaxScrollOffset();
|
||||
|
||||
if (0 > mLayoutHelper.mScrollOffset + diff) {
|
||||
resultScroll = -mLayoutHelper.mScrollOffset; //to make it 0
|
||||
} else if (mLayoutHelper.mScrollOffset + diff > maxOffset) {
|
||||
resultScroll = maxOffset - mLayoutHelper.mScrollOffset; //to make it maxOffset
|
||||
} else {
|
||||
resultScroll = diff;
|
||||
}
|
||||
}
|
||||
if (0 != resultScroll) {
|
||||
mLayoutHelper.mScrollOffset += resultScroll;
|
||||
fillData(recycler, state);
|
||||
}
|
||||
return resultScroll;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMeasure(@NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state, final int widthSpec, final int heightSpec) {
|
||||
mDecoratedChildSizeInvalid = true;
|
||||
|
||||
super.onMeasure(recycler, state, widthSpec, heightSpec);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public void onAdapterChanged(final RecyclerView.Adapter oldAdapter, final RecyclerView.Adapter newAdapter) {
|
||||
super.onAdapterChanged(oldAdapter, newAdapter);
|
||||
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("RefusedBequest")
|
||||
@Override
|
||||
@CallSuper
|
||||
public void onLayoutChildren(@NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state) {
|
||||
if (0 == state.getItemCount()) {
|
||||
removeAndRecycleAllViews(recycler);
|
||||
selectItemCenterPosition(INVALID_POSITION);
|
||||
return;
|
||||
}
|
||||
|
||||
detachAndScrapAttachedViews(recycler);
|
||||
|
||||
if (null == mDecoratedChildWidth || mDecoratedChildSizeInvalid) {
|
||||
final List<RecyclerView.ViewHolder> scrapList = recycler.getScrapList();
|
||||
|
||||
final boolean shouldRecycle;
|
||||
final View view;
|
||||
if (scrapList.isEmpty()) {
|
||||
shouldRecycle = true;
|
||||
final int itemsCount = state.getItemCount();
|
||||
view = recycler.getViewForPosition(
|
||||
mPendingScrollPosition == INVALID_POSITION ?
|
||||
0 :
|
||||
Math.max(0, Math.min(itemsCount - 1, mPendingScrollPosition))
|
||||
);
|
||||
addView(view);
|
||||
} else {
|
||||
shouldRecycle = false;
|
||||
view = scrapList.get(0).itemView;
|
||||
}
|
||||
measureChildWithMargins(view, 0, 0);
|
||||
|
||||
final int decoratedChildWidth = getDecoratedMeasuredWidth(view);
|
||||
final int decoratedChildHeight = getDecoratedMeasuredHeight(view);
|
||||
if (shouldRecycle) {
|
||||
detachAndScrapView(view, recycler);
|
||||
}
|
||||
|
||||
if (null != mDecoratedChildWidth && (mDecoratedChildWidth != decoratedChildWidth || mDecoratedChildHeight != decoratedChildHeight)) {
|
||||
if (INVALID_POSITION == mPendingScrollPosition && null == mPendingCarouselSavedState) {
|
||||
mPendingScrollPosition = mCenterItemPosition;
|
||||
}
|
||||
}
|
||||
|
||||
mDecoratedChildWidth = decoratedChildWidth;
|
||||
mDecoratedChildHeight = decoratedChildHeight;
|
||||
mDecoratedChildSizeInvalid = false;
|
||||
}
|
||||
|
||||
if (INVALID_POSITION != mPendingScrollPosition) {
|
||||
final int itemsCount = state.getItemCount();
|
||||
mPendingScrollPosition = 0 == itemsCount ? INVALID_POSITION : Math.max(0, Math.min(itemsCount - 1, mPendingScrollPosition));
|
||||
}
|
||||
if (INVALID_POSITION != mPendingScrollPosition) {
|
||||
mLayoutHelper.mScrollOffset = calculateScrollForSelectingPosition(mPendingScrollPosition, state);
|
||||
mPendingScrollPosition = INVALID_POSITION;
|
||||
mPendingCarouselSavedState = null;
|
||||
} else if (null != mPendingCarouselSavedState) {
|
||||
mLayoutHelper.mScrollOffset = calculateScrollForSelectingPosition(mPendingCarouselSavedState.mCenterItemPosition, state);
|
||||
mPendingCarouselSavedState = null;
|
||||
} else if (state.didStructureChange() && INVALID_POSITION != mCenterItemPosition) {
|
||||
mLayoutHelper.mScrollOffset = calculateScrollForSelectingPosition(mCenterItemPosition, state);
|
||||
}
|
||||
|
||||
fillData(recycler, state);
|
||||
}
|
||||
|
||||
private int calculateScrollForSelectingPosition(final int itemPosition, final RecyclerView.State state) {
|
||||
if (itemPosition == INVALID_POSITION) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
final int fixedItemPosition = itemPosition < state.getItemCount() ? itemPosition : state.getItemCount() - 1;
|
||||
return fixedItemPosition * (VERTICAL == mOrientation ? mDecoratedChildHeight : mDecoratedChildWidth);
|
||||
}
|
||||
|
||||
private void fillData(@NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state) {
|
||||
final float currentScrollPosition = getCurrentScrollPosition();
|
||||
|
||||
generateLayoutOrder(currentScrollPosition, state);
|
||||
detachAndScrapAttachedViews(recycler);
|
||||
recyclerOldViews(recycler);
|
||||
|
||||
final int width = getWidthNoPadding();
|
||||
final int height = getHeightNoPadding();
|
||||
if (VERTICAL == mOrientation) {
|
||||
fillDataVertical(recycler, width, height);
|
||||
} else {
|
||||
fillDataHorizontal(recycler, width, height);
|
||||
}
|
||||
|
||||
recycler.clear();
|
||||
|
||||
detectOnItemSelectionChanged(currentScrollPosition, state);
|
||||
}
|
||||
|
||||
private void detectOnItemSelectionChanged(final float currentScrollPosition, final RecyclerView.State state) {
|
||||
final float absCurrentScrollPosition = makeScrollPositionInRange0ToCount(currentScrollPosition, state.getItemCount());
|
||||
final int centerItem = Math.round(absCurrentScrollPosition);
|
||||
if(currentScrollPosition-centerItem!=0){
|
||||
new Handler(Looper.getMainLooper()).post(() -> dragDxDiff(currentScrollPosition,mCenterItemPosition));
|
||||
}
|
||||
if (mCenterItemPosition != centerItem) {
|
||||
mCenterItemPosition = centerItem;
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
selectItemCenterPosition(centerItem);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void selectItemCenterPosition(final int centerItem) {
|
||||
for (final OnCenterItemSelectionListener onCenterItemSelectionListener : mOnCenterItemSelectionListeners) {
|
||||
onCenterItemSelectionListener.onCenterItemChanged(centerItem);
|
||||
}
|
||||
}
|
||||
private void dragDxDiff(final float centerItem,final int currentPosition) {
|
||||
for (final OnDargAutoDiffListener onDargAutoDiffListener : onDargAutoDiffListeners) {
|
||||
onDargAutoDiffListener.onDxChanged(centerItem,currentPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private void fillDataVertical(final RecyclerView.Recycler recycler, final int width, final int height) {
|
||||
final int start = (width - mDecoratedChildWidth) / 2;
|
||||
final int end = start + mDecoratedChildWidth;
|
||||
|
||||
final int centerViewTop = (height - mDecoratedChildHeight) / 2;
|
||||
|
||||
for (int i = 0, count = mLayoutHelper.mLayoutOrder.length; i < count; ++i) {
|
||||
final LayoutOrder layoutOrder = mLayoutHelper.mLayoutOrder[i];
|
||||
final int offset = getCardOffsetByPositionDiff(layoutOrder.mItemPositionDiff);
|
||||
final int top = centerViewTop + offset;
|
||||
final int bottom = top + mDecoratedChildHeight;
|
||||
fillChildItem(start, top, end, bottom, layoutOrder, recycler, i);
|
||||
}
|
||||
}
|
||||
|
||||
private void fillDataHorizontal(final RecyclerView.Recycler recycler, final int width, final int height) {
|
||||
final int top = (height - mDecoratedChildHeight) / 2;
|
||||
final int bottom = top + mDecoratedChildHeight;
|
||||
|
||||
final int centerViewStart = (width - mDecoratedChildWidth) / 2;
|
||||
|
||||
for (int i = 0, count = mLayoutHelper.mLayoutOrder.length; i < count; ++i) {
|
||||
final LayoutOrder layoutOrder = mLayoutHelper.mLayoutOrder[i];
|
||||
final int offset = getCardOffsetByPositionDiff(layoutOrder.mItemPositionDiff);
|
||||
final int start = centerViewStart + offset;
|
||||
final int end = start + mDecoratedChildWidth;
|
||||
fillChildItem(start, top, end, bottom, layoutOrder, recycler, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("MethodWithTooManyParameters")
|
||||
private void fillChildItem(final int start, final int top, final int end, final int bottom, @NonNull final LayoutOrder layoutOrder, @NonNull final RecyclerView.Recycler recycler, final int i) {
|
||||
final View view = bindChild(layoutOrder.mItemAdapterPosition, recycler);
|
||||
ViewCompat.setElevation(view, i);
|
||||
ItemTransformation transformation = null;
|
||||
if (null != mViewPostLayout) {
|
||||
transformation = mViewPostLayout.transformChild(view, layoutOrder.mItemPositionDiff, mOrientation, layoutOrder.mItemAdapterPosition);
|
||||
}
|
||||
if (null == transformation) {
|
||||
view.layout(start, top, end, bottom);
|
||||
} else {
|
||||
view.layout(Math.round(start + transformation.mTranslationX), Math.round(top + transformation.mTranslationY),
|
||||
Math.round(end + transformation.mTranslationX), Math.round(bottom + transformation.mTranslationY));
|
||||
view.setScaleX(transformation.mScaleX);
|
||||
view.setScaleY(transformation.mScaleY);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
view.setTransitionAlpha(transformation.mAlpha);
|
||||
}else {
|
||||
view.setAlpha(transformation.mAlpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 中心项目的当前滚动位置。如果是循环布局,则该值可以在任何范围内。如果不是,那么它在[0,-1]
|
||||
*/
|
||||
private float getCurrentScrollPosition() {
|
||||
final int fullScrollSize = getMaxScrollOffset();
|
||||
if (0 == fullScrollSize) {
|
||||
return 0;
|
||||
}
|
||||
return 1.0f * mLayoutHelper.mScrollOffset / getScrollItemSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充布局中所有项目的最大滚动值。通常,这仅适用于非循环布局。
|
||||
*/
|
||||
private int getMaxScrollOffset() {
|
||||
return getScrollItemSize() * (mItemsCount - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Because we can support old Android versions, we should layout our children in specific order to make our center view in the top of layout
|
||||
* (this item should layout last). So this method will calculate layout order and fill up {@link #mLayoutHelper} object.
|
||||
* This object will be filled by only needed to layout items. Non visible items will not be there.
|
||||
*
|
||||
* @param currentScrollPosition current scroll position this is a value that indicates position of center item
|
||||
* (if this value is int, then center item is really in the center of the layout, else it is near state).
|
||||
* Be aware that this value can be in any range is it is cycle layout
|
||||
* @param state Transient state of RecyclerView
|
||||
* @see #getCurrentScrollPosition()
|
||||
*/
|
||||
private void generateLayoutOrder(final float currentScrollPosition, @NonNull final RecyclerView.State state) {
|
||||
mItemsCount = state.getItemCount();
|
||||
final float absCurrentScrollPosition = makeScrollPositionInRange0ToCount(currentScrollPosition, mItemsCount);
|
||||
final int centerItem = Math.round(absCurrentScrollPosition);
|
||||
|
||||
if (mCircleLayout && 1 < mItemsCount) {
|
||||
final int layoutCount = Math.min(mLayoutHelper.mMaxVisibleItems * 2 + 1, mItemsCount);
|
||||
|
||||
mLayoutHelper.initLayoutOrder(layoutCount);
|
||||
|
||||
final int countLayoutHalf = layoutCount / 2;
|
||||
// before center item
|
||||
for (int i = 1; i <= countLayoutHalf; ++i) {
|
||||
final int position = Math.round(absCurrentScrollPosition - i + mItemsCount) % mItemsCount;
|
||||
mLayoutHelper.setLayoutOrder(countLayoutHalf - i, position, centerItem - absCurrentScrollPosition - i);
|
||||
}
|
||||
// after center item
|
||||
for (int i = layoutCount - 1; i >= countLayoutHalf + 1; --i) {
|
||||
final int position = Math.round(absCurrentScrollPosition - i + layoutCount) % mItemsCount;
|
||||
mLayoutHelper.setLayoutOrder(i - 1, position, centerItem - absCurrentScrollPosition + layoutCount - i);
|
||||
}
|
||||
mLayoutHelper.setLayoutOrder(layoutCount - 1, centerItem, centerItem - absCurrentScrollPosition);
|
||||
|
||||
} else {
|
||||
final int firstVisible = Math.max(centerItem - mLayoutHelper.mMaxVisibleItems, 0);
|
||||
final int lastVisible = Math.min(centerItem + mLayoutHelper.mMaxVisibleItems, mItemsCount - 1);
|
||||
final int layoutCount = lastVisible - firstVisible + 1;
|
||||
|
||||
mLayoutHelper.initLayoutOrder(layoutCount);
|
||||
|
||||
for (int i = firstVisible; i <= lastVisible; ++i) {
|
||||
if (i == centerItem) {
|
||||
mLayoutHelper.setLayoutOrder(layoutCount - 1, i, i - absCurrentScrollPosition);
|
||||
} else if (i < centerItem) {
|
||||
mLayoutHelper.setLayoutOrder(i - firstVisible, i, i - absCurrentScrollPosition);
|
||||
} else {
|
||||
mLayoutHelper.setLayoutOrder(layoutCount - (i - centerItem) - 1, i, i - absCurrentScrollPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getWidthNoPadding() {
|
||||
return getWidth() - getPaddingStart() - getPaddingEnd();
|
||||
}
|
||||
|
||||
public int getHeightNoPadding() {
|
||||
return getHeight() - getPaddingEnd() - getPaddingStart();
|
||||
}
|
||||
|
||||
private View bindChild(final int position, @NonNull final RecyclerView.Recycler recycler) {
|
||||
final View view = recycler.getViewForPosition(position);
|
||||
|
||||
addView(view);
|
||||
measureChildWithMargins(view, 0, 0);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void recyclerOldViews(final RecyclerView.Recycler recycler) {
|
||||
for (RecyclerView.ViewHolder viewHolder : new ArrayList<>(recycler.getScrapList())) {
|
||||
int adapterPosition = viewHolder.getAdapterPosition();
|
||||
boolean found = false;
|
||||
for (LayoutOrder layoutOrder : mLayoutHelper.mLayoutOrder) {
|
||||
if (layoutOrder.mItemAdapterPosition == adapterPosition) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
recycler.recycleView(viewHolder.itemView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during {@link #fillData(RecyclerView.Recycler, RecyclerView.State)} to calculate item offset from layout center line. <br />
|
||||
* <br />
|
||||
* Returns {@link #convertItemPositionDiffToSmoothPositionDiff(float)} * (size off area above center item when it is on the center). <br />
|
||||
* Sign is: plus if this item is bellow center line, minus if not<br />
|
||||
* <br />
|
||||
* ----- - area above it<br />
|
||||
* ||||| - center item<br />
|
||||
* ----- - area bellow it (it has the same size as are above center item)<br />
|
||||
*
|
||||
* @param itemPositionDiff current item difference with layout center line. if this is 0, then this item center is in layout center line.
|
||||
* if this is 1 then this item is bellow the layout center line in the full item size distance.
|
||||
* @return offset in scroll px coordinates.
|
||||
*/
|
||||
protected int getCardOffsetByPositionDiff(final float itemPositionDiff) {
|
||||
final double smoothPosition = convertItemPositionDiffToSmoothPositionDiff(itemPositionDiff);
|
||||
|
||||
final int dimenDiff;
|
||||
if (VERTICAL == mOrientation) {
|
||||
dimenDiff = (getHeightNoPadding() - mDecoratedChildHeight) / 2;
|
||||
} else {
|
||||
dimenDiff = (getWidthNoPadding() - mDecoratedChildWidth) / 2;
|
||||
}
|
||||
//noinspection NumericCastThatLosesPrecision
|
||||
return (int) Math.round(Math.signum(itemPositionDiff) * dimenDiff * smoothPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during {@link #getCardOffsetByPositionDiff(float)} for better item movement. <br/>
|
||||
* Current implementation speed up items that are far from layout center line and slow down items that are close to this line.
|
||||
* This code is full of maths. If you want to make items move in a different way, probably you should override this method.<br />
|
||||
* Please see code comments for better explanations.
|
||||
*
|
||||
* @param itemPositionDiff current item difference with layout center line. if this is 0, then this item center is in layout center line.
|
||||
* if this is 1 then this item is bellow the layout center line in the full item size distance.
|
||||
* @return smooth position offset. needed for scroll calculation and better user experience.
|
||||
* @see #getCardOffsetByPositionDiff(float)
|
||||
*/
|
||||
@SuppressWarnings({"MagicNumber", "InstanceMethodNamingConvention"})
|
||||
protected double convertItemPositionDiffToSmoothPositionDiff(final float itemPositionDiff) {
|
||||
// generally item moves the same way above center and bellow it. So we don't care about diff sign.
|
||||
final float absIemPositionDiff = Math.abs(itemPositionDiff);
|
||||
|
||||
// we detect if this item is close for center or not. We use (1 / maxVisibleItem) ^ (1/3) as close definer.
|
||||
if (absIemPositionDiff > StrictMath.pow(1.0f / mLayoutHelper.mMaxVisibleItems, 1.0f / 3)) {
|
||||
// this item is far from center line, so we should make it move like square root function
|
||||
return StrictMath.pow(absIemPositionDiff / mLayoutHelper.mMaxVisibleItems, 1 / 2.0f);
|
||||
} else {
|
||||
// this item is close from center line. we should slow it down and don't make it speed up very quick.
|
||||
// so square function in range of [0, (1/maxVisible)^(1/3)] is quite good in it;
|
||||
return StrictMath.pow(absIemPositionDiff, 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return full item size
|
||||
*/
|
||||
protected int getScrollItemSize() {
|
||||
if (VERTICAL == mOrientation) {
|
||||
return mDecoratedChildHeight;
|
||||
} else {
|
||||
return mDecoratedChildWidth;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
if (null != mPendingCarouselSavedState) {
|
||||
return new CarouselSavedState(mPendingCarouselSavedState);
|
||||
}
|
||||
final CarouselSavedState savedState = new CarouselSavedState(super.onSaveInstanceState());
|
||||
savedState.mCenterItemPosition = mCenterItemPosition;
|
||||
return savedState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(final Parcelable state) {
|
||||
if (state instanceof CarouselSavedState) {
|
||||
mPendingCarouselSavedState = (CarouselSavedState) state;
|
||||
|
||||
super.onRestoreInstanceState(mPendingCarouselSavedState.mSuperState);
|
||||
} else {
|
||||
super.onRestoreInstanceState(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 从中心到最近项目的滚动偏移量
|
||||
*/
|
||||
protected int getOffsetCenterView() {
|
||||
return Math.round(getCurrentScrollPosition()) * getScrollItemSize() - mLayoutHelper.mScrollOffset;
|
||||
}
|
||||
|
||||
protected int getOffsetForCurrentView(@NonNull final View view) {
|
||||
final int targetPosition = getPosition(view);
|
||||
final float directionDistance = getScrollDirection(targetPosition);
|
||||
|
||||
return Math.round(directionDistance * getScrollItemSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* 使滚动范围在[0,count]内的Helper方法。通常,只有循环布局才需要此方法。
|
||||
*
|
||||
* @param currentScrollPosition 滚动位置范围 个位数 view的index 小数滚动的范围
|
||||
* @param count adapter 中的数量
|
||||
* @return 在[0,总数]范围内滚动位置良好
|
||||
*/
|
||||
private static float makeScrollPositionInRange0ToCount(final float currentScrollPosition, final int count) {
|
||||
float absCurrentScrollPosition = currentScrollPosition;
|
||||
while (0 > absCurrentScrollPosition) {
|
||||
absCurrentScrollPosition += count;
|
||||
}
|
||||
while (Math.round(absCurrentScrollPosition) >= count) {
|
||||
absCurrentScrollPosition -= count;
|
||||
}
|
||||
return absCurrentScrollPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* This interface methods will be called for each visible view item after general LayoutManager layout finishes. <br />
|
||||
* <br />
|
||||
* Generally this method should be used for scaling and translating view item for better (different) view presentation of layouting.
|
||||
*/
|
||||
@SuppressWarnings("InterfaceNeverImplemented")
|
||||
public abstract static class PostLayoutListener {
|
||||
|
||||
/**
|
||||
* 子布局完成后调用。通常,您可以在这里进行任何平移和缩放工作。
|
||||
*
|
||||
* @param child view that was layout
|
||||
* @param itemPositionToCenterDiff view center line difference to layout center. if > 0 then this item is bellow layout center line, else if not
|
||||
* @param orientation layoutManager orientation {@link #getLayoutDirection()}
|
||||
* @param itemPositionInAdapter item position inside adapter for this layout pass
|
||||
*/
|
||||
public ItemTransformation transformChild(
|
||||
@NonNull final View child,
|
||||
final float itemPositionToCenterDiff,
|
||||
final int orientation,
|
||||
final int itemPositionInAdapter
|
||||
) {
|
||||
return transformChild(child, itemPositionToCenterDiff, orientation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after child layout finished. Generally you can do any translation and scaling work here.
|
||||
*
|
||||
* @param child view that was layout
|
||||
* @param itemPositionToCenterDiff view center line difference to layout center. if > 0 then this item is bellow layout center line, else if not
|
||||
* @param orientation layoutManager orientation {@link #getLayoutDirection()}
|
||||
*/
|
||||
public ItemTransformation transformChild(
|
||||
@NonNull final View child,
|
||||
final float itemPositionToCenterDiff,
|
||||
final int orientation
|
||||
) {
|
||||
throw new IllegalStateException("at least one transformChild should be implemented");
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnCenterItemSelectionListener {
|
||||
|
||||
/**
|
||||
* Listener that will be called on every change of center item.
|
||||
* This listener will be triggered on <b>every</b> layout operation if item was changed.
|
||||
* Do not do any expensive operations in this method since this will effect scroll experience.
|
||||
*
|
||||
* @param adapterPosition current layout center item
|
||||
*/
|
||||
void onCenterItemChanged(final int adapterPosition);
|
||||
}
|
||||
|
||||
public interface OnDargAutoDiffListener {
|
||||
void onDxChanged(final float adapterPosition,final int currentPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class that holds currently visible items.
|
||||
* Generally this class fills this list. <br />
|
||||
* <br />
|
||||
* This class holds all scroll and maxVisible items state.
|
||||
*
|
||||
* @see #getMaxVisibleItems()
|
||||
*/
|
||||
private static class LayoutHelper {
|
||||
|
||||
private int mMaxVisibleItems;
|
||||
|
||||
private int mScrollOffset;
|
||||
|
||||
private LayoutOrder[] mLayoutOrder;
|
||||
|
||||
private final List<WeakReference<LayoutOrder>> mReusedItems = new ArrayList<>();
|
||||
|
||||
LayoutHelper(final int maxVisibleItems) {
|
||||
mMaxVisibleItems = maxVisibleItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before any fill calls. Needed to recycle old items and init new array list. Generally this list is an array an it is reused.
|
||||
*
|
||||
* @param layoutCount items count that will be layout
|
||||
*/
|
||||
void initLayoutOrder(final int layoutCount) {
|
||||
if (null == mLayoutOrder || mLayoutOrder.length != layoutCount) {
|
||||
if (null != mLayoutOrder) {
|
||||
recycleItems(mLayoutOrder);
|
||||
}
|
||||
mLayoutOrder = new LayoutOrder[layoutCount];
|
||||
fillLayoutOrder();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during layout generation process of filling this list. Should be called only after {@link #initLayoutOrder(int)} method call.
|
||||
*
|
||||
* @param arrayPosition position in layout order
|
||||
* @param itemAdapterPosition adapter position of item for future data filling logic
|
||||
* @param itemPositionDiff difference of current item scroll position and center item position.
|
||||
* if this is a center item and it is in real center of layout, then this will be 0.
|
||||
* if current layout is not in the center, then this value will never be int.
|
||||
* if this item center is bellow layout center line then this value is greater then 0,
|
||||
* else less then 0.
|
||||
*/
|
||||
void setLayoutOrder(final int arrayPosition, final int itemAdapterPosition, final float itemPositionDiff) {
|
||||
final LayoutOrder item = mLayoutOrder[arrayPosition];
|
||||
item.mItemAdapterPosition = itemAdapterPosition;
|
||||
item.mItemPositionDiff = itemPositionDiff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks is this screen Layout has this adapterPosition view in layout
|
||||
*
|
||||
* @param adapterPosition adapter position of item for future data filling logic
|
||||
* @return true is adapterItem is in layout
|
||||
*/
|
||||
boolean hasAdapterPosition(final int adapterPosition) {
|
||||
if (null != mLayoutOrder) {
|
||||
for (final LayoutOrder layoutOrder : mLayoutOrder) {
|
||||
if (layoutOrder.mItemAdapterPosition == adapterPosition) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("VariableArgumentMethod")
|
||||
private void recycleItems(@NonNull final LayoutOrder... layoutOrders) {
|
||||
for (final LayoutOrder layoutOrder : layoutOrders) {
|
||||
//noinspection ObjectAllocationInLoop
|
||||
mReusedItems.add(new WeakReference<>(layoutOrder));
|
||||
}
|
||||
}
|
||||
|
||||
private void fillLayoutOrder() {
|
||||
for (int i = 0, length = mLayoutOrder.length; i < length; ++i) {
|
||||
if (null == mLayoutOrder[i]) {
|
||||
mLayoutOrder[i] = createLayoutOrder();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private LayoutOrder createLayoutOrder() {
|
||||
final Iterator<WeakReference<LayoutOrder>> iterator = mReusedItems.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final WeakReference<LayoutOrder> layoutOrderWeakReference = iterator.next();
|
||||
final LayoutOrder layoutOrder = layoutOrderWeakReference.get();
|
||||
iterator.remove();
|
||||
if (null != layoutOrder) {
|
||||
return layoutOrder;
|
||||
}
|
||||
}
|
||||
return new LayoutOrder();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that holds item data.
|
||||
* This class is filled during {@link #generateLayoutOrder(float, RecyclerView.State)} and used during {@link #fillData(RecyclerView.Recycler, RecyclerView.State)}
|
||||
*/
|
||||
private static class LayoutOrder {
|
||||
|
||||
/**
|
||||
* Item adapter position
|
||||
*/
|
||||
private int mItemAdapterPosition;
|
||||
/**
|
||||
* Item center difference to layout center. If center of item is bellow layout center, then this value is greater then 0, else it is less.
|
||||
*/
|
||||
private float mItemPositionDiff;
|
||||
}
|
||||
|
||||
protected static class CarouselSavedState implements Parcelable {
|
||||
|
||||
private final Parcelable mSuperState;
|
||||
private int mCenterItemPosition;
|
||||
|
||||
protected CarouselSavedState(@Nullable final Parcelable superState) {
|
||||
mSuperState = superState;
|
||||
}
|
||||
|
||||
private CarouselSavedState(@NonNull final Parcel in) {
|
||||
mSuperState = in.readParcelable(RecyclerView.LayoutManager.class.getClassLoader());
|
||||
mCenterItemPosition = in.readInt();
|
||||
}
|
||||
|
||||
protected CarouselSavedState(@NonNull final CarouselSavedState other) {
|
||||
mSuperState = other.mSuperState;
|
||||
mCenterItemPosition = other.mCenterItemPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(final Parcel parcel, final int i) {
|
||||
parcel.writeParcelable(mSuperState, i);
|
||||
parcel.writeInt(mCenterItemPosition);
|
||||
}
|
||||
|
||||
public static final Creator<CarouselSavedState> CREATOR
|
||||
= new Creator<CarouselSavedState>() {
|
||||
@Override
|
||||
public CarouselSavedState createFromParcel(final Parcel parcel) {
|
||||
return new CarouselSavedState(parcel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CarouselSavedState[] newArray(final int i) {
|
||||
return new CarouselSavedState[i];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.mogo.och.taxi.passenger.ui.video.layoutmanage;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Implementation of {@link CarouselLayoutManager.PostLayoutListener} that makes interesting scaling of items. <br />
|
||||
* We are trying to make items scaling quicker for closer items for center and slower for when they are far away.<br />
|
||||
* Tis implementation uses atan function for this purpose.
|
||||
*/
|
||||
public class CarouselZoomPostLayoutListener extends CarouselLayoutManager.PostLayoutListener {
|
||||
|
||||
private final float mScaleMultiplier;
|
||||
|
||||
public CarouselZoomPostLayoutListener() {
|
||||
this(0.21f);
|
||||
}
|
||||
|
||||
public CarouselZoomPostLayoutListener(final float scaleMultiplier) {
|
||||
mScaleMultiplier = scaleMultiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemTransformation transformChild(@NonNull final View child, final float itemPositionToCenterDiff, final int orientation) {
|
||||
float scale = 1.0f - mScaleMultiplier * Math.abs(itemPositionToCenterDiff);
|
||||
final float translateY;
|
||||
final float translateX;
|
||||
if (CarouselLayoutManager.VERTICAL == orientation) {
|
||||
final float translateYGeneral = child.getMeasuredHeight() * (1 - scale) / 2f;
|
||||
translateY = Math.signum(itemPositionToCenterDiff) * translateYGeneral;
|
||||
translateX = 0;
|
||||
} else {
|
||||
final float translateXGeneral = child.getMeasuredWidth() * (1 - scale)/8;
|
||||
translateX = Math.signum(itemPositionToCenterDiff) * translateXGeneral;
|
||||
translateY = 0;
|
||||
}
|
||||
return new ItemTransformation(scale,scale, scale, 0, translateY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.mogo.och.taxi.passenger.ui.video.layoutmanage;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* Class for centering items after scroll event.<br />
|
||||
* This class will listen to current scroll state and if item is not centered after scroll it will automatically scroll it to center.
|
||||
*/
|
||||
public class CenterScrollListener extends RecyclerView.OnScrollListener {
|
||||
|
||||
private boolean mAutoSet = true;
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(@NonNull final RecyclerView recyclerView, final int newState) {
|
||||
super.onScrollStateChanged(recyclerView, newState);
|
||||
final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
|
||||
if (!(layoutManager instanceof CarouselLayoutManager)) {
|
||||
mAutoSet = true;
|
||||
return;
|
||||
}
|
||||
|
||||
final CarouselLayoutManager lm = (CarouselLayoutManager) layoutManager;
|
||||
if (!mAutoSet) {
|
||||
if (RecyclerView.SCROLL_STATE_IDLE == newState) {
|
||||
final int scrollNeeded = lm.getOffsetCenterView();
|
||||
// 滚动到中心位置
|
||||
if (CarouselLayoutManager.HORIZONTAL == lm.getOrientation()) {
|
||||
recyclerView.smoothScrollBy(scrollNeeded, 0);
|
||||
} else {
|
||||
recyclerView.smoothScrollBy(0, scrollNeeded);
|
||||
}
|
||||
pageSelect(recyclerView,newState);
|
||||
mAutoSet = true;
|
||||
}
|
||||
}
|
||||
if (RecyclerView.SCROLL_STATE_DRAGGING == newState || RecyclerView.SCROLL_STATE_SETTLING == newState) {
|
||||
mAutoSet = false;
|
||||
}
|
||||
if(RecyclerView.SCROLL_STATE_DRAGGING == newState){
|
||||
pageStop();
|
||||
}
|
||||
}
|
||||
|
||||
protected void pageStop() {
|
||||
|
||||
}
|
||||
|
||||
protected void pageSelect(RecyclerView recyclerView, final int newState) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.mogo.och.taxi.passenger.ui.video.layoutmanage;
|
||||
|
||||
public class ItemTransformation {
|
||||
|
||||
final float mAlpha;
|
||||
final float mScaleX;
|
||||
final float mScaleY;
|
||||
final float mTranslationX;
|
||||
final float mTranslationY;
|
||||
|
||||
public ItemTransformation(final float alpha,final float scaleX, final float scaleY, final float translationX, final float translationY) {
|
||||
mScaleX = scaleX;
|
||||
mScaleY = scaleY;
|
||||
mTranslationX = translationX;
|
||||
mTranslationY = translationY;
|
||||
mAlpha = alpha;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.mogo.och.taxi.passenger.utils
|
||||
|
||||
import android.text.TextUtils
|
||||
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
|
||||
import com.mogo.commons.debug.DebugConfig
|
||||
import com.mogo.commons.utils.MogoAnalyticUtils
|
||||
import com.mogo.eagle.core.data.app.AppConfigInfo
|
||||
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.e
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_TAXI_P
|
||||
import com.mogo.eagle.core.utilcode.util.DateTimeUtils
|
||||
import com.mogo.och.common.module.utils.RxUtils
|
||||
import com.mogo.och.taxi.passenger.constant.TaxiPassengerConst
|
||||
import io.reactivex.disposables.Disposable
|
||||
|
||||
/**
|
||||
* OCH Taxi埋点工具
|
||||
*
|
||||
* Created on 2022/3/24
|
||||
*/
|
||||
object TaxiPassengerAnalyticsManager {
|
||||
|
||||
|
||||
private var mStartAutopilotKey: String? = null
|
||||
private val mStartAutopilotParams = HashMap<String, Any>()
|
||||
|
||||
var startAutopiloTimeOut: Disposable? = null
|
||||
|
||||
|
||||
fun triggerStartAutopilotFailureEventByAdas(failCode: String, failMsg: String) {
|
||||
RxUtils.disposeSubscribe(startAutopiloTimeOut)
|
||||
triggerStartAutopilotFailureEvent(failCode, failMsg)
|
||||
}
|
||||
|
||||
private fun clearStartAutopilotParams() {
|
||||
mStartAutopilotParams.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* ① 15s超时调用
|
||||
* ② 底盘明确给出错误原因
|
||||
* 启动自驾失败写日志
|
||||
*/
|
||||
private fun triggerStartAutopilotFailureEvent(failCode: String, failMsg: String) {
|
||||
if (mStartAutopilotParams.isEmpty()) return
|
||||
e(M_TAXI_P + "triggerStartAutopilotFailureEvent", failMsg)
|
||||
if (CallerAutoPilotStatusListenerManager.getState() != IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING) {
|
||||
mStartAutopilotParams[TaxiPassengerConst.EVENT_PARAM_START_FAILURE_CODE] = failCode
|
||||
mStartAutopilotParams[TaxiPassengerConst.EVENT_PARAM_START_FAILURE_MSG] = failMsg
|
||||
}
|
||||
mStartAutopilotParams[TaxiPassengerConst.EVENT_PARAM_START_RESULT] =
|
||||
CallerAutoPilotStatusListenerManager.getState() == IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING
|
||||
MogoAnalyticUtils.track(mStartAutopilotKey, mStartAutopilotParams)
|
||||
clearStartAutopilotParams() //清空参数数据,防止误传
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* ① 自检完成后 启动自驾
|
||||
* ② 自驾由其他状态转换到自驾中回调
|
||||
* 触发'开启自动驾驶'埋点流程
|
||||
* 开启自动驾驶,15s内成功则发送成功埋点,否则发送失败埋点
|
||||
* @param restart false(点击'开始服务'启动)/true(接管后点击'自动驾驶'按钮启动)
|
||||
* @param send 是否直接发送埋点(15s内开启成功则直接发送成功埋点)
|
||||
*/
|
||||
fun triggerStartAutopilotEvent(restart: Boolean, send: Boolean, startName: String, endName: String, orderNo: String) {
|
||||
mStartAutopilotKey = if (restart) TaxiPassengerConst.EVENT_KEY_RESTART_AUTOPILOT else TaxiPassengerConst.EVENT_KEY_START_SERVICE
|
||||
|
||||
if (send) {
|
||||
if (mStartAutopilotParams.isEmpty()) return
|
||||
// 开启成功,取消失败定时任务
|
||||
mStartAutopilotParams[TaxiPassengerConst.EVENT_PARAM_START_FAILURE_CODE] = ""
|
||||
mStartAutopilotParams[TaxiPassengerConst.EVENT_PARAM_START_FAILURE_MSG] = ""
|
||||
// 取消15s超时
|
||||
RxUtils.disposeSubscribe(startAutopiloTimeOut)
|
||||
mStartAutopilotParams[TaxiPassengerConst.EVENT_PARAM_START_RESULT] = true
|
||||
MogoAnalyticUtils.track(mStartAutopilotKey, mStartAutopilotParams)
|
||||
clearStartAutopilotParams() //清空参数数据,防止误传
|
||||
} else {
|
||||
val plateNum = AppConfigInfo.plateNumber
|
||||
mStartAutopilotParams[TaxiPassengerConst.EVENT_PARAM_SN] = MoGoAiCloudClientConfig.getInstance().sn
|
||||
mStartAutopilotParams[TaxiPassengerConst.EVENT_PARAM_PLATE_NUM] = if (TextUtils.isEmpty(plateNum)) "" else plateNum
|
||||
mStartAutopilotParams[TaxiPassengerConst.EVENT_PARAM_ENV_ONLINE] = DebugConfig.getNetMode() == DebugConfig.NET_MODE_RELEASE
|
||||
mStartAutopilotParams[TaxiPassengerConst.EVENT_PARAM_TIME] = DateTimeUtils.getTimeText(DateTimeUtils.yyyy_MM_dd_HH_mm_ss)
|
||||
mStartAutopilotParams[TaxiPassengerConst.EVENT_PARAM_START_NAME] = startName
|
||||
mStartAutopilotParams[TaxiPassengerConst.EVENT_PARAM_END_NAME] = endName
|
||||
mStartAutopilotParams[TaxiPassengerConst.EVENT_PARAM_ORDER_NUMBER] = orderNo
|
||||
startAutopiloTimeOut = RxUtils.createSubscribeOnOwnThread(TaxiPassengerConst.LOOP_PERIOD_15S) {
|
||||
// 15s内未开启,上报失败埋点
|
||||
triggerStartAutopilotFailureEvent("", "15s后app等待超时")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发"无法开启自驾已知异常"埋点
|
||||
* @param startName
|
||||
* @param endName
|
||||
* @param orderNo
|
||||
*/
|
||||
fun triggerUnableStartAPReasonEvent(startName: String, endName: String, orderNo: String, reason: String) {
|
||||
val plateNum = AppConfigInfo.plateNumber
|
||||
val params = HashMap<String, Any>()
|
||||
params[TaxiPassengerConst.EVENT_PARAM_SN] = MoGoAiCloudClientConfig.getInstance().sn
|
||||
params[TaxiPassengerConst.EVENT_PARAM_PLATE_NUM] = if (TextUtils.isEmpty(plateNum)) "" else plateNum
|
||||
params[TaxiPassengerConst.EVENT_PARAM_ENV_ONLINE] = DebugConfig.getNetMode() == DebugConfig.NET_MODE_RELEASE
|
||||
params[TaxiPassengerConst.EVENT_PARAM_TIME] = DateTimeUtils.getTimeText(DateTimeUtils.yyyy_MM_dd_HH_mm_ss)
|
||||
params[TaxiPassengerConst.EVENT_PARAM_START_NAME] = startName
|
||||
params[TaxiPassengerConst.EVENT_PARAM_END_NAME] = endName
|
||||
params[TaxiPassengerConst.EVENT_PARAM_ORDER_NUMBER] = orderNo
|
||||
params[TaxiPassengerConst.EVENT_PARAM_UNABLE_START_REASON] = reason
|
||||
MogoAnalyticUtils.track(TaxiPassengerConst.EVENT_KEY_AP_UNABLE_START_REASON, params)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.mogo.och.taxi.passenger.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.*
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.annotation.DrawableRes
|
||||
|
||||
object ZoomDrawable {
|
||||
fun zoomDrawableImage(context: Context,@DrawableRes id:Int,scaleX:Float,scaleY:Float):Drawable{
|
||||
|
||||
val bitmap: Bitmap = BitmapFactory.decodeResource(context.resources, id)
|
||||
val bitmapWidth = bitmap.width
|
||||
val bitmapHeight = bitmap.height
|
||||
val matrix = Matrix()
|
||||
matrix.postScale(scaleX, scaleY)
|
||||
// 产生缩放后的Bitmap对象
|
||||
val resizeBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmapWidth, bitmapHeight, matrix, true)
|
||||
return BitmapDrawable(context.resources,resizeBitmap)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,529 @@
|
||||
package com.mogo.och.taxi.passenger.widget
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.Surface
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.app.ActivityCompat
|
||||
import com.mogo.eagle.core.utilcode.util.TimeTransformUtils
|
||||
import com.mogo.eagle.core.utilcode.util.ToastUtils
|
||||
import com.mogo.eagle.core.widget.media.video.TextureVideoViewOutlineProvider
|
||||
import com.mogo.och.taxi.passenger.R
|
||||
import com.mogo.och.taxi.passenger.ui.statusview.StatusBarView
|
||||
import com.mogo.och.taxi.passenger.ui.video.FullVideoUtils
|
||||
import com.mogo.och.taxi.passenger.utils.ZoomDrawable
|
||||
import com.shuyu.gsyvideoplayer.listener.VideoAllCallBack
|
||||
import com.shuyu.gsyvideoplayer.utils.GSYVideoType
|
||||
import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
|
||||
import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer
|
||||
import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer
|
||||
import com.shuyu.gsyvideoplayer.video.base.GSYVideoView
|
||||
import me.jessyan.autosize.utils.AutoSizeUtils
|
||||
import java.lang.reflect.Constructor
|
||||
|
||||
/**
|
||||
* @author lixiaopeng
|
||||
* @since 2021/11/3
|
||||
*
|
||||
* 视频播放器,ui定制
|
||||
*/
|
||||
class ConsultVideoPlayer : StandardGSYVideoPlayer {
|
||||
|
||||
private lateinit var start: ImageView
|
||||
lateinit var coverImage: ImageView
|
||||
private lateinit var currentTimeTextView: TextView
|
||||
private lateinit var totalTimeTextView: TextView
|
||||
private lateinit var aivStartPlay: AppCompatImageView
|
||||
private lateinit var layoutBottom: ConstraintLayout
|
||||
private lateinit var vPpenLeft: View
|
||||
|
||||
private var fullVideoPlayer:ConsultVideoPlayer?=null
|
||||
var smalllPlayer:ConsultVideoPlayer?=null
|
||||
private var statusBarView: StatusBarView? = null
|
||||
|
||||
private var currentTime = 0
|
||||
|
||||
constructor(context: Context?) : super(context)
|
||||
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
|
||||
constructor(context: Context?, fullFlag: Boolean?) : super(context, fullFlag)
|
||||
|
||||
override fun init(context: Context) {
|
||||
mEnlargeImageRes = R.drawable.taxi_p_change_full
|
||||
super.init(context)
|
||||
start = findViewById(R.id.start)
|
||||
coverImage = findViewById(R.id.thumbImage)
|
||||
currentTimeTextView = findViewById(R.id.current)
|
||||
totalTimeTextView = findViewById(R.id.total)
|
||||
aivStartPlay = findViewById(R.id.aiv_start_play)
|
||||
layoutBottom = findViewById(R.id.layout_bottom)
|
||||
vPpenLeft = findViewById(R.id.v_open_left)
|
||||
fullscreenButton.setOnClickListener(this)
|
||||
aivStartPlay.setOnClickListener(this)
|
||||
if (mThumbImageViewLayout != null
|
||||
&& (mCurrentState == -1 || mCurrentState == CURRENT_STATE_NORMAL || mCurrentState == CURRENT_STATE_ERROR)
|
||||
) {
|
||||
mThumbImageViewLayout.visibility = View.VISIBLE
|
||||
}
|
||||
GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_FULL)
|
||||
aivStartPlay.scaleX = 0.8f
|
||||
aivStartPlay.scaleY = 0.8f
|
||||
|
||||
mProgressBar.thumb = ZoomDrawable.zoomDrawableImage(context,R.drawable.bg_taxi_p_video_index,0.66f,0.66f)
|
||||
}
|
||||
|
||||
private fun addDrageAnchor(){
|
||||
vPpenLeft.visibility = VISIBLE
|
||||
layoutBottom.post {
|
||||
val layoutParams = layoutBottom.layoutParams as ConstraintLayout.LayoutParams
|
||||
layoutParams.setMargins(333,0,333,90)
|
||||
layoutParams.height = 148
|
||||
layoutBottom.layoutParams = layoutParams
|
||||
}
|
||||
|
||||
mTopContainer.post {
|
||||
val layoutParams = mTopContainer.layoutParams as ConstraintLayout.LayoutParams
|
||||
layoutParams.height = 320
|
||||
mTopContainer.layoutParams = layoutParams
|
||||
val background = layoutBottom.background as GradientDrawable
|
||||
val x = floatArrayOf(12f, 12f,12f, 12f,12f, 12f,12f, 12f)
|
||||
background.cornerRadii = x
|
||||
|
||||
layoutBottom.background = background
|
||||
|
||||
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, 50f)
|
||||
val layoutParams1 = titleTextView.layoutParams as ConstraintLayout.LayoutParams
|
||||
layoutParams1.marginStart = 80
|
||||
titleTextView.layoutParams = layoutParams1
|
||||
aivStartPlay.scaleX = 1f
|
||||
aivStartPlay.scaleY = 1f
|
||||
val drawable = ActivityCompat.getDrawable(context, R.drawable.bg_taxi_p_video_index)
|
||||
mProgressBar.thumb = drawable
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
mProgressBar.maxHeight = 6
|
||||
mProgressBar.minHeight = 6
|
||||
}
|
||||
|
||||
val layoutParams2 = fullscreenButton.layoutParams as ConstraintLayout.LayoutParams
|
||||
layoutParams2.topMargin = AutoSizeUtils.dp2px(context,119f)
|
||||
layoutParams2.rightMargin = AutoSizeUtils.dp2px(context,70f)
|
||||
layoutParams2.height = AutoSizeUtils.dp2px(context,108f)
|
||||
layoutParams2.width = AutoSizeUtils.dp2px(context,108f)
|
||||
fullscreenButton.layoutParams = layoutParams2
|
||||
fullscreenButton.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun getLayoutId(): Int {
|
||||
return R.layout.taxi_p_video_show
|
||||
}
|
||||
|
||||
override fun updateStartImage() {
|
||||
when (mCurrentState) {
|
||||
GSYVideoView.CURRENT_STATE_PLAYING ->{
|
||||
start.setImageResource(R.drawable.notice_video_pause)
|
||||
aivStartPlay.visibility = View.GONE
|
||||
}
|
||||
else -> {
|
||||
start.setImageResource(R.drawable.notice_video_after_pause)
|
||||
aivStartPlay.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setStateAndUi(state: Int) {
|
||||
super.setStateAndUi(state)
|
||||
if(state==CURRENT_STATE_PLAYING_BUFFERING_START){
|
||||
ToastUtils.showShort("加载中请稍等")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onWindowFocusChanged(hasWindowFocus: Boolean) {
|
||||
super.onWindowFocusChanged(hasWindowFocus)
|
||||
if(isIfCurrentIsFullscreen&&smalllPlayer!=null){
|
||||
if(hasWindowFocus){//获取焦点
|
||||
//onVideoResume()
|
||||
}else{
|
||||
onVideoPause()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun touchDoubleUp() {
|
||||
|
||||
}
|
||||
|
||||
override fun changeUiToNormal() {
|
||||
super.changeUiToNormal()
|
||||
//setViewShowState(fullscreenButton, INVISIBLE)
|
||||
this.statusBarView?.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun changeUiToPreparingShow() {
|
||||
super.changeUiToPreparingShow()
|
||||
this.statusBarView?.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun changeUiToPlayingShow() {
|
||||
super.changeUiToPlayingShow()
|
||||
setViewShowState(fullscreenButton, VISIBLE)
|
||||
this.statusBarView?.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun changeUiToPlayingClear() {
|
||||
super.changeUiToPlayingClear()
|
||||
this.statusBarView?.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun changeUiToPauseShow() {
|
||||
super.changeUiToPauseShow()
|
||||
this.statusBarView?.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun changeUiToPlayingBufferingShow() {
|
||||
super.changeUiToPlayingBufferingShow()
|
||||
this.statusBarView?.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun changeUiToCompleteShow() {
|
||||
super.changeUiToCompleteShow()
|
||||
this.statusBarView?.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
public override fun hideAllWidget() {
|
||||
super.hideAllWidget()
|
||||
this.statusBarView?.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun setProgressAndTime(
|
||||
progress: Int,
|
||||
secProgress: Int,
|
||||
currentTime: Int,
|
||||
totalTime: Int,
|
||||
forceChange: Boolean
|
||||
) {
|
||||
super.setProgressAndTime(progress, secProgress, currentTime, totalTime, forceChange)
|
||||
//时间显示
|
||||
currentTimeTextView.text = TimeTransformUtils.stringForTime(currentTime)
|
||||
totalTimeTextView.text = TimeTransformUtils.stringForTime(totalTime)
|
||||
if(currentTime>=totalTime-3000){//
|
||||
this.currentTime = -1
|
||||
}else{
|
||||
this.currentTime = currentTime
|
||||
}
|
||||
if (progress != 0) {
|
||||
mProgressBar?.progress = progress
|
||||
}
|
||||
}
|
||||
|
||||
override fun showWifiDialog() {
|
||||
//直接播放,不显示WIFI对话框
|
||||
startPlayLogic()
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
mProgressBar?.progress = 0
|
||||
fullVideoPlayer?.let {
|
||||
clearFullscreenLayout(it)
|
||||
}
|
||||
fullVideoPlayer = null
|
||||
if(!isIfCurrentIsFullscreen) {
|
||||
onVideoReset()
|
||||
setVideoAllCallBack(null)
|
||||
}
|
||||
dismissProgressDialog()
|
||||
super.onDetachedFromWindow()
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
super.onClick(v)
|
||||
when (v?.id) {
|
||||
R.id.fullscreen -> {
|
||||
startWindowFullscreenOwn(context)
|
||||
// startWindowFullscreen(context)
|
||||
}
|
||||
R.id.aiv_start_play -> {
|
||||
if(currentState==GSYVideoView.CURRENT_STATE_PAUSE){
|
||||
onVideoResume(false)
|
||||
}else{
|
||||
if (mProgressBar==null) {
|
||||
startPlayLogic()
|
||||
}else {
|
||||
mProgressBar?.let {
|
||||
if(currentTime>0) {
|
||||
seekOnStart = currentTime.toLong()
|
||||
}
|
||||
startPlayLogic()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCompletion() {
|
||||
start.setImageResource(R.drawable.notice_video_after_pause)
|
||||
}
|
||||
|
||||
override fun onSurfaceUpdated(surface: Surface) {
|
||||
super.onSurfaceUpdated(surface)
|
||||
if (mThumbImageViewLayout != null && mThumbImageViewLayout.visibility == View.VISIBLE) {
|
||||
mThumbImageViewLayout.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPrepared() {
|
||||
super.onPrepared()
|
||||
}
|
||||
|
||||
override fun onBufferingUpdate(percent: Int) {
|
||||
super.onBufferingUpdate(percent)
|
||||
|
||||
}
|
||||
|
||||
override fun onError(what: Int, extra: Int) {
|
||||
super.onError(what, extra)
|
||||
mThumbImageViewLayout?.visibility = View.VISIBLE
|
||||
ToastUtils.showLong("哎呀,出错了,看看其他视频吧")
|
||||
currentTime = -1
|
||||
if(isIfCurrentIsFullscreen){
|
||||
smalllPlayer?.clearFullscreenLayout(this)
|
||||
smalllPlayer?.currentTime = -1
|
||||
FullVideoUtils.dismissOverlayView(false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setViewShowState(view: View?, visibility: Int) {
|
||||
if (view === mThumbImageViewLayout && visibility != View.VISIBLE) {
|
||||
return
|
||||
}
|
||||
super.setViewShowState(view, visibility)
|
||||
}
|
||||
|
||||
override fun onSurfaceAvailable(surface: Surface) {
|
||||
super.onSurfaceAvailable(surface)
|
||||
if (GSYVideoType.getRenderType() != GSYVideoType.TEXTURE) {
|
||||
if (mThumbImageViewLayout != null && mThumbImageViewLayout.visibility == View.VISIBLE) {
|
||||
mThumbImageViewLayout.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAutoCompletion() {
|
||||
super.onAutoCompletion()
|
||||
if(mIfCurrentIsFullscreen){
|
||||
if(smalllPlayer!=null){
|
||||
smalllPlayer?.clearFullscreenLayout(this)
|
||||
}
|
||||
FullVideoUtils.dismissOverlayView(false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||
super.onSizeChanged(w, h, oldw, oldh)
|
||||
if (!mIfCurrentIsFullscreen) {
|
||||
this.outlineProvider = TextureVideoViewOutlineProvider(12F)
|
||||
this.clipToOutline = true
|
||||
}
|
||||
}
|
||||
|
||||
fun exitFullScreenMode() {
|
||||
fullVideoPlayer?.let {
|
||||
clearFullscreenLayout(it)
|
||||
FullVideoUtils.dismissOverlayView(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startWindowFullscreenOwn(context:Context){
|
||||
val gsyBaseVideoPlayer = startWindowFullscreen(context)
|
||||
gsyBaseVideoPlayer?.let {
|
||||
val gsyVideoPlayer = it as StandardGSYVideoPlayer
|
||||
gsyVideoPlayer.setLockClickListener(mLockClickListener)
|
||||
gsyVideoPlayer.isNeedLockFull = isNeedLockFull
|
||||
initFullUI(gsyVideoPlayer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initFullUI(standardGSYVideoPlayer: StandardGSYVideoPlayer) {
|
||||
if (mBottomProgressDrawable != null) {
|
||||
standardGSYVideoPlayer.setBottomProgressBarDrawable(mBottomProgressDrawable)
|
||||
}
|
||||
if (mBottomShowProgressDrawable != null && mBottomShowProgressThumbDrawable != null) {
|
||||
standardGSYVideoPlayer.setBottomShowProgressBarDrawable(
|
||||
mBottomShowProgressDrawable,
|
||||
mBottomShowProgressThumbDrawable
|
||||
)
|
||||
}
|
||||
if (mVolumeProgressDrawable != null) {
|
||||
standardGSYVideoPlayer.setDialogVolumeProgressBar(mVolumeProgressDrawable)
|
||||
}
|
||||
if (mDialogProgressBarDrawable != null) {
|
||||
standardGSYVideoPlayer.setDialogProgressBar(mDialogProgressBarDrawable)
|
||||
}
|
||||
if (mDialogProgressHighLightColor >= 0 && mDialogProgressNormalColor >= 0) {
|
||||
standardGSYVideoPlayer.setDialogProgressColor(
|
||||
mDialogProgressHighLightColor,
|
||||
mDialogProgressNormalColor
|
||||
)
|
||||
}
|
||||
standardGSYVideoPlayer.titleTextView?.text = titleTextView.text
|
||||
}
|
||||
|
||||
private fun startWindowFullscreen(context:Context):GSYBaseVideoPlayer?{
|
||||
if (mTextureViewContainer.childCount > 0) {
|
||||
mTextureViewContainer.removeAllViews()
|
||||
}
|
||||
var hadNewConstructor = true
|
||||
|
||||
//切换时关闭非全屏定时器
|
||||
cancelProgressTimer()
|
||||
try {
|
||||
this@ConsultVideoPlayer.javaClass.getConstructor(
|
||||
Context::class.java,
|
||||
Boolean::class.java
|
||||
)
|
||||
} catch (e: java.lang.Exception) {
|
||||
hadNewConstructor = false
|
||||
}
|
||||
try {
|
||||
//通过被重载的不同构造器来选择
|
||||
val constructor: Constructor<ConsultVideoPlayer>
|
||||
val gsyVideoPlayer: ConsultVideoPlayer
|
||||
if (!hadNewConstructor) {
|
||||
constructor = this@ConsultVideoPlayer.javaClass.getConstructor(Context::class.java)
|
||||
gsyVideoPlayer = constructor.newInstance(mContext)
|
||||
} else {
|
||||
constructor = this@ConsultVideoPlayer.javaClass.getConstructor(
|
||||
Context::class.java,
|
||||
Boolean::class.java
|
||||
)
|
||||
gsyVideoPlayer = constructor.newInstance(mContext, true)
|
||||
}
|
||||
this.fullVideoPlayer = gsyVideoPlayer
|
||||
gsyVideoPlayer.id = fullId
|
||||
gsyVideoPlayer.isIfCurrentIsFullscreen = true
|
||||
gsyVideoPlayer.setVideoAllCallBack(mVideoAllCallBack)
|
||||
gsyVideoPlayer.addDrageAnchor()
|
||||
cloneParams(this, gsyVideoPlayer)
|
||||
val frameLayout = FrameLayout(context)
|
||||
if (gsyVideoPlayer.fullscreenButton != null) {
|
||||
gsyVideoPlayer.fullscreenButton.setImageResource(R.drawable.taxi_p_change_normal)
|
||||
gsyVideoPlayer.fullscreenButton.setOnClickListener { v ->
|
||||
if (mBackFromFullScreenListener == null) {
|
||||
clearFullscreenLayout(gsyVideoPlayer)
|
||||
FullVideoUtils.dismissOverlayView(false)
|
||||
} else {
|
||||
mBackFromFullScreenListener.onClick(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 点击视频不展示状态栏
|
||||
gsyVideoPlayer.isHideKey = false
|
||||
gsyVideoPlayer.smalllPlayer = this
|
||||
frameLayout.setBackgroundColor(Color.BLACK)
|
||||
val lp = LayoutParams(width, height)
|
||||
frameLayout.addView(gsyVideoPlayer, lp)
|
||||
gsyVideoPlayer.statusBarView = StatusBarView(context)
|
||||
frameLayout.addView(gsyVideoPlayer.statusBarView)
|
||||
FullVideoUtils.showOverlayView(context as Activity,frameLayout,R.style.och_window_anim_alpha)
|
||||
gsyVideoPlayer.visibility = INVISIBLE
|
||||
frameLayout.visibility = INVISIBLE
|
||||
resolveFullVideoShow(context, gsyVideoPlayer, frameLayout)
|
||||
gsyVideoPlayer.addTextureView()
|
||||
gsyVideoPlayer.startProgressTimer()
|
||||
gsyVideoManager.setLastListener(this)
|
||||
gsyVideoManager.setListener(gsyVideoPlayer)
|
||||
checkoutState()
|
||||
thumbImageViewLayout.visibility = View.VISIBLE
|
||||
return gsyVideoPlayer
|
||||
} catch (e: java.lang.Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 全屏
|
||||
*/
|
||||
override fun resolveFullVideoShow(context: Context?, gsyVideoPlayer: GSYBaseVideoPlayer,
|
||||
frameLayout: FrameLayout) {
|
||||
val lp = gsyVideoPlayer.layoutParams as LayoutParams
|
||||
lp.setMargins(0, 0, 0, 0)
|
||||
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
lp.width = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
lp.gravity = Gravity.BOTTOM
|
||||
gsyVideoPlayer.layoutParams = lp
|
||||
gsyVideoPlayer.isIfCurrentIsFullscreen = true
|
||||
val isVertical = isVerticalFullByVideoSize
|
||||
val isLockLand = isLockLandByAutoFullSize
|
||||
if (isShowFullAnimation) {
|
||||
mInnerHandler.postDelayed({ //autoFull模式下,非横屏视频视频不横屏,并且不自动旋转
|
||||
if (!isVertical && isLockLand && mOrientationUtils != null && mOrientationUtils.isLand != 1) {
|
||||
mOrientationUtils.resolveByClick()
|
||||
}
|
||||
gsyVideoPlayer.visibility = VISIBLE
|
||||
frameLayout.visibility = VISIBLE
|
||||
}, 300)
|
||||
} else {
|
||||
if (!isVertical && isLockLand && mOrientationUtils != null) {
|
||||
mOrientationUtils.resolveByClick()
|
||||
}
|
||||
gsyVideoPlayer.visibility = VISIBLE
|
||||
frameLayout.visibility = VISIBLE
|
||||
}
|
||||
if (mVideoAllCallBack != null) {
|
||||
mVideoAllCallBack.onEnterFullscreen(mOriginUrl, mTitle, gsyVideoPlayer)
|
||||
}
|
||||
mIfCurrentIsFullscreen = true
|
||||
checkoutState()
|
||||
checkAutoFullWithSizeAndAdaptation(gsyVideoPlayer)
|
||||
}
|
||||
|
||||
fun clearFullscreenLayout(gsyVideoPlayer:ConsultVideoPlayer) {
|
||||
if (mIfCurrentIsFullscreen) {
|
||||
mIfCurrentIsFullscreen = false
|
||||
val delay = 100
|
||||
gsyVideoPlayer.smalllPlayer = null
|
||||
mInnerHandler.postDelayed({ resolveNormalVideoShow(gsyVideoPlayer) }, delay.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveNormalVideoShow(gsyVideoPlayer: GSYVideoPlayer) {
|
||||
mCurrentState = gsyVideoManager.lastState
|
||||
cloneParams(gsyVideoPlayer, this)
|
||||
gsyVideoManager.setListener(gsyVideoManager.lastListener())
|
||||
gsyVideoManager.setLastListener(null)
|
||||
gsyVideoPlayer.setVideoAllCallBack(null)
|
||||
setStateAndUi(mCurrentState)
|
||||
addTextureView()
|
||||
mSaveChangeViewTIme = System.currentTimeMillis()
|
||||
if (mVideoAllCallBack != null) {
|
||||
mVideoAllCallBack.onQuitFullscreen(mOriginUrl, mTitle, this)
|
||||
}
|
||||
mIfCurrentIsFullscreen = false
|
||||
if (fullscreenButton != null) {
|
||||
fullscreenButton.setImageResource(enlargeImageRes)
|
||||
}
|
||||
this.fullVideoPlayer = null
|
||||
}
|
||||
|
||||
fun getVideoAllCallBack(): VideoAllCallBack? {
|
||||
return mVideoAllCallBack
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.mogo.och.taxi.passenger.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.lifecycle.ViewModelStore
|
||||
import androidx.lifecycle.ViewModelStoreOwner
|
||||
|
||||
open class WindowRelativeLayout: RelativeLayout, ViewModelStoreOwner {
|
||||
|
||||
constructor(context: Context?) : super(context)
|
||||
|
||||
constructor(context: Context?, attributeSet: AttributeSet) : super(context, attributeSet)
|
||||
|
||||
constructor(context: Context?, attributeSet: AttributeSet, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr)
|
||||
|
||||
constructor(context: Context?, attributeSet: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attributeSet, defStyleAttr, defStyleRes)
|
||||
|
||||
//定义ViewModelStore变量
|
||||
private var mViewModelStore: ViewModelStore = ViewModelStore()
|
||||
|
||||
override fun getViewModelStore(): ViewModelStore {
|
||||
return mViewModelStore
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
mViewModelStore.clear()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package com.mogo.och.taxi.passenger.widget.animutils
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.widget.ImageView
|
||||
import com.mogo.commons.AbsMogoApplication
|
||||
import java.lang.ref.SoftReference
|
||||
|
||||
class AnimationsContainer(resId: Int, fps: Int, imageView: ImageView) {
|
||||
private lateinit var mFrames: IntArray // 帧数组
|
||||
private var mIndex = 0 // 当前帧
|
||||
private var mShouldRun = false // 开始/停止播放用
|
||||
private var mIsRunning = false // 动画是否正在播放,防止重复播放
|
||||
private var mSoftReferenceImageView: SoftReference<ImageView>? = null // 软引用ImageView,以便及时释放掉
|
||||
private var mHandler: Handler? = null
|
||||
private var mDelayMillis = 0
|
||||
private var mOnAnimationStoppedListener: OnAnimationStoppedListener? = null//播放停止监听
|
||||
private var mBitmap: Bitmap? = null
|
||||
private var mBitmapOptions: BitmapFactory.Options? = null //Bitmap管理类,可有效减少Bitmap的OOM问题
|
||||
|
||||
init {
|
||||
createAnimation(imageView, getData(resId), fps)
|
||||
}
|
||||
|
||||
private fun createAnimation(imageView: ImageView, frames: IntArray, fps: Int) {
|
||||
mHandler = Handler(Looper.myLooper()!!)
|
||||
mFrames = frames
|
||||
mIndex = -1
|
||||
mSoftReferenceImageView = SoftReference(imageView)
|
||||
mShouldRun = false
|
||||
mIsRunning = false
|
||||
mDelayMillis = 1000 / fps //帧动画时间间隔,毫秒
|
||||
imageView.setImageResource(mFrames[0])
|
||||
|
||||
// 当图片大小类型相同时进行复用,避免频繁GC
|
||||
val bmp = (imageView.drawable as BitmapDrawable).bitmap
|
||||
val width = bmp.width
|
||||
val height = bmp.height
|
||||
val config = bmp.config
|
||||
mBitmap = Bitmap.createBitmap(width, height, config)
|
||||
mBitmapOptions = BitmapFactory.Options()
|
||||
//设置Bitmap内存复用
|
||||
mBitmapOptions!!.inBitmap = mBitmap //Bitmap复用内存块,类似对象池,避免不必要的内存分配和回收
|
||||
mBitmapOptions!!.inMutable = true //解码时返回可变Bitmap
|
||||
mBitmapOptions!!.inSampleSize = 1 //缩放比例
|
||||
}
|
||||
|
||||
//循环读取下一帧
|
||||
private val next: Int
|
||||
get() {
|
||||
mIndex++
|
||||
if (mIndex >= mFrames.size) mIndex = 0
|
||||
return mFrames[mIndex]
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放动画,同步锁防止多线程读帧时,数据安全问题
|
||||
*/
|
||||
@Synchronized
|
||||
fun start() {
|
||||
mShouldRun = true
|
||||
if (mIsRunning) return
|
||||
val runnable: Runnable = object : Runnable {
|
||||
override fun run() {
|
||||
val imageView = mSoftReferenceImageView!!.get()
|
||||
if (!mShouldRun || imageView == null) {
|
||||
mIsRunning = false
|
||||
if (mOnAnimationStoppedListener != null) {
|
||||
mOnAnimationStoppedListener!!.AnimationStopped()
|
||||
}
|
||||
return
|
||||
}
|
||||
mIsRunning = true
|
||||
//新开线程去读下一帧
|
||||
mHandler!!.postDelayed(this, mDelayMillis.toLong())
|
||||
if (imageView.isShown) {
|
||||
val imageRes: Int = next
|
||||
if (mBitmap != null) { // so Build.VERSION.SDK_INT >= 11
|
||||
var bitmap: Bitmap? = null
|
||||
try {
|
||||
bitmap = BitmapFactory.decodeResource(
|
||||
imageView.resources,
|
||||
imageRes,
|
||||
mBitmapOptions
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
if (bitmap != null) {
|
||||
imageView.setImageBitmap(bitmap)
|
||||
} else {
|
||||
imageView.setImageResource(imageRes)
|
||||
mBitmap!!.recycle()
|
||||
mBitmap = null
|
||||
}
|
||||
} else {
|
||||
imageView.setImageResource(imageRes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mHandler!!.post(runnable)
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止播放
|
||||
*/
|
||||
@Synchronized
|
||||
fun stop() {
|
||||
mShouldRun = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置停止播放监听
|
||||
* @param listener 设置监听
|
||||
*/
|
||||
fun setOnAnimStopListener(listener: OnAnimationStoppedListener?) {
|
||||
mOnAnimationStoppedListener = listener
|
||||
}
|
||||
|
||||
/**
|
||||
* 从xml中读取帧数组
|
||||
* @param resId
|
||||
* @return
|
||||
*/
|
||||
private fun getData(resId: Int): IntArray {
|
||||
val array = AbsMogoApplication.getApp().resources.obtainTypedArray(resId)
|
||||
val len = array.length()
|
||||
val intArray = IntArray(array.length())
|
||||
for (i in 0 until len) {
|
||||
intArray[i] = array.getResourceId(i, 0)
|
||||
}
|
||||
array.recycle()
|
||||
return intArray
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止播放监听
|
||||
*/
|
||||
interface OnAnimationStoppedListener {
|
||||
fun AnimationStopped()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.util.AttributeSet
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.annotation.AIndicatorOrientation
|
||||
|
||||
import com.zhpan.indicator.base.BaseIndicatorView
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.drawer.DrawerProxy
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorOrientation
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.option.AttrsController
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.option.IndicatorOptions
|
||||
|
||||
class IndicatorView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : BaseIndicatorView(context, attrs, defStyleAttr) {
|
||||
|
||||
private var mDrawerProxy: DrawerProxy
|
||||
|
||||
init {
|
||||
AttrsController.initAttrs(context, attrs, mIndicatorOptions)
|
||||
mDrawerProxy = DrawerProxy(mIndicatorOptions)
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
mDrawerProxy.onLayout(changed, left, top, right, bottom)
|
||||
}
|
||||
|
||||
override fun onMeasure(
|
||||
widthMeasureSpec: Int,
|
||||
heightMeasureSpec: Int
|
||||
) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
val measureResult = mDrawerProxy.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
setMeasuredDimension(measureResult.measureWidth, measureResult.measureHeight)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
rotateCanvas(canvas)
|
||||
mDrawerProxy.onDraw(canvas)
|
||||
}
|
||||
|
||||
override fun setIndicatorOptions(options: IndicatorOptions) {
|
||||
super.setIndicatorOptions(options)
|
||||
mDrawerProxy.setIndicatorOptions(options)
|
||||
}
|
||||
|
||||
override fun notifyDataChanged(itemCount:Int) {
|
||||
mDrawerProxy = DrawerProxy(mIndicatorOptions)
|
||||
super.notifyDataChanged(itemCount)
|
||||
}
|
||||
|
||||
private fun rotateCanvas(canvas: Canvas) {
|
||||
if (mIndicatorOptions.orientation == IndicatorOrientation.INDICATOR_VERTICAL) {
|
||||
canvas.rotate(90f, width / 2f, width / 2f)
|
||||
} else if (mIndicatorOptions.orientation == IndicatorOrientation.INDICATOR_RTL) {
|
||||
canvas.rotate(180f, width / 2f, height / 2f)
|
||||
}
|
||||
}
|
||||
|
||||
fun setOrientation(@AIndicatorOrientation orientation: Int) {
|
||||
mIndicatorOptions.orientation = orientation
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.annotation
|
||||
|
||||
import androidx.annotation.IntDef
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorOrientation
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhangpan
|
||||
* @date 2021/1/21
|
||||
*/
|
||||
@IntDef(
|
||||
IndicatorOrientation.INDICATOR_HORIZONTAL, IndicatorOrientation.INDICATOR_VERTICAL,
|
||||
IndicatorOrientation.INDICATOR_RTL
|
||||
)
|
||||
@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FIELD)
|
||||
annotation class AIndicatorOrientation()
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.annotation
|
||||
|
||||
import androidx.annotation.IntDef
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorSlideMode.Companion.COLOR
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorSlideMode.Companion.NORMAL
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorSlideMode.Companion.SCALE
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorSlideMode.Companion.SMOOTH
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorSlideMode.Companion.WORM
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by zhangpan on 2019-10-18.
|
||||
* Description:
|
||||
</pre> *
|
||||
*/
|
||||
@IntDef(NORMAL, SMOOTH, WORM, COLOR, SCALE)
|
||||
@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FIELD)
|
||||
annotation class AIndicatorSlideMode
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.annotation
|
||||
|
||||
import androidx.annotation.IntDef
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorStyle
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by zhangpan on 2019-10-18.
|
||||
* Description:
|
||||
</pre> *
|
||||
*/
|
||||
@IntDef(IndicatorStyle.CIRCLE, IndicatorStyle.DASH, IndicatorStyle.ROUND_RECT)
|
||||
@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FIELD)
|
||||
annotation class AIndicatorStyle
|
||||
@@ -0,0 +1,205 @@
|
||||
package com.zhpan.indicator.base
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.annotation.AIndicatorSlideMode
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.annotation.AIndicatorStyle
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.base.IIndicator
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorSlideMode
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.option.IndicatorOptions
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by zhangpan on 2019-09-04.
|
||||
* Description:IndicatorView基类,处理了页面滑动。
|
||||
* </pre>
|
||||
*/
|
||||
@Suppress("UNUSED")
|
||||
open class BaseIndicatorView constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet?,
|
||||
defStyleAttr: Int
|
||||
) : View(context, attrs, defStyleAttr), IIndicator {
|
||||
|
||||
var mIndicatorOptions: IndicatorOptions
|
||||
|
||||
private var recyclerView: RecyclerView? = null
|
||||
|
||||
init {
|
||||
mIndicatorOptions = IndicatorOptions()
|
||||
}
|
||||
|
||||
fun setPageSize(pageSize: Int): BaseIndicatorView {
|
||||
mIndicatorOptions.pageSize = pageSize
|
||||
return this
|
||||
}
|
||||
|
||||
// 页面选定
|
||||
fun onPageSelected(position: Int) {
|
||||
if (getSlideMode() == IndicatorSlideMode.NORMAL) {
|
||||
setCurrentPosition(position)
|
||||
setSlideProgress(0f)
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
// 滚动距离
|
||||
fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||
if (getSlideMode() != IndicatorSlideMode.NORMAL && getPageSize() > 1) {
|
||||
scrollSlider(position, positionOffset)
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
private fun scrollSlider(position: Int, positionOffset: Float) {
|
||||
if (mIndicatorOptions.slideMode == IndicatorSlideMode.SCALE
|
||||
|| mIndicatorOptions.slideMode == IndicatorSlideMode.COLOR) {
|
||||
setCurrentPosition(position)
|
||||
setSlideProgress(positionOffset)
|
||||
} else {
|
||||
if (position % getPageSize() == getPageSize() - 1) { // 最后一个页面与第一个页面
|
||||
if (positionOffset < 0.5) {
|
||||
setCurrentPosition(position)
|
||||
setSlideProgress(0f)
|
||||
} else {
|
||||
setCurrentPosition(0)
|
||||
setSlideProgress(0f)
|
||||
}
|
||||
} else { // 中间页面
|
||||
setCurrentPosition(position)
|
||||
setSlideProgress(positionOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun notifyDataChanged(itemCount: Int) {
|
||||
setPageSize(itemCount)
|
||||
requestLayout()
|
||||
invalidate()
|
||||
}
|
||||
|
||||
private fun setupViewPager() {
|
||||
|
||||
}
|
||||
|
||||
fun getNormalSlideWidth(): Float {
|
||||
return mIndicatorOptions.normalSliderWidth
|
||||
}
|
||||
|
||||
fun setNormalSlideWidth(normalSliderWidth: Float) {
|
||||
mIndicatorOptions.normalSliderWidth = normalSliderWidth
|
||||
}
|
||||
|
||||
fun getCheckedSlideWidth(): Float {
|
||||
return mIndicatorOptions.checkedSliderWidth
|
||||
}
|
||||
|
||||
fun setCheckedSlideWidth(checkedSliderWidth: Float) {
|
||||
mIndicatorOptions.checkedSliderWidth = checkedSliderWidth
|
||||
}
|
||||
|
||||
val checkedSliderWidth: Float
|
||||
get() = mIndicatorOptions.checkedSliderWidth
|
||||
|
||||
fun setCurrentPosition(currentPosition: Int) {
|
||||
mIndicatorOptions.currentPosition = currentPosition
|
||||
}
|
||||
|
||||
fun getCurrentPosition(): Int {
|
||||
return mIndicatorOptions.currentPosition
|
||||
}
|
||||
|
||||
fun getIndicatorGap(indicatorGap: Float) {
|
||||
mIndicatorOptions.sliderGap = indicatorGap
|
||||
}
|
||||
|
||||
fun setIndicatorGap(indicatorGap: Float) {
|
||||
mIndicatorOptions.sliderGap = indicatorGap
|
||||
}
|
||||
|
||||
fun setCheckedColor(@ColorInt normalColor: Int) {
|
||||
mIndicatorOptions.checkedSliderColor = normalColor
|
||||
}
|
||||
|
||||
fun getCheckedColor(): Int {
|
||||
return mIndicatorOptions.checkedSliderColor
|
||||
}
|
||||
|
||||
fun setNormalColor(@ColorInt normalColor: Int) {
|
||||
mIndicatorOptions.normalSliderColor = normalColor
|
||||
}
|
||||
|
||||
fun getSlideProgress(): Float {
|
||||
return mIndicatorOptions.slideProgress
|
||||
}
|
||||
|
||||
fun setSlideProgress(slideProgress: Float) {
|
||||
mIndicatorOptions.slideProgress = slideProgress
|
||||
}
|
||||
|
||||
fun getPageSize(): Int {
|
||||
return mIndicatorOptions.pageSize
|
||||
}
|
||||
|
||||
fun setSliderColor(
|
||||
@ColorInt normalColor: Int,
|
||||
@ColorInt selectedColor: Int,
|
||||
@ColorInt selectedEndColor: Int
|
||||
): BaseIndicatorView {
|
||||
mIndicatorOptions.setSliderColor(normalColor, selectedColor,selectedEndColor)
|
||||
return this
|
||||
}
|
||||
|
||||
fun setSliderWidth(sliderWidth: Float): BaseIndicatorView {
|
||||
mIndicatorOptions.setSliderWidth(sliderWidth)
|
||||
return this
|
||||
}
|
||||
|
||||
fun setSliderWidth(
|
||||
normalSliderWidth: Float,
|
||||
selectedSliderWidth: Float
|
||||
): BaseIndicatorView {
|
||||
mIndicatorOptions.setSliderWidth(normalSliderWidth, selectedSliderWidth)
|
||||
return this
|
||||
}
|
||||
|
||||
fun setSliderGap(sliderGap: Float): BaseIndicatorView {
|
||||
mIndicatorOptions.sliderGap = sliderGap
|
||||
return this
|
||||
}
|
||||
|
||||
fun getSlideMode(): Int {
|
||||
return mIndicatorOptions.slideMode
|
||||
}
|
||||
|
||||
fun setSlideMode(@AIndicatorSlideMode slideMode: Int): BaseIndicatorView {
|
||||
mIndicatorOptions.slideMode = slideMode
|
||||
return this
|
||||
}
|
||||
|
||||
fun setIndicatorStyle(@AIndicatorStyle indicatorStyle: Int): BaseIndicatorView {
|
||||
mIndicatorOptions.indicatorStyle = indicatorStyle
|
||||
return this
|
||||
}
|
||||
|
||||
fun setSliderHeight(sliderHeight: Float): BaseIndicatorView {
|
||||
mIndicatorOptions.sliderHeight = sliderHeight
|
||||
return this
|
||||
}
|
||||
|
||||
fun showIndicatorWhenOneItem(showIndicatorWhenOneItem: Boolean) {
|
||||
mIndicatorOptions.showIndicatorOneItem = showIndicatorWhenOneItem
|
||||
}
|
||||
|
||||
fun onPageScrollStateChanged(state: Int) {
|
||||
}
|
||||
|
||||
override fun setIndicatorOptions(options: IndicatorOptions) {
|
||||
mIndicatorOptions = options
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.base
|
||||
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.option.IndicatorOptions
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by zhangpan on 2019-09-02.
|
||||
* Description:
|
||||
</pre> *
|
||||
*/
|
||||
interface IIndicator {
|
||||
|
||||
fun notifyDataChanged(itemCount:Int)
|
||||
|
||||
fun setIndicatorOptions(options: IndicatorOptions)
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.drawer
|
||||
|
||||
import android.animation.ArgbEvaluator
|
||||
import android.graphics.Paint
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorOrientation
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorSlideMode
|
||||
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.option.IndicatorOptions
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by zhpan on 2019/11/23.
|
||||
* Description:
|
||||
</pre> *
|
||||
*/
|
||||
abstract class BaseDrawer internal constructor(internal var mIndicatorOptions: IndicatorOptions) :
|
||||
IDrawer {
|
||||
|
||||
private val mMeasureResult: MeasureResult
|
||||
internal var maxWidth: Float = 0.toFloat()
|
||||
internal var minWidth: Float = 0.toFloat()
|
||||
internal var mPaint: Paint = Paint()
|
||||
internal var argbEvaluator: ArgbEvaluator? = null
|
||||
|
||||
companion object {
|
||||
const val INDICATOR_PADDING_ADDITION = 6
|
||||
const val INDICATOR_PADDING = 3
|
||||
}
|
||||
|
||||
protected val isWidthEquals: Boolean
|
||||
get() = mIndicatorOptions.normalSliderWidth == mIndicatorOptions.checkedSliderWidth
|
||||
|
||||
init {
|
||||
mPaint.isAntiAlias = true
|
||||
mMeasureResult = MeasureResult()
|
||||
if (mIndicatorOptions.slideMode == IndicatorSlideMode.SCALE
|
||||
|| mIndicatorOptions.slideMode == IndicatorSlideMode.COLOR
|
||||
) {
|
||||
argbEvaluator = ArgbEvaluator()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMeasure(
|
||||
widthMeasureSpec: Int,
|
||||
heightMeasureSpec: Int
|
||||
): MeasureResult {
|
||||
maxWidth =
|
||||
mIndicatorOptions.normalSliderWidth.coerceAtLeast(mIndicatorOptions.checkedSliderWidth)
|
||||
minWidth =
|
||||
mIndicatorOptions.normalSliderWidth.coerceAtMost(mIndicatorOptions.checkedSliderWidth)
|
||||
if (mIndicatorOptions.orientation == IndicatorOrientation.INDICATOR_VERTICAL) {
|
||||
mMeasureResult.setMeasureResult(measureHeight(), measureWidth())
|
||||
} else {
|
||||
mMeasureResult.setMeasureResult(measureWidth(), measureHeight())
|
||||
}
|
||||
return mMeasureResult
|
||||
}
|
||||
|
||||
protected open fun measureHeight(): Int {
|
||||
return mIndicatorOptions.sliderHeight.toInt() + INDICATOR_PADDING
|
||||
}
|
||||
|
||||
private fun measureWidth(): Int {
|
||||
val pageSize = mIndicatorOptions.pageSize
|
||||
val indicatorGap = mIndicatorOptions.sliderGap
|
||||
return ((pageSize - 1) * indicatorGap + maxWidth + (pageSize - 1) * minWidth).toInt() + INDICATOR_PADDING_ADDITION
|
||||
}
|
||||
|
||||
override fun onLayout(
|
||||
changed: Boolean,
|
||||
left: Int,
|
||||
top: Int,
|
||||
right: Int,
|
||||
bottom: Int
|
||||
) {
|
||||
}
|
||||
|
||||
inner class MeasureResult {
|
||||
|
||||
var measureWidth: Int = 0
|
||||
internal set
|
||||
|
||||
var measureHeight: Int = 0
|
||||
internal set
|
||||
|
||||
internal fun setMeasureResult(
|
||||
measureWidth: Int,
|
||||
measureHeight: Int
|
||||
) {
|
||||
this.measureWidth = measureWidth
|
||||
this.measureHeight = measureHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.drawer
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.RectF
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorSlideMode
|
||||
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.option.IndicatorOptions
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.utils.IndicatorUtils
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by zhpan on 2019/11/23.
|
||||
* Description: Circle Indicator drawer.
|
||||
</pre> *
|
||||
*/
|
||||
class CircleDrawer internal constructor(indicatorOptions: IndicatorOptions) : BaseDrawer(
|
||||
indicatorOptions
|
||||
) {
|
||||
|
||||
private val rectF = RectF()
|
||||
|
||||
override fun measureHeight(): Int {
|
||||
return maxWidth.toInt() + INDICATOR_PADDING_ADDITION
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
val pageSize = mIndicatorOptions.pageSize
|
||||
if (pageSize > 1 || mIndicatorOptions.showIndicatorOneItem && pageSize == 1) {
|
||||
drawNormal(canvas)
|
||||
drawSlider(canvas)
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawNormal(canvas: Canvas) {
|
||||
val normalIndicatorWidth = mIndicatorOptions.normalSliderWidth
|
||||
mPaint.color = mIndicatorOptions.normalSliderColor
|
||||
for (i in 0 until mIndicatorOptions.pageSize) {
|
||||
val coordinateX = IndicatorUtils.getCoordinateX(mIndicatorOptions, maxWidth, i)
|
||||
val coordinateY = IndicatorUtils.getCoordinateY(maxWidth)
|
||||
drawCircle(canvas, coordinateX, coordinateY, normalIndicatorWidth / 2)
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawSlider(canvas: Canvas) {
|
||||
mPaint.color = mIndicatorOptions.checkedSliderColor
|
||||
when (mIndicatorOptions.slideMode) {
|
||||
IndicatorSlideMode.NORMAL, IndicatorSlideMode.SMOOTH -> drawCircleSlider(canvas)
|
||||
IndicatorSlideMode.WORM -> drawWormSlider(canvas)
|
||||
IndicatorSlideMode.SCALE -> drawScaleSlider(canvas)
|
||||
IndicatorSlideMode.COLOR -> drawColor(canvas)
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawColor(canvas: Canvas) {
|
||||
val currentPosition = mIndicatorOptions.currentPosition
|
||||
val slideProgress = mIndicatorOptions.slideProgress
|
||||
val coordinateX = IndicatorUtils.getCoordinateX(mIndicatorOptions, maxWidth, currentPosition)
|
||||
val coordinateY = IndicatorUtils.getCoordinateY(maxWidth)
|
||||
var evaluate = argbEvaluator?.evaluate(
|
||||
slideProgress, mIndicatorOptions.checkedSliderColor, mIndicatorOptions.normalSliderColor
|
||||
)
|
||||
mPaint.color = (evaluate as Int)
|
||||
drawCircle(canvas, coordinateX, coordinateY, mIndicatorOptions.normalSliderWidth / 2)
|
||||
|
||||
// 绘制可循环的ViewPager指示器渐变
|
||||
evaluate = argbEvaluator?.evaluate(
|
||||
1 - slideProgress, mIndicatorOptions.checkedSliderColor, mIndicatorOptions.normalSliderColor
|
||||
)
|
||||
mPaint.color = evaluate as Int
|
||||
val nextCoordinateX = if (currentPosition == mIndicatorOptions.pageSize - 1) {
|
||||
IndicatorUtils.getCoordinateX(mIndicatorOptions, maxWidth, 0)
|
||||
} else {
|
||||
coordinateX + mIndicatorOptions.sliderGap + mIndicatorOptions.normalSliderWidth
|
||||
}
|
||||
drawCircle(canvas, nextCoordinateX, coordinateY, mIndicatorOptions.checkedSliderWidth / 2)
|
||||
}
|
||||
|
||||
private fun drawScaleSlider(canvas: Canvas) {
|
||||
val currentPosition = mIndicatorOptions.currentPosition
|
||||
val slideProgress = mIndicatorOptions.slideProgress
|
||||
val coordinateX = IndicatorUtils.getCoordinateX(mIndicatorOptions, maxWidth, currentPosition)
|
||||
val coordinateY = IndicatorUtils.getCoordinateY(maxWidth)
|
||||
if (slideProgress < 1) {
|
||||
val evaluate = argbEvaluator?.evaluate(
|
||||
slideProgress, mIndicatorOptions.checkedSliderColor, mIndicatorOptions.normalSliderColor
|
||||
)
|
||||
mPaint.color = (evaluate as Int)
|
||||
val radius =
|
||||
mIndicatorOptions.checkedSliderWidth / 2 - (mIndicatorOptions.checkedSliderWidth / 2 - mIndicatorOptions.normalSliderWidth / 2) * slideProgress
|
||||
drawCircle(canvas, coordinateX, coordinateY, radius)
|
||||
}
|
||||
|
||||
if (currentPosition == mIndicatorOptions.pageSize - 1) {
|
||||
val evaluate = argbEvaluator?.evaluate(
|
||||
slideProgress, mIndicatorOptions.normalSliderColor, mIndicatorOptions.checkedSliderColor
|
||||
)
|
||||
mPaint.color = evaluate as Int
|
||||
val nextCoordinateX = maxWidth / 2
|
||||
val nextRadius = minWidth / 2 + (maxWidth / 2 - minWidth / 2) * (slideProgress)
|
||||
drawCircle(canvas, nextCoordinateX, coordinateY, nextRadius)
|
||||
} else {
|
||||
if (slideProgress > 0) {
|
||||
val evaluate = argbEvaluator?.evaluate(
|
||||
slideProgress, mIndicatorOptions.normalSliderColor, mIndicatorOptions.checkedSliderColor
|
||||
)
|
||||
mPaint.color = evaluate as Int
|
||||
val nextCoordinateX =
|
||||
coordinateX + mIndicatorOptions.sliderGap + mIndicatorOptions.normalSliderWidth
|
||||
val nextRadius =
|
||||
mIndicatorOptions.normalSliderWidth / 2 + (mIndicatorOptions.checkedSliderWidth / 2 - mIndicatorOptions.normalSliderWidth / 2) * slideProgress
|
||||
drawCircle(canvas, nextCoordinateX, coordinateY, nextRadius)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawCircleSlider(canvas: Canvas) {
|
||||
val currentPosition = mIndicatorOptions.currentPosition
|
||||
val startCoordinateX =
|
||||
IndicatorUtils.getCoordinateX(mIndicatorOptions, maxWidth, currentPosition)
|
||||
val endCoordinateX = IndicatorUtils.getCoordinateX(
|
||||
mIndicatorOptions, maxWidth, (currentPosition + 1) % mIndicatorOptions.pageSize
|
||||
)
|
||||
val coordinateX =
|
||||
startCoordinateX + (endCoordinateX - startCoordinateX) * mIndicatorOptions.slideProgress
|
||||
val coordinateY = IndicatorUtils.getCoordinateY(maxWidth)
|
||||
val radius = mIndicatorOptions.checkedSliderWidth / 2
|
||||
drawCircle(canvas, coordinateX, coordinateY, radius)
|
||||
}
|
||||
|
||||
private fun drawWormSlider(canvas: Canvas) {
|
||||
val sliderHeight = mIndicatorOptions.normalSliderWidth
|
||||
val slideProgress = mIndicatorOptions.slideProgress
|
||||
val currentPosition = mIndicatorOptions.currentPosition
|
||||
val distance = mIndicatorOptions.sliderGap + mIndicatorOptions.normalSliderWidth
|
||||
val startCoordinateX =
|
||||
IndicatorUtils.getCoordinateX(mIndicatorOptions, maxWidth, currentPosition)
|
||||
val left = startCoordinateX + (distance * (slideProgress - 0.5f) * 2.0f).coerceAtLeast(
|
||||
0f
|
||||
) - mIndicatorOptions.normalSliderWidth / 2 + INDICATOR_PADDING
|
||||
val right = startCoordinateX + (distance * slideProgress * 2f).coerceAtMost(
|
||||
distance
|
||||
) + mIndicatorOptions.normalSliderWidth / 2 + INDICATOR_PADDING
|
||||
rectF.set(left, INDICATOR_PADDING.toFloat(), right, sliderHeight + INDICATOR_PADDING)
|
||||
canvas.drawRoundRect(rectF, sliderHeight, sliderHeight, mPaint)
|
||||
}
|
||||
|
||||
private fun drawCircle(
|
||||
canvas: Canvas,
|
||||
coordinateX: Float,
|
||||
coordinateY: Float,
|
||||
radius: Float
|
||||
) {
|
||||
canvas.drawCircle(
|
||||
coordinateX + INDICATOR_PADDING, coordinateY + INDICATOR_PADDING, radius, mPaint
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.drawer
|
||||
|
||||
import android.graphics.Canvas
|
||||
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.option.IndicatorOptions
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by zhpan on 2019/11/23.
|
||||
* Description: Dash Indicator Drawer.
|
||||
</pre> *
|
||||
*/
|
||||
class DashDrawer internal constructor(indicatorOptions: IndicatorOptions) : RectDrawer(
|
||||
indicatorOptions
|
||||
) {
|
||||
|
||||
override fun drawDash(canvas: Canvas) {
|
||||
canvas.drawRect(mRectF, mPaint)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.drawer
|
||||
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorStyle
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.option.IndicatorOptions
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by zhpan on 2019/11/24.
|
||||
* Description: Indicator Drawer Factory.
|
||||
</pre> *
|
||||
*/
|
||||
internal object DrawerFactory {
|
||||
fun createDrawer(indicatorOptions: IndicatorOptions): IDrawer {
|
||||
return when (indicatorOptions.indicatorStyle) {
|
||||
IndicatorStyle.DASH -> DashDrawer(indicatorOptions)
|
||||
IndicatorStyle.ROUND_RECT -> RoundRectDrawer(indicatorOptions)
|
||||
else -> CircleDrawer(indicatorOptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.drawer
|
||||
|
||||
import android.graphics.Canvas
|
||||
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.option.IndicatorOptions
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by zhpan on 2019/11/23.
|
||||
* Description: Indicator Drawer Proxy.
|
||||
</pre> *
|
||||
*/
|
||||
class DrawerProxy(indicatorOptions: IndicatorOptions) : IDrawer {
|
||||
|
||||
private lateinit var mIDrawer: IDrawer
|
||||
|
||||
init {
|
||||
init(indicatorOptions)
|
||||
}
|
||||
|
||||
private fun init(indicatorOptions: IndicatorOptions) {
|
||||
mIDrawer = DrawerFactory.createDrawer(indicatorOptions)
|
||||
}
|
||||
|
||||
override fun onLayout(
|
||||
changed: Boolean,
|
||||
left: Int,
|
||||
top: Int,
|
||||
right: Int,
|
||||
bottom: Int
|
||||
) {
|
||||
}
|
||||
|
||||
override fun onMeasure(
|
||||
widthMeasureSpec: Int,
|
||||
heightMeasureSpec: Int
|
||||
): BaseDrawer.MeasureResult {
|
||||
return mIDrawer.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
mIDrawer.onDraw(canvas)
|
||||
}
|
||||
|
||||
fun setIndicatorOptions(indicatorOptions: IndicatorOptions) {
|
||||
init(indicatorOptions)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.drawer
|
||||
|
||||
import android.graphics.Canvas
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by zhpan on 2019/11/23.
|
||||
* Description:
|
||||
</pre> *
|
||||
*/
|
||||
interface IDrawer {
|
||||
|
||||
fun onLayout(
|
||||
changed: Boolean,
|
||||
left: Int,
|
||||
top: Int,
|
||||
right: Int,
|
||||
bottom: Int
|
||||
)
|
||||
|
||||
fun onMeasure(
|
||||
widthMeasureSpec: Int,
|
||||
heightMeasureSpec: Int
|
||||
): BaseDrawer.MeasureResult
|
||||
|
||||
fun onDraw(canvas: Canvas)
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.drawer
|
||||
|
||||
import android.graphics.*
|
||||
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorSlideMode
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.option.IndicatorOptions
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.utils.IndicatorUtils
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by zhpan on 2020/1/17.
|
||||
* Description:
|
||||
</pre> *
|
||||
*/
|
||||
open class RectDrawer internal constructor(indicatorOptions: IndicatorOptions) : BaseDrawer(
|
||||
indicatorOptions
|
||||
) {
|
||||
internal var mRectF: RectF = RectF()
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
val pageSize = mIndicatorOptions.pageSize
|
||||
if (pageSize > 1 || mIndicatorOptions.showIndicatorOneItem && pageSize == 1) {
|
||||
if (isWidthEquals && mIndicatorOptions.slideMode != IndicatorSlideMode.NORMAL) {
|
||||
drawUncheckedSlider(canvas, pageSize)
|
||||
drawCheckedSlider(canvas)
|
||||
} else { // 单独处理normalWidth与checkedWidth不一致的情况
|
||||
if (mIndicatorOptions.slideMode == IndicatorSlideMode.SCALE) {
|
||||
for (i in 0 until pageSize) {
|
||||
drawScaleSlider(canvas, i)
|
||||
}
|
||||
} else {
|
||||
drawInequalitySlider(canvas, pageSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawScaleSlider(
|
||||
canvas: Canvas,
|
||||
i: Int
|
||||
) {
|
||||
val checkedColor = mIndicatorOptions.checkedSliderColor
|
||||
val indicatorGap = mIndicatorOptions.sliderGap
|
||||
val sliderHeight = mIndicatorOptions.sliderHeight
|
||||
val currentPosition = mIndicatorOptions.currentPosition
|
||||
val normalWidth = mIndicatorOptions.normalSliderWidth
|
||||
val checkedWidth = mIndicatorOptions.checkedSliderWidth
|
||||
when {
|
||||
i < currentPosition -> {
|
||||
mPaint.color = mIndicatorOptions.normalSliderColor
|
||||
val left: Float = if (currentPosition == mIndicatorOptions.pageSize - 1) {
|
||||
(i * normalWidth + i * indicatorGap) + (checkedWidth - normalWidth) * mIndicatorOptions.slideProgress
|
||||
} else {
|
||||
(i * normalWidth + i * indicatorGap)
|
||||
}
|
||||
mRectF.set(left, 0f, left + normalWidth, sliderHeight)
|
||||
drawRoundRect(canvas, sliderHeight, sliderHeight,mRectF.width()>checkedWidth/2)
|
||||
}
|
||||
i == currentPosition -> {
|
||||
mPaint.color = checkedColor
|
||||
val slideProgress = mIndicatorOptions.slideProgress
|
||||
if (currentPosition == mIndicatorOptions.pageSize - 1) {
|
||||
val evaluate = argbEvaluator?.evaluate(
|
||||
slideProgress, checkedColor, mIndicatorOptions.normalSliderColor
|
||||
)
|
||||
mPaint.color = (evaluate as Int)
|
||||
val right =
|
||||
(mIndicatorOptions.pageSize - 1) * (normalWidth + mIndicatorOptions.sliderGap) + checkedWidth
|
||||
val left = right - checkedWidth + (checkedWidth - normalWidth) * (slideProgress)
|
||||
mRectF.set(left, 0f, right, sliderHeight)
|
||||
drawRoundRect(canvas, sliderHeight, sliderHeight,mRectF.width()>checkedWidth/2)
|
||||
} else {
|
||||
if (slideProgress < 1) {
|
||||
val evaluate = argbEvaluator?.evaluate(
|
||||
slideProgress, checkedColor, mIndicatorOptions.normalSliderColor
|
||||
)
|
||||
mPaint.color = (evaluate as Int)
|
||||
val left = i * normalWidth + i * indicatorGap
|
||||
val right = left + normalWidth + (checkedWidth - normalWidth) * (1 - slideProgress)
|
||||
mRectF.set(left, 0f, right, sliderHeight)
|
||||
drawRoundRect(canvas, sliderHeight, sliderHeight,mRectF.width()>checkedWidth/2)
|
||||
}
|
||||
}
|
||||
|
||||
if (currentPosition == mIndicatorOptions.pageSize - 1) {
|
||||
if (slideProgress > 0) {
|
||||
val evaluate = argbEvaluator?.evaluate(
|
||||
1 - slideProgress, checkedColor, mIndicatorOptions.normalSliderColor
|
||||
)
|
||||
mPaint.color = evaluate as Int
|
||||
val left = 0f
|
||||
val right = left + normalWidth + (checkedWidth - normalWidth) * slideProgress
|
||||
|
||||
mRectF.set(left, 0f, right, sliderHeight)
|
||||
drawRoundRect(canvas, sliderHeight, sliderHeight,mRectF.width()>checkedWidth/2)
|
||||
}
|
||||
} else {
|
||||
if (slideProgress > 0) {
|
||||
val evaluate = argbEvaluator?.evaluate(
|
||||
1 - slideProgress, checkedColor, mIndicatorOptions.normalSliderColor
|
||||
)
|
||||
mPaint.color = evaluate as Int
|
||||
val right =
|
||||
i * normalWidth + i * indicatorGap + normalWidth + (indicatorGap + checkedWidth)
|
||||
val left = right - (normalWidth) - (checkedWidth - normalWidth) * (slideProgress)
|
||||
mRectF.set(left, 0f, right, sliderHeight)
|
||||
drawRoundRect(canvas, sliderHeight, sliderHeight,mRectF.width()>checkedWidth/2)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
if ((currentPosition + 1 != i || mIndicatorOptions.slideProgress == 0f)) { // 避免多余绘制
|
||||
mPaint.color = mIndicatorOptions.normalSliderColor
|
||||
val left = i * minWidth + i * indicatorGap + (checkedWidth - minWidth)
|
||||
mRectF.set(left, 0f, left + minWidth, sliderHeight)
|
||||
drawRoundRect(canvas, sliderHeight, sliderHeight,false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawUncheckedSlider(
|
||||
canvas: Canvas,
|
||||
pageSize: Int
|
||||
) {
|
||||
for (i in 0 until pageSize) {
|
||||
mPaint.color = mIndicatorOptions.normalSliderColor
|
||||
val left = i * maxWidth + i * +mIndicatorOptions.sliderGap + (maxWidth - minWidth)
|
||||
mRectF.set(left, 0f, left + minWidth, mIndicatorOptions.sliderHeight)
|
||||
drawRoundRect(canvas, mIndicatorOptions.sliderHeight, mIndicatorOptions.sliderHeight,false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawInequalitySlider(
|
||||
canvas: Canvas,
|
||||
pageSize: Int
|
||||
) {
|
||||
var left = 0f
|
||||
for (i in 0 until pageSize) {
|
||||
val sliderWidth = (if (i == mIndicatorOptions.currentPosition) maxWidth else minWidth)
|
||||
mPaint.color =
|
||||
if (i == mIndicatorOptions.currentPosition) mIndicatorOptions.checkedSliderColor else mIndicatorOptions.normalSliderColor
|
||||
mRectF.set(left, 0f, left + sliderWidth, mIndicatorOptions.sliderHeight)
|
||||
drawRoundRect(canvas, mIndicatorOptions.sliderHeight, mIndicatorOptions.sliderHeight,false)
|
||||
left += sliderWidth + mIndicatorOptions.sliderGap
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawCheckedSlider(canvas: Canvas) {
|
||||
mPaint.color = mIndicatorOptions.checkedSliderColor
|
||||
when (mIndicatorOptions.slideMode) {
|
||||
IndicatorSlideMode.SMOOTH -> drawSmoothSlider(canvas)
|
||||
IndicatorSlideMode.WORM -> drawWormSlider(canvas)
|
||||
IndicatorSlideMode.COLOR -> drawColorSlider(canvas)
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawColorSlider(canvas: Canvas) {
|
||||
val currentPosition = mIndicatorOptions.currentPosition
|
||||
val slideProgress = mIndicatorOptions.slideProgress
|
||||
val left = currentPosition * minWidth + currentPosition * mIndicatorOptions.sliderGap
|
||||
if (slideProgress < 0.99) {
|
||||
val evaluate = argbEvaluator?.evaluate(
|
||||
slideProgress, mIndicatorOptions.checkedSliderColor, mIndicatorOptions.normalSliderColor
|
||||
)
|
||||
mPaint.color = (evaluate as Int)
|
||||
mRectF.set(left, 0f, left + minWidth, mIndicatorOptions.sliderHeight)
|
||||
drawRoundRect(canvas, mIndicatorOptions.sliderHeight, mIndicatorOptions.sliderHeight,false)
|
||||
}
|
||||
|
||||
var nextSliderLeft = left + mIndicatorOptions.sliderGap + mIndicatorOptions.normalSliderWidth
|
||||
if (currentPosition == mIndicatorOptions.pageSize - 1) {
|
||||
nextSliderLeft = 0f
|
||||
}
|
||||
val evaluate = argbEvaluator?.evaluate(
|
||||
1 - slideProgress, mIndicatorOptions.checkedSliderColor, mIndicatorOptions.normalSliderColor
|
||||
)
|
||||
mPaint.color = evaluate as Int
|
||||
mRectF.set(nextSliderLeft, 0f, nextSliderLeft + minWidth, mIndicatorOptions.sliderHeight)
|
||||
drawRoundRect(canvas, mIndicatorOptions.sliderHeight, mIndicatorOptions.sliderHeight,false)
|
||||
}
|
||||
|
||||
private fun drawWormSlider(canvas: Canvas) {
|
||||
val sliderHeight = mIndicatorOptions.sliderHeight
|
||||
val slideProgress = mIndicatorOptions.slideProgress
|
||||
val currentPosition = mIndicatorOptions.currentPosition
|
||||
val distance = mIndicatorOptions.sliderGap + mIndicatorOptions.normalSliderWidth
|
||||
val startCoordinateX =
|
||||
IndicatorUtils.getCoordinateX(mIndicatorOptions, maxWidth, currentPosition)
|
||||
val left = startCoordinateX + (distance * (slideProgress - 0.5f) * 2.0f).coerceAtLeast(
|
||||
0f
|
||||
) - mIndicatorOptions.normalSliderWidth / 2
|
||||
val right = startCoordinateX + (distance * slideProgress * 2f).coerceAtMost(
|
||||
distance
|
||||
) + mIndicatorOptions.normalSliderWidth / 2
|
||||
mRectF.set(left, 0f, right, sliderHeight)
|
||||
drawRoundRect(canvas, sliderHeight, sliderHeight,false)
|
||||
}
|
||||
|
||||
private fun drawSmoothSlider(canvas: Canvas) {
|
||||
val currentPosition = mIndicatorOptions.currentPosition
|
||||
val indicatorGap = mIndicatorOptions.sliderGap
|
||||
val sliderHeight = mIndicatorOptions.sliderHeight
|
||||
val left =
|
||||
currentPosition * maxWidth + currentPosition * +indicatorGap + (maxWidth + indicatorGap) * mIndicatorOptions.slideProgress
|
||||
mRectF.set(left, 0f, left + maxWidth, sliderHeight)
|
||||
drawRoundRect(canvas, sliderHeight, sliderHeight,false)
|
||||
}
|
||||
|
||||
protected open fun drawRoundRect(
|
||||
canvas: Canvas,
|
||||
rx: Float,
|
||||
ry: Float,
|
||||
isWidth:Boolean
|
||||
) {
|
||||
drawDash(canvas)
|
||||
}
|
||||
|
||||
protected open fun drawDash(canvas: Canvas) {}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.drawer
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.LinearGradient
|
||||
import android.graphics.Shader
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.option.IndicatorOptions
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by zhpan on 2019/11/26.
|
||||
* Description:
|
||||
</pre> *
|
||||
*/
|
||||
class RoundRectDrawer internal constructor(val indicatorOptions: IndicatorOptions) : RectDrawer(
|
||||
indicatorOptions
|
||||
) {
|
||||
|
||||
override fun drawRoundRect(canvas: Canvas, rx: Float, ry: Float, isWidth: Boolean) {
|
||||
if(isWidth) {
|
||||
val linearGradient =
|
||||
LinearGradient(
|
||||
mRectF.left,
|
||||
(mRectF.bottom - mRectF.top) / 2,
|
||||
mRectF.right,
|
||||
(mRectF.bottom - mRectF.top) / 2,
|
||||
indicatorOptions.checkedSliderColor,
|
||||
indicatorOptions.checkedEndSliderColor,
|
||||
Shader.TileMode.CLAMP
|
||||
)
|
||||
mPaint.shader = linearGradient
|
||||
}else{
|
||||
mPaint.color = indicatorOptions.normalSliderColor
|
||||
mPaint.shader = null
|
||||
}
|
||||
canvas.drawRoundRect(mRectF, rx, ry, mPaint)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.enums
|
||||
|
||||
import android.widget.LinearLayout
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhangpan
|
||||
* @date 2021/1/21
|
||||
*/
|
||||
class IndicatorOrientation {
|
||||
companion object {
|
||||
const val INDICATOR_HORIZONTAL = LinearLayout.HORIZONTAL
|
||||
const val INDICATOR_VERTICAL = LinearLayout.VERTICAL
|
||||
const val INDICATOR_RTL = 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.enums
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by zhangpan on 2019-10-18.
|
||||
* Description:
|
||||
</pre> *
|
||||
*/
|
||||
interface IndicatorSlideMode {
|
||||
companion object {
|
||||
const val NORMAL = 0
|
||||
const val SMOOTH = 2
|
||||
const val WORM = 3
|
||||
const val SCALE = 4
|
||||
const val COLOR = 5
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.enums
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by zhangpan on 2019-10-18.
|
||||
* Description:
|
||||
</pre> *
|
||||
*/
|
||||
interface IndicatorStyle {
|
||||
companion object {
|
||||
const val CIRCLE = 0
|
||||
const val DASH = 1 shl 1
|
||||
const val ROUND_RECT = 1 shl 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.option;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.mogo.och.taxi.passenger.R;
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.utils.IndicatorUtils;
|
||||
|
||||
public class AttrsController {
|
||||
|
||||
public static void initAttrs(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||
IndicatorOptions indicatorOptions) {
|
||||
if (attrs != null) {
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.IndicatorView);
|
||||
int indicatorSlideMode = typedArray.getInt(R.styleable.IndicatorView_vpi_slide_mode, 0);
|
||||
int indicatorStyle = typedArray.getInt(R.styleable.IndicatorView_vpi_style, 0);
|
||||
int checkedColor = typedArray.getColor(R.styleable.IndicatorView_vpi_slider_checked_color,
|
||||
Color.parseColor("#6C6D72"));
|
||||
int normalColor = typedArray.getColor(R.styleable.IndicatorView_vpi_slider_normal_color,
|
||||
Color.parseColor("#8C18171C"));
|
||||
int orientation = typedArray.getInt(R.styleable.IndicatorView_vpi_orientation, 0);
|
||||
float radius = typedArray.getDimension(R.styleable.IndicatorView_vpi_slider_radius,
|
||||
IndicatorUtils.dp2px(8));
|
||||
indicatorOptions.setCheckedColor(checkedColor);
|
||||
indicatorOptions.setNormalSliderColor(normalColor);
|
||||
indicatorOptions.setOrientation(orientation);
|
||||
indicatorOptions.setIndicatorStyle(indicatorStyle);
|
||||
indicatorOptions.setSlideMode(indicatorSlideMode);
|
||||
indicatorOptions.setSliderWidth(radius * 2, radius * 2);
|
||||
typedArray.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.option
|
||||
|
||||
import android.graphics.Color
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.annotation.AIndicatorOrientation
|
||||
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.annotation.AIndicatorSlideMode
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.annotation.AIndicatorStyle
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorOrientation
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.enums.IndicatorSlideMode
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.utils.IndicatorUtils
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by zhpan on 2019/11/20.
|
||||
* Description:Indicator的配置参数
|
||||
</pre> *
|
||||
*/
|
||||
class IndicatorOptions {
|
||||
|
||||
@AIndicatorOrientation
|
||||
var orientation = IndicatorOrientation.INDICATOR_HORIZONTAL
|
||||
|
||||
@AIndicatorStyle
|
||||
var indicatorStyle: Int = 0
|
||||
|
||||
/**
|
||||
* Indicator滑动模式,目前仅支持两种
|
||||
*
|
||||
* @see IndicatorSlideMode.NORMAL
|
||||
*
|
||||
* @see IndicatorSlideMode.SMOOTH
|
||||
*/
|
||||
@AIndicatorSlideMode
|
||||
var slideMode: Int = 0
|
||||
|
||||
/**
|
||||
* 页面size
|
||||
*/
|
||||
var pageSize: Int = 0
|
||||
|
||||
/**
|
||||
* 未选中时Indicator颜色
|
||||
*/
|
||||
var normalSliderColor: Int = 0
|
||||
|
||||
/**
|
||||
* 选中时Indicator颜色
|
||||
*/
|
||||
var checkedSliderColor: Int = 0
|
||||
/**
|
||||
* 选中时IndicatorEnd颜色
|
||||
*/
|
||||
var checkedEndSliderColor: Int = 0
|
||||
|
||||
/**
|
||||
* Indicator间距
|
||||
*/
|
||||
var sliderGap: Float = 0f
|
||||
|
||||
var sliderHeight: Float = 0f
|
||||
get() = if (field > 0) field else normalSliderWidth / 2
|
||||
|
||||
var normalSliderWidth: Float = 0f
|
||||
|
||||
var checkedSliderWidth: Float = 0f
|
||||
|
||||
/**
|
||||
* 指示器当前位置
|
||||
*/
|
||||
var currentPosition: Int = 0
|
||||
|
||||
/**
|
||||
* 从一个点滑动到另一个点的进度
|
||||
*/
|
||||
var slideProgress: Float = 0f
|
||||
|
||||
var showIndicatorOneItem: Boolean = false
|
||||
|
||||
init {
|
||||
normalSliderWidth = IndicatorUtils.dp2px(8f)
|
||||
.toFloat()
|
||||
checkedSliderWidth = normalSliderWidth
|
||||
sliderGap = normalSliderWidth
|
||||
normalSliderColor = Color.parseColor("#8C18171C")
|
||||
checkedSliderColor = Color.parseColor("#8C6C6D72")
|
||||
slideMode = IndicatorSlideMode.NORMAL
|
||||
}
|
||||
|
||||
fun setCheckedColor(checkedColor: Int) {
|
||||
this.checkedSliderColor = checkedColor
|
||||
}
|
||||
|
||||
fun setSliderWidth(
|
||||
normalIndicatorWidth: Float,
|
||||
checkedIndicatorWidth: Float
|
||||
) {
|
||||
this.normalSliderWidth = normalIndicatorWidth
|
||||
this.checkedSliderWidth = checkedIndicatorWidth
|
||||
}
|
||||
|
||||
fun setSliderWidth(sliderWidth: Float) {
|
||||
setSliderWidth(sliderWidth, sliderWidth)
|
||||
}
|
||||
|
||||
fun setSliderColor(
|
||||
normalColor: Int,
|
||||
checkedColor: Int,
|
||||
selectedEndColor: Int
|
||||
) {
|
||||
this.normalSliderColor = normalColor
|
||||
this.checkedSliderColor = checkedColor
|
||||
this.checkedEndSliderColor = selectedEndColor
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.mogo.och.taxi.passenger.widget.indicator.utils
|
||||
|
||||
import android.content.res.Resources
|
||||
|
||||
import com.mogo.och.taxi.passenger.widget.indicator.option.IndicatorOptions
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by zhangpan on 2020-01-20.
|
||||
* Description:
|
||||
</pre> *
|
||||
*/
|
||||
object IndicatorUtils {
|
||||
|
||||
@JvmStatic
|
||||
fun dp2px(dpValue: Float): Int {
|
||||
return (0.5f + dpValue * Resources.getSystem().displayMetrics.density).toInt()
|
||||
}
|
||||
|
||||
fun getCoordinateX(
|
||||
indicatorOptions: IndicatorOptions,
|
||||
maxDiameter: Float,
|
||||
index: Int
|
||||
): Float {
|
||||
val normalIndicatorWidth = indicatorOptions.normalSliderWidth
|
||||
return maxDiameter / 2 + (normalIndicatorWidth + indicatorOptions.sliderGap) * index
|
||||
}
|
||||
|
||||
fun getCoordinateY(maxDiameter: Float): Float {
|
||||
return maxDiameter / 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="1000" android:fromAlpha="0" android:toAlpha="1" />
|
||||
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="1000" android:fromAlpha="1" android:toAlpha="0" />
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android" android:startOffset="500" >
|
||||
<translate
|
||||
android:duration="142"
|
||||
android:fromXDelta="-110%"
|
||||
android:toXDelta="0"/>
|
||||
<alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="142" />
|
||||
</set>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android" android:startOffset="500" >
|
||||
<translate
|
||||
android:duration="142"
|
||||
android:fromXDelta="0"
|
||||
android:toXDelta="-110%"/>
|
||||
<alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="142" />
|
||||
</set>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<objectAnimator android:propertyName="alpha" android:duration="1000" android:valueFrom="1.0" android:valueTo="0.0"/>
|
||||
</set>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<objectAnimator android:propertyName="alpha" android:duration="1000" android:valueFrom="0.0" android:valueTo="1.0"/>
|
||||
</set>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/taxi_autopilot_text_color_normal"/>
|
||||
</selector>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@android:id/background">
|
||||
<shape>
|
||||
<corners android:radius="5dp"/>
|
||||
<solid android:color="@android:color/transparent" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:id="@android:id/secondaryProgress">
|
||||
<clip>
|
||||
<shape>
|
||||
<corners android:radius="5dp"/>
|
||||
<solid android:color="@android:color/transparent" />
|
||||
</shape>
|
||||
</clip>
|
||||
</item>
|
||||
<item android:id="@android:id/progress">
|
||||
<scale android:scaleWidth="100%">
|
||||
<shape android:shape="rectangle" >
|
||||
<corners android:radius="7dp"/>
|
||||
<gradient android:startColor="@android:color/transparent" android:endColor="#54D7FF" android:angle="0"/>
|
||||
</shape>
|
||||
</scale>
|
||||
</item>
|
||||
</layer-list>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@android:id/background">
|
||||
<shape>
|
||||
<corners android:radius="5dp"/>
|
||||
<solid android:color="#99D8D8D8" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:id="@android:id/secondaryProgress">
|
||||
<clip>
|
||||
<shape>
|
||||
<corners android:radius="5dp"/>
|
||||
<solid android:color="#66FFFFFF" />
|
||||
</shape>
|
||||
</clip>
|
||||
</item>
|
||||
<item android:id="@android:id/progress">
|
||||
<scale android:scaleWidth="100%">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="5dp"/>
|
||||
<gradient android:startColor="#303CFF" android:centerColor="#216CFF" android:endColor="#25C1F9" android:angle="0"/>
|
||||
</shape>
|
||||
</scale>
|
||||
</item>
|
||||
</layer-list>
|
||||
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 897 KiB |
|
After Width: | Height: | Size: 450 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 851 B |
|
After Width: | Height: | Size: 339 KiB |
|
After Width: | Height: | Size: 101 KiB |