增加导航模块

This commit is contained in:
zhangyuanzhen
2020-01-02 16:56:16 +08:00
parent 5762b771e3
commit c3df548d04
34 changed files with 3360 additions and 13 deletions

View File

@@ -36,16 +36,16 @@ dependencies {
implementation rootProject.ext.dependencies.arouter
annotationProcessor rootProject.ext.dependencies.aroutercompiler
if (Boolean.valueOf(RELEASE)) {
implementation rootProject.ext.dependencies.mogomap
implementation rootProject.ext.dependencies.mogomapapi
implementation rootProject.ext.dependencies.mogoutils
api rootProject.ext.dependencies.mogomap
api rootProject.ext.dependencies.mogomapapi
api rootProject.ext.dependencies.mogoutils
api rootProject.ext.dependencies.mogocommons
api rootProject.ext.dependencies.mogoserviceapi
implementation rootProject.ext.dependencies.modulecommon
} else {
implementation project(":libraries:mogo-map")
implementation project(":libraries:mogo-map-api")
implementation project(":foudations:mogo-utils")
api project(":libraries:mogo-map")
api project(":libraries:mogo-map-api")
api project(":foudations:mogo-utils")
api project(":foudations:mogo-commons")
api project(':services:mogo-service-api')
implementation project(':modules:mogo-module-common')

View File

@@ -20,6 +20,11 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
@@ -36,7 +41,9 @@ dependencies {
implementation rootProject.ext.dependencies.androidxconstraintlayout
implementation rootProject.ext.dependencies.arouter
implementation rootProject.ext.dependencies.room
implementation rootProject.ext.dependencies.roomAnnotationProcessor
implementation rootProject.ext.dependencies.androidxrecyclerview
annotationProcessor rootProject.ext.dependencies.roomAnnotationProcessor
implementation rootProject.ext.dependencies.roomRxjava
annotationProcessor rootProject.ext.dependencies.aroutercompiler
implementation 'org.jetbrains:annotations-java5:15.0'
}

View File

@@ -1,4 +1,4 @@
package com.mogo.module.navi.database.bean;
package com.mogo.module.navi.bean;
import androidx.room.Entity;
import androidx.room.PrimaryKey;

View File

@@ -0,0 +1,45 @@
package com.mogo.module.navi.constants;
import android.net.Uri;
/**
* @author zyz
* @since 2019-10-02
* <p>
* 描述
*/
public class DataConstants {
public static final int DB_VERSION = 1;
public static final String DB_NAME = "zhidao.amap.db";
public static final String T_SEARCH_POI = "t_search_poi";
public static final int TYPE_POI = 0; // 普通搜索的类型
public static final int TYPE_HOME_ADDRESS = 1; // 家地址
public static final int TYPE_COMPANY_ADDRESS = 2; // 公司地址
public static final String POI_ID_HOME = "common_address_home";
public static final String POI_ID_COMPANY = "common_address_company";
public static final String CP_AUTHORITY = "com.zhidao.amap";
// 家的地址
public static final Uri CONTENT_HOME_ADDRESS_URI = Uri.parse( "content://com.zhidao.amap/homeAddress" );
public static final int HOME_ADDRESS_CODE = TYPE_HOME_ADDRESS;
public static final String HOME_ADDRESS_PATH = "homeAddress";
public static final String HOME_ADDRESS = "homeAddress";
public static final String HOME_ADDRESS_NAME = "homeAddressName";
public static final String HOME_ADDRESS_LATITUDE = "homeAddressLatitude";
public static final String HOME_ADDRESS_LONGITUDE = "homeAddressLongitude";
// 公司地址
public static final Uri CONTENT_COMPANY_ADDRESS_URI = Uri.parse( "content://com.zhidao.amap/companyAddress" );
public static final int COMPANY_ADDRESS_CODE = TYPE_COMPANY_ADDRESS;
public static final String COMPANY_ADDRESS_PATH = "companyAddress";
public static final String COMPANY_ADDRESS = "companyAddress";
public static final String COMPANY_ADDRESS_NAME = "companyAddressName";
public static final String COMPANY_ADDRESS_LATITUDE = "companyAddressLatitude";
public static final String COMPANY_ADDRESS_LONGITUDE = "companyAddressLongitude";
}

View File

@@ -1,10 +1,10 @@
package com.mogo.module.navi.database.dao;
package com.mogo.module.navi.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import com.mogo.module.navi.database.bean.POIInfo;
import com.mogo.module.navi.bean.POIInfo;
import io.reactivex.Flowable;
import java.util.List;

View File

@@ -0,0 +1,19 @@
package com.mogo.module.navi.lib;
import com.amap.api.maps.AMap;
import com.amap.api.navi.AMapNaviListener;
import com.amap.api.navi.AMapNaviViewListener;
import com.mogo.module.navi.database.wrapper.NaviInfoWrapper;
/**
* @author congtaowang
* @since 2019-09-27
* <p>
* 描述
*/
public interface AMapNaviStatusChangedListener extends AMapNaviListener,
AMapNaviViewListener,
AMap.OnCameraChangeListener {
void onNaviInfoUpdate(NaviInfoWrapper wrapper);
}

View File

@@ -0,0 +1,281 @@
package com.mogo.module.navi.lib;
import com.amap.api.maps.model.CameraPosition;
import com.amap.api.navi.model.AMapCalcRouteResult;
import com.amap.api.navi.model.AMapLaneInfo;
import com.amap.api.navi.model.AMapModelCross;
import com.amap.api.navi.model.AMapNaviCameraInfo;
import com.amap.api.navi.model.AMapNaviCross;
import com.amap.api.navi.model.AMapNaviInfo;
import com.amap.api.navi.model.AMapNaviLocation;
import com.amap.api.navi.model.AMapNaviRouteNotifyData;
import com.amap.api.navi.model.AMapNaviTrafficFacilityInfo;
import com.amap.api.navi.model.AMapServiceAreaInfo;
import com.amap.api.navi.model.AimLessModeCongestionInfo;
import com.amap.api.navi.model.AimLessModeStat;
import com.amap.api.navi.model.NaviInfo;
import com.autonavi.tbt.TrafficFacilityInfo;
import com.mogo.module.navi.database.wrapper.NaviInfoWrapper;
/**
* @author congtaowang
* @since 2019-10-01
* <p>
*/
public abstract class AMapNaviStatusChangedListenerDefault implements AMapNaviStatusChangedListener {
@Override
public void onNaviInfoUpdate( NaviInfoWrapper wrapper ) {
}
@Override
public void onCameraChange( CameraPosition cameraPosition ) {
}
@Override
public void onCameraChangeFinish( CameraPosition cameraPosition ) {
}
@Override
public void onInitNaviFailure() {
}
@Override
public void onInitNaviSuccess() {
}
@Override
public void onStartNavi( int i ) {
}
@Override
public void onTrafficStatusUpdate() {
}
@Override
public void onLocationChange( AMapNaviLocation aMapNaviLocation ) {
}
@Override
public void onGetNavigationText( int i, String s ) {
}
@Override
public void onGetNavigationText( String s ) {
}
@Override
public void onEndEmulatorNavi() {
}
@Override
public void onArriveDestination() {
}
@Override
public void onCalculateRouteFailure( int i ) {
}
@Override
public void onReCalculateRouteForYaw() {
}
@Override
public void onReCalculateRouteForTrafficJam() {
}
@Override
public void onArrivedWayPoint( int i ) {
}
@Override
public void onGpsOpenStatus( boolean b ) {
}
@Override
public void onNaviInfoUpdate( NaviInfo naviInfo ) {
}
@Override
public void onNaviInfoUpdated( AMapNaviInfo aMapNaviInfo ) {
}
@Override
public void updateCameraInfo( AMapNaviCameraInfo[] aMapNaviCameraInfos ) {
}
@Override
public void updateIntervalCameraInfo( AMapNaviCameraInfo aMapNaviCameraInfo, AMapNaviCameraInfo aMapNaviCameraInfo1, int i ) {
}
@Override
public void onServiceAreaUpdate( AMapServiceAreaInfo[] aMapServiceAreaInfos ) {
}
@Override
public void showCross( AMapNaviCross aMapNaviCross ) {
}
@Override
public void hideCross() {
}
@Override
public void showModeCross( AMapModelCross aMapModelCross ) {
}
@Override
public void hideModeCross() {
}
@Override
public void showLaneInfo( AMapLaneInfo[] aMapLaneInfos, byte[] bytes, byte[] bytes1 ) {
}
@Override
public void showLaneInfo( AMapLaneInfo aMapLaneInfo ) {
}
@Override
public void hideLaneInfo() {
}
@Override
public void onCalculateRouteSuccess( int[] ints ) {
}
@Override
public void notifyParallelRoad( int i ) {
}
@Override
public void OnUpdateTrafficFacility( AMapNaviTrafficFacilityInfo aMapNaviTrafficFacilityInfo ) {
}
@Override
public void OnUpdateTrafficFacility( AMapNaviTrafficFacilityInfo[] aMapNaviTrafficFacilityInfos ) {
}
@Override
public void OnUpdateTrafficFacility( TrafficFacilityInfo trafficFacilityInfo ) {
}
@Override
public void updateAimlessModeStatistics( AimLessModeStat aimLessModeStat ) {
}
@Override
public void updateAimlessModeCongestionInfo( AimLessModeCongestionInfo aimLessModeCongestionInfo ) {
}
@Override
public void onPlayRing( int i ) {
}
@Override
public void onCalculateRouteSuccess( AMapCalcRouteResult aMapCalcRouteResult ) {
}
@Override
public void onCalculateRouteFailure( AMapCalcRouteResult aMapCalcRouteResult ) {
}
@Override
public void onNaviRouteNotify( AMapNaviRouteNotifyData aMapNaviRouteNotifyData ) {
}
@Override
public void onNaviSetting() {
}
@Override
public void onNaviCancel() {
}
@Override
public boolean onNaviBackClick() {
return false;
}
@Override
public void onNaviMapMode( int i ) {
}
@Override
public void onNaviTurnClick() {
}
@Override
public void onNextRoadClick() {
}
@Override
public void onScanViewButtonClick() {
}
@Override
public void onLockMap( boolean b ) {
}
@Override
public void onNaviViewLoaded() {
}
@Override
public void onMapTypeChanged( int i ) {
}
@Override
public void onNaviViewShowMode( int i ) {
}
}

View File

@@ -0,0 +1,40 @@
package com.mogo.module.navi.ui.adapter;
import android.content.Context;
import android.view.View;
import com.amap.api.services.help.Tip;
import com.mogo.module.navi.R;
import com.mogo.module.navi.ui.adapter.base.RecycleBaseAdapter;
import com.mogo.module.navi.ui.adapter.base.RecycleViewHolder;
import java.util.List;
/**
* @author zyz
* 2019-08-13.
*/
public class SearchPoiAdapter extends RecycleBaseAdapter<Tip> {
/**
* @param context
* @param list
*/
public SearchPoiAdapter(Context context, List<Tip> list) {
super(context, list, R.layout.item_search_poi);
}
private View.OnClickListener onClickListener;
@Override
public void onBindViewHolder(RecycleViewHolder holder, Tip tip) {
holder.setText(R.id.tv_position, tip.getName());
holder.setText(R.id.tv_position_des, tip.getAddress());
holder.itemView.setTag(R.id.tag_position, tip);
holder.itemView.setOnClickListener(onClickListener);
}
public void setOnClickListener(View.OnClickListener onClickListener) {
this.onClickListener = onClickListener;
}
}

View File

@@ -0,0 +1,112 @@
package com.mogo.module.navi.ui.adapter.base;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
/**
* <p>
* Title: adapter
* </p>
* <p>
* Description:
* </p>
* <p>
* Copyright: Copyright (c) 2015
* </p>
* <p>
* </p>
*/
public abstract class RecycleBaseAdapter<T> extends
RecyclerView.Adapter<RecycleViewHolder>
{
protected Context context;
protected List<T> list;
private int resourceID;
private Toast toast;
/**
* @param context
*/
public RecycleBaseAdapter(Context context, List<T> list, int resourceID)
{
super();
this.context = context;
this.list = list;
this.resourceID = resourceID;
}
@Override
public RecycleViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType)
{
View v = LayoutInflater.from(context).inflate(resourceID, viewGroup,
false);
RecycleViewHolder holder = RecycleViewHolder
.get(v);
initHolder(holder);
return holder;
}
@Override
public void onBindViewHolder(
RecycleViewHolder viewHolder, int position)
{
onBindViewHolder(viewHolder, list.get(position % list.size()));
}
@Override
public int getItemCount()
{
return list == null ? 0 : list.size();
}
public abstract void onBindViewHolder(
RecycleViewHolder holder, T t);
public void initHolder(RecycleViewHolder holder)
{
}
public void setDatas(List<T> list)
{
setDatas(list, false);
}
public void setDatas(List<T> list, boolean add)
{
if (add)
{
this.list.addAll(list);
}
else
{
this.list = list;
}
notifyDataSetChanged();
}
public void messageShow(String mes)
{
if (toast==null){
toast= Toast.makeText(context,mes, Toast.LENGTH_LONG);
}
else{
toast.setText(mes);
}
toast.show();
}
public T getItem(int position){
if (list==null||list.size()==0){
return null;
}
return list.get(position);
}
}

View File

@@ -0,0 +1,138 @@
package com.mogo.module.navi.ui.adapter.base;
import android.graphics.Bitmap;
import android.text.SpannableString;
import android.util.SparseArray;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
public class RecycleViewHolder extends RecyclerView.ViewHolder {
private SparseArray<View> mViews;
private View mConvertView;
public RecycleViewHolder(View itemView) {
super(itemView);
this.mConvertView = itemView;
mViews = new SparseArray<View>();
// TODO Auto-generated constructor stub
}
public static RecycleViewHolder get(View itemView) {
return new RecycleViewHolder(itemView);
}
public View getConvertView() {
return mConvertView;
}
public <T extends View> T getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
/**
*
* @param viewId
* @param text
* @return
*/
public RecycleViewHolder setText(int viewId, String text) {
TextView tv = getView(viewId);
if (tv==null)return this;
tv.setText(text);
return this;
}
/**
*
* @param viewId
* @param text
* @return
*/
public RecycleViewHolder setText(int viewId, SpannableString text) {
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
/**
*
* @param viewId
* @param resId
* @return
*/
public RecycleViewHolder setImageResource(int viewId, int resId) {
ImageView view = getView(viewId);
view.setImageResource(resId);
return this;
}
/**
*
* @param viewId
* @return
*/
public RecycleViewHolder setImageBitmap(int viewId, Bitmap bitmap) {
ImageView view = getView(viewId);
view.setImageBitmap(bitmap);
return this;
}
/**
*
* @param viewId
* @param resId
* @return
*/
// public ViewHolder setImageURI(int viewId, String url) {
// ImageView view = getView(viewId);
// // ImageLoader.getInstance.loadImg(view,url);
// return this;
// }
/**
*
* @param viewId
* @param resId
* @return
*/
public RecycleViewHolder setBackgroundImage(int viewId, int resId) {
View view = getView(viewId);
view.setBackgroundResource(resId);
return this;
}
/**
*
* @param viewId
* @param resId
* @return
*/
public RecycleViewHolder setTextColor(int viewId, int resId) {
TextView view = getView(viewId);
view.setTextColor(resId);
return this;
}
/**
*
* @param viewId
* @return
*/
public RecycleViewHolder setOnClickListener(int viewId,
OnClickListener listener) {
getView(viewId).setOnClickListener(listener);
return this;
}
}

View File

@@ -0,0 +1,50 @@
package com.mogo.module.navi.ui.base;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* @author congtaowang
* @since 2019-10-04
* <p>
* 描述
*/
public abstract class AMapBaseFragment extends com.mogo.module.navi.database.ui.base.BaseFragment {
private static final String TAG = "amap.AMapBaseFragment";
@Override
public void onActivityCreated( @Nullable Bundle savedInstanceState ) {
super.onActivityCreated( savedInstanceState );
initViews();
}
protected abstract void initViews();
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void onSaveInstanceState( @NonNull Bundle outState ) {
super.onSaveInstanceState( outState );
}
@Override
public void onLowMemory() {
super.onLowMemory();
}
}

View File

@@ -0,0 +1,48 @@
package com.mogo.module.navi.ui.base;
import com.amap.api.maps.AMap;
import com.amap.api.navi.AMapNavi;
import com.amap.api.navi.AMapNaviView;
import com.amap.api.navi.model.AMapNaviPath;
import com.mogo.module.navi.lib.AMapNaviStatusChangedListener;
/**
* @author congtaowang
* @since 2019-10-01
* <p>
* 描述
*/
public interface AMapServiceVisitor {
AMapNaviView getNaviView();
AMap getMap();
AMapNavi getAMapNavi();
void registerNaviListener(AMapNaviStatusChangedListener listener);
/**
* 是否正在导航
*/
boolean isNaving();
void stopNavi();
void setHost(String host);
/**
* 将导航路线发送给launcher
*
* @param path
*/
void pushNaviPathToLauncher(AMapNaviPath path);
/**
* 将导航路线发送给launcher
*
* @param path 导航路线
* @param directly 是否直接发送
*/
void pushNaviPathToLauncher(AMapNaviPath path, boolean directly);
}

View File

@@ -0,0 +1,65 @@
package com.mogo.module.navi.ui.base;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.mogo.utils.SoftKeyBoardJobber;
import com.mogo.utils.statusbar.Eyes;
/**
* @author congtaowang
* @since 2019-10-02
* <p>
* 地图 activity 基类
*/
public class BaseActivity extends AppCompatActivity {
//@Override
//public Context getContext() {
// return this;
//}
@Override
protected void onCreate( @Nullable Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
Eyes.setStatusBarStyle( this, false, Color.parseColor( "#66000000" ), true );
getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN );
hideSystemUI();
}
protected void hideSystemUI() {
//隐藏虚拟按键
if ( Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19 ) { // lower api
View v = this.getWindow().getDecorView();
v.setSystemUiVisibility( View.GONE );
} else if ( Build.VERSION.SDK_INT >= 19 ) {
//for new api versions.
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility( uiOptions );
}
}
protected boolean enableDispatchTouchEventToDismissSoftKeyBoard() {
return true;
}
@Override
public boolean dispatchTouchEvent( MotionEvent ev ) {
if ( ev.getAction() == MotionEvent.ACTION_DOWN && enableDispatchTouchEventToDismissSoftKeyBoard() ) {
SoftKeyBoardJobber.hideIfNecessary( this, ev );
return super.dispatchTouchEvent( ev );
}
// 必不可少否则所有的组件都不会有TouchEvent了
if ( getWindow().superDispatchTouchEvent( ev ) ) {
return true;
}
return onTouchEvent( ev );
}
}

View File

@@ -0,0 +1,73 @@
package com.mogo.module.navi.ui.base;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.mogo.utils.NetworkUtils;
/**
* @author congtaowang
* @since 2019-10-03
* <p>
* 描述
*/
public abstract class BaseFragment extends Fragment {
protected Context mContext;
private View mRootView;
@Override
public void onAttach( Context context ) {
super.onAttach( context );
mContext = context;
}
@Nullable
@Override
public View onCreateView( @NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable
Bundle savedInstanceState ) {
mRootView = inflater.inflate( getLayoutId(), container, false );
return mRootView;
}
protected abstract int getLayoutId();
protected < T extends View> T findViewById( @IdRes int id ) {
if ( mRootView != null ) {
return mRootView.findViewById( id );
}
if ( getView() != null ) {
return getView().findViewById( id );
}
return null;
}
protected void overridePendingTransition( int enter, int exit ) {
if ( getActivity() != null ) {
getActivity().overridePendingTransition( enter, exit );
}
}
protected boolean checkNetwork() {
return NetworkUtils.isConnected( mContext );
}
protected void shortToast( CharSequence msg ) {
if ( mContext != null && msg != null ) {
Toast.makeText( mContext, msg, Toast.LENGTH_SHORT ).show();
}
}
protected void longToast( CharSequence msg ) {
if ( mContext != null && msg != null ) {
Toast.makeText( mContext, msg, Toast.LENGTH_LONG ).show();
}
}
}

View File

@@ -0,0 +1,53 @@
package com.mogo.module.navi.ui.base;
import androidx.fragment.app.FragmentManager;
/**
* @author congtaowang
* @since 2019-10-04
* <p>
* 描述
*/
public interface UiController {
/**
* 显示地图模式
*/
void onMapMode();
/**
* 显示导航模式
*/
void onNaviMode();
/**
* 搜索地址
*
* @param searchType {@link com.zhidao.amap.splitunit.ui.search.SearchConstants}
*/
void onSearchMode(int searchType);
/**
* 规划路径
*/
void onCalculateMode();
/**
* 设置
*/
void onSettingsMode();
/**
* 直接退出应用
*/
void finishDirectly();
FragmentManager _getSupportFragmentManager();
AMapServiceVisitor getAMapServiceVisitor();
//CommonAddressModel getCommonAddressModel();
void popBackStackImmediate();
}

View File

@@ -0,0 +1,54 @@
package com.mogo.module.navi.ui.search;
import com.mogo.module.navi.constants.DataConstants;
/**
* @author congtaowang
* @since 2019-12-08
* <p>
* 搜索页面常量字段
*/
public class SearchConstants {
/**
* 搜索页面类型
*/
public static final String KEY_SEARCH_TYPE = "type";
/**
* 普通搜索:首页搜索按钮点击
*/
public static final int SEARCH_TYPE_COMMON = DataConstants.TYPE_POI;
/**
* 设置家:多种搜索方式:普通搜索、我的位置、地图选点
*/
public static final int SEARCH_TYPE_MULTI_HOME = DataConstants.TYPE_HOME_ADDRESS;
/**
* 设置公司:多种搜索方式:普通搜索、我的位置、地图选点
*/
public static final int SEARCH_TYPE_MULTI_COMPANY = DataConstants.TYPE_COMPANY_ADDRESS;
/**
* 搜索页面当前状态
*/
public static final int UI_MODE_COMMON = 1;
/**
* 搜索页面当前状态:常用地址搜索模式
*/
public static final int UI_MODE_MULTI = 2;
/**
* 搜索页面当前状态:常用地址搜索模式
*/
public static final int UI_MODE_MULTI_SEARCH = 5;
/**
* 搜索页面当前状态:我的位置模式
*/
public static final int UI_MODE_MULTI_MY_LOCATION = 3;
/**
* 搜索页面当前状态:选点模式
*/
public static final int UI_MODE_MULTI_CHOICE_POINT = 4;
}

View File

@@ -0,0 +1,566 @@
package com.mogo.module.navi.ui.search;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.view.animation.Interpolator;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.amap.api.location.AMapLocation;
import com.amap.api.location.AMapLocationListener;
import com.amap.api.maps.AMap;
import com.amap.api.maps.AMapUtils;
import com.amap.api.maps.model.BitmapDescriptorFactory;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.Marker;
import com.amap.api.maps.model.MarkerOptions;
import com.amap.api.maps.model.animation.Animation;
import com.amap.api.maps.model.animation.TranslateAnimation;
import com.amap.api.services.geocoder.RegeocodeAddress;
import com.amap.api.services.help.Tip;
import com.mogo.module.navi.R;
import com.mogo.module.navi.ui.adapter.SearchPoiAdapter;
import com.mogo.module.navi.ui.base.BaseFragment;
import com.mogo.module.navi.ui.base.UiController;
import com.mogo.utils.WindowUtils;
import io.reactivex.disposables.Disposable;
import java.util.List;
/**
* 搜索页面
* <p>
* 普通搜索:从首页点击搜索按钮进入:包含:仅输入搜索(列表不包含设置按钮)
* {@link SearchConstants#SEARCH_TYPE_COMMON}
* <p>
* 地址设置搜索:设置家、公司、其他的地址:包含当前位置、选点、搜索列表(列表包含设置按钮)、普通页面
* {@link SearchConstants#SEARCH_TYPE_MULTI_COMPANY}
* {@link SearchConstants#SEARCH_TYPE_MULTI_HOME}
*/
@Route( path = "/amap/search" )
public class SearchFragment extends BaseFragment implements SearchView,
AMapLocationListener {
public static final String TAG = "search";
public int mSearchType;
private SearchPresenter mSearchPresenter;
private View mClose;
private EditText mSearchBox;
private RecyclerView mSearchResult;
private SearchPoiAdapter mPoiAdapter;
private View mMyLocation;
private View mChoicePoint;
private View mCurrentLocation;
/**
* 设置常用地址(我的位置、选点)时的设置按钮
*/
private TextView mActionButton;
private UiController mUiController;
private Bitmap mSearchBoxRightDrawableBitmap = null;
/**
* 地址设置是否完成
*/
private boolean mActionSuccess = false;
/**
* 当前UI的样式具体是哪个操作
*/
private int mUiMode = SearchConstants.UI_MODE_COMMON;
/**
* 地图选点的marker
*/
private Marker mChoicePointMaker = null;
private AMapLocation mLastAMapLocation;
@Override
public void onAttach( Context context ) {
super.onAttach( context );
if ( context instanceof UiController) {
mUiController = ( ( UiController ) context );
}
}
@Override
protected int getLayoutId() {
return R.layout.fragment_search;
}
@Override
public void onActivityCreated( @Nullable Bundle savedInstanceState ) {
super.onActivityCreated( savedInstanceState );
initViews();
getLifecycle().addObserver( mSearchPresenter = new com.mogo.module.navi.database.ui.search.SearchPresenter( this ) );
}
private void initViews() {
mClose = findViewById( R.id.amap_search_close );
mClose.setOnClickListener( view -> {
exitSearch();
} );
mSearchBox = findViewById( R.id.amap_search_search_box );
mSearchResult = findViewById( R.id.amap_search_poi_result );
mSearchResult.setLayoutManager( new LinearLayoutManager( mContext, LinearLayoutManager.VERTICAL, false ) );
mMyLocation = findViewById( R.id.amap_search_poi_my_location );
mMyLocation.setOnClickListener( view -> {
if ( !checkNetwork() ) {
shortToast( "网络未连接,请检查网络" );
return;
}
multiSearchMyLocationUI();
AMapLocationManager.getInstance( mContext ).addLocationListener( this );
AMapLocationManager.getInstance( mContext ).start();
} );
mChoicePoint = findViewById( R.id.amap_search_poi_choice_point );
mChoicePoint.setOnClickListener( view -> {
multiSearchChoicePointUI();
} );
mCurrentLocation = findViewById( R.id.amap_search_current_location );
mCurrentLocation.setOnClickListener( view -> {
if ( !checkNetwork() ) {
shortToast( "网络未连接,请检查网络" );
return;
}
AMapLocationManager.getInstance( mContext ).addLocationListener( this );
AMapLocationManager.getInstance( mContext ).start();
} );
mActionButton = findViewById( R.id.amap_search_action_setting );
mActionButton.setOnClickListener( view -> {
if ( mUiMode == SearchConstants.UI_MODE_MULTI_MY_LOCATION ) {
saveCurrentLocationAsCommonAddress();
} else if ( mUiMode == SearchConstants.UI_MODE_MULTI_CHOICE_POINT ) {
saveRegeoAddressAsCommonAddress();
}
} );
switch ( mSearchType ) {
case SearchConstants.SEARCH_TYPE_COMMON:
commonSearchUI();
break;
case SearchConstants.SEARCH_TYPE_MULTI_HOME:
case SearchConstants.SEARCH_TYPE_MULTI_COMPANY:
multiSearchUI();
break;
}
}
/**
* 普通搜索UI不显示我的位置、地图选点、我的定位
*/
private void commonSearchUI() {
mUiMode = SearchConstants.UI_MODE_COMMON;
mSearchBox.setEnabled( true );
mMyLocation.setVisibility( View.GONE );
mChoicePoint.setVisibility( View.GONE );
mCurrentLocation.setVisibility( View.GONE );
mActionButton.setVisibility( View.INVISIBLE );
mSearchResult.setVisibility( View.GONE );
if ( mSearchBox.getCompoundDrawables()[2] == null ) {
Drawable rightDrawable = null;
if ( mSearchBoxRightDrawableBitmap == null ) {
mSearchBoxRightDrawableBitmap = BitmapFactory.decodeResource( getResources(), R.drawable.ic_search_unshadow );
}
rightDrawable = new BitmapDrawable( getResources(), mSearchBoxRightDrawableBitmap );
mSearchBox.setCompoundDrawables( null, null, rightDrawable, null );
rightDrawable.setBounds( 0, 0, rightDrawable.getIntrinsicWidth(), rightDrawable.getIntrinsicHeight() );
}
removeChoicePointMarker();
mSearchBox.setTag( null );
if ( mSearchBox.getLayoutParams() instanceof RelativeLayout.LayoutParams ) {
final RelativeLayout.LayoutParams params = ( ( RelativeLayout.LayoutParams ) mSearchBox.getLayoutParams() );
params.removeRule( RelativeLayout.LEFT_OF );
mSearchBox.setPadding( 0, 0, WindowUtils.dip2px( mContext, 0 ), 0 );
mSearchBox.setLayoutParams( params );
}
mLastAMapLocation = null;
}
/**
* 多项搜索:显示我的位置、地图选点、我的定位
*/
private void multiSearchUI() {
mUiMode = SearchConstants.UI_MODE_MULTI;
mSearchBox.setText( "" );
mSearchBox.setEnabled( true );
mMyLocation.setVisibility( View.VISIBLE );
mChoicePoint.setVisibility( View.VISIBLE );
mCurrentLocation.setVisibility( View.VISIBLE );
mSearchResult.setVisibility( View.GONE );
if ( mSearchBox.getCompoundDrawables()[2] == null ) {
Drawable rightDrawable = null;
if ( mSearchBoxRightDrawableBitmap == null ) {
mSearchBoxRightDrawableBitmap = BitmapFactory.decodeResource( getResources(), R.drawable.ic_search_unshadow );
}
rightDrawable = new BitmapDrawable( getResources(), mSearchBoxRightDrawableBitmap );
rightDrawable.setBounds( 0, 0, rightDrawable.getIntrinsicWidth(), rightDrawable.getIntrinsicHeight() );
mSearchBox.setCompoundDrawables( null, null, rightDrawable, null );
}
mActionButton.setVisibility( View.INVISIBLE );
removeChoicePointMarker();
mSearchBox.setTag( null );
if ( mSearchBox.getLayoutParams() instanceof RelativeLayout.LayoutParams ) {
final RelativeLayout.LayoutParams params = ( ( RelativeLayout.LayoutParams ) mSearchBox.getLayoutParams() );
params.removeRule( RelativeLayout.LEFT_OF );
mSearchBox.setPadding( 0, 0, WindowUtils.dip2px( mContext, 0 ), 0 );
mSearchBox.setLayoutParams( params );
}
mLastAMapLocation = null;
}
/**
* 显示我的位置,并且可设置为家
*/
private void multiSearchMyLocationUI() {
mUiMode = SearchConstants.UI_MODE_MULTI_MY_LOCATION;
mSearchBox.setEnabled( false );
mMyLocation.setVisibility( View.GONE );
mChoicePoint.setVisibility( View.GONE );
mCurrentLocation.setVisibility( View.GONE );
mSearchResult.setVisibility( View.GONE );
mActionButton.setVisibility( View.VISIBLE );
mActionButton.setText( com.mogo.module.navi.database.ui.search.SearchUtils.getSearchTypeActionName( mSearchType ) );
mSearchBox.setCompoundDrawables( null, null, null, null );
removeChoicePointMarker();
mSearchBox.setTag( null );
if ( mSearchBox.getLayoutParams() instanceof RelativeLayout.LayoutParams ) {
final RelativeLayout.LayoutParams params = ( ( RelativeLayout.LayoutParams ) mSearchBox.getLayoutParams() );
params.addRule( RelativeLayout.LEFT_OF, R.id.amap_search_action_setting );
mSearchBox.setPadding( 0, 0, WindowUtils.dip2px( mContext, 15 ), 0 );
mSearchBox.setLayoutParams( params );
}
}
/**
* 显示我的位置,并且可设置为家
*/
private void multiSearchChoicePointUI() {
mUiMode = SearchConstants.UI_MODE_MULTI_CHOICE_POINT;
mSearchBox.setEnabled( false );
mMyLocation.setVisibility( View.GONE );
mChoicePoint.setVisibility( View.GONE );
mCurrentLocation.setVisibility( View.GONE );
mSearchResult.setVisibility( View.GONE );
mActionButton.setVisibility( View.VISIBLE );
mActionButton.setText( com.mogo.module.navi.database.ui.search.SearchUtils.getSearchTypeActionName( mSearchType ) );
mSearchBox.setCompoundDrawables( null, null, null, null );
showChoicePointMarker();
mSearchBox.setTag( null );
if ( mSearchBox.getLayoutParams() instanceof RelativeLayout.LayoutParams ) {
final RelativeLayout.LayoutParams params = ( ( RelativeLayout.LayoutParams ) mSearchBox.getLayoutParams() );
params.addRule( RelativeLayout.LEFT_OF, R.id.amap_search_action_setting );
mSearchBox.setPadding( 0, 0, WindowUtils.dip2px( mContext, 15 ), 0 );
mSearchBox.setLayoutParams( params );
}
mLastAMapLocation = null;
}
private void saveCurrentLocationAsCommonAddress() {
if ( mLastAMapLocation == null ) {
shortToast( "定位失败,请重试" );
return;
}
final Disposable disposable = mSearchPresenter.cacheCommonAddressPoi( mLastAMapLocation ).subscribe( output -> {
Toast.makeText( mContext, "设置成功!", Toast.LENGTH_SHORT ).show();
mActionSuccess = true;
}, error -> {
if ( error instanceof Exception) {
Toast.makeText( mContext, ( (Exception) error ).getMessage(), Toast.LENGTH_SHORT ).show();
mActionSuccess = false;
}
} );
mSearchPresenter.addDisposable( disposable );
}
private void saveRegeoAddressAsCommonAddress() {
if ( mSearchBox.getTag() instanceof RegeocodeAddress ) {
final Disposable disposable = mSearchPresenter.cacheCommonAddressPoi( ( ( RegeocodeAddress ) mSearchBox.getTag() ) ).subscribe( output -> {
Toast.makeText( mContext, "设置成功!", Toast.LENGTH_SHORT ).show();
mActionSuccess = true;
}, error -> {
if ( error instanceof Exception) {
Toast.makeText( mContext, ( (Exception) error ).getMessage(), Toast.LENGTH_SHORT ).show();
mActionSuccess = false;
}
} );
mSearchPresenter.addDisposable( disposable );
} else {
Toast.makeText( mContext, "请选择位置", Toast.LENGTH_SHORT ).show();
}
}
@Override
public void onLocationChanged( AMapLocation aMapLocation ) {
final String checkMsg = AMapUtils.getAMapLocationErrorMsg( aMapLocation );
if ( AMapUtils.LOC_SUCCESS.equals( checkMsg ) ) {
mLastAMapLocation = aMapLocation;
MapUIController.getInstance().moveCurrentPositionToCenter( new LatLng( aMapLocation.getLatitude(), aMapLocation.getLongitude() ) );
if ( mUiMode == SearchConstants.UI_MODE_MULTI_MY_LOCATION ) {
showMyLocationAddress( aMapLocation );
} else if ( mUiMode == SearchConstants.UI_MODE_MULTI_CHOICE_POINT ) {
showChoicePointAddress( aMapLocation );// 显示当前中心点地址
}
} else {
Toast.makeText( mContext, checkMsg, Toast.LENGTH_SHORT ).show();
}
AMapLocationManager.getInstance( mContext ).removeLocationListener( this );
AMapLocationManager.getInstance( mContext ).stop();
}
private void showMyLocationAddress( AMapLocation location ) {
if ( location == null ) {
return;
}
mSearchBox.setEnabled( false );
mSearchBox.setText( location.getPoiName() );
}
/**
* 显示点选marker隐藏自车marker
*/
private void showChoicePointMarker() {
final AMap aMap = mUiController.getAMapServiceVisitor().getMap();
if ( aMap == null ) {
return;
}
// AMapService 里设置了监听,此处会覆盖
LatLng latLng = aMap.getCameraPosition().target;
Point screenPosition = aMap.getProjection().toScreenLocation( latLng );
mChoicePointMaker = aMap.addMarker( new MarkerOptions().zIndex( 1 ).anchor( 0.5f, 0.5f ).position( latLng ).icon( BitmapDescriptorFactory.fromResource( R.drawable.ic_search_choice_point ) ) );
mChoicePointMaker.setPositionByPixels( screenPosition.x, screenPosition.y );
MapUIController.getInstance().changeMyLocationVisibility( false );
}
/**
* 隐藏点选marker显示自车marker
*/
private void removeChoicePointMarker() {
if ( mChoicePointMaker != null ) {
mChoicePointMaker.remove();
}
mChoicePointMaker = null;
MapUIController.getInstance().changeMyLocationVisibility( true );
}
private void showChoicePointAddress( AMapLocation location ) {
if ( location == null ) {
return;
}
mSearchBox.setEnabled( false );
mSearchBox.setText( location.getPoiName() );
}
// view interface
@Override
public EditText getSearchBox() {
return mSearchBox;
}
@Override
public void renderSearchPoiResult( List< Tip > datums, boolean showDelete ) {
if ( datums == null || datums.isEmpty() ) {
mSearchResult.setVisibility( View.GONE );
} else {
mSearchResult.setVisibility( View.VISIBLE );
}
if ( mPoiAdapter == null ) {
mPoiAdapter = new SearchPoiAdapter( datums, com.mogo.module.navi.database.ui.search.SearchUtils
.getSearchTypeActionName( mSearchType ) );
mPoiAdapter.setOnItemClickedListener( item -> {
if ( mSearchType == SearchConstants.SEARCH_TYPE_COMMON ) {
final Disposable disposable = mSearchPresenter.cacheSelectPoiItem( item ).subscribe( output -> {
navi2Location( EntityConvertUtils.tipToPoi( item ) );
} );
mSearchPresenter.addDisposable( disposable );
} else {
// do nothing.
}
} );
mPoiAdapter.setOnDeleteAllClickedListener( NULL -> {
mSearchPresenter.deleteAllCachedPoi();
} );
mPoiAdapter.setOnActionButtonClickedListener( poi -> {
final Disposable disposable = mSearchPresenter.cacheCommonAddressPoi( poi ).subscribe( output -> {
Toast.makeText( mContext, "设置成功!", Toast.LENGTH_SHORT ).show();
mActionSuccess = true;
} );
mSearchPresenter.addDisposable( disposable );
} );
mPoiAdapter.setShowDelete( showDelete );
mSearchResult.setAdapter( mPoiAdapter );
} else {
if ( datums != null && !datums.isEmpty() ) {
mSearchResult.scrollToPosition( 0 );
}
mPoiAdapter.refresh( datums, showDelete );
}
}
@Override
public int getSearchType() {
return mSearchType;
}
@Override
public int getUiMode() {
return mUiMode;
}
@Override
public void renderChoicePointResult( RegeocodeAddress address ) {
if ( address == null ) {
mSearchBox.setTag( null );
mSearchBox.setText( "" );
return;
}
mSearchBox.setTag( address );
mSearchBox.setText( address.getFormatAddress() );
}
@Override
public void renderErrorView() {
}
@Override
public void renderContentView() {
}
// view interface end
/**
* 屏幕中心marker 跳动
*/
@Override
public void startJumpAnimation() {
final AMap aMap = mUiController.getAMapServiceVisitor().getMap();
if ( mChoicePointMaker != null ) {
//根据屏幕距离计算需要移动的目标点
final LatLng latLng = mChoicePointMaker.getPosition();
Point point = aMap.getProjection().toScreenLocation( latLng );
point.y -= WindowUtils.dip2px( mContext, 125 );
LatLng target = aMap.getProjection()
.fromScreenLocation( point );
//使用TranslateAnimation,填写一个需要移动的目标点
Animation animation = new TranslateAnimation( target );
animation.setInterpolator( new Interpolator() {
@Override
public float getInterpolation( float input ) {
// 模拟重加速度的interpolator
if ( input <= 0.5 ) {
return ( float ) ( 0.5f - 2 * ( 0.5 - input ) * ( 0.5 - input ) );
} else {
return ( float ) ( 0.5f - Math.sqrt( ( input - 0.5f ) * ( 1.5f - input ) ) );
}
}
} );
//整个移动所需要的时间
animation.setDuration( 600 );
//设置动画
mChoicePointMaker.setAnimation( animation );
//开始动画
mChoicePointMaker.startAnimation();
}
}
/**
* @param searchPoi 导航目的地
*/
private void navi2Location( SearchPoi searchPoi ) {
if ( searchPoi == null || searchPoi == SearchPoi.NULL ) {
Toast.makeText( mContext, "未设置", Toast.LENGTH_SHORT ).show();
return;
}
SearchPoiLiveData.getInstance().postValue( searchPoi );
exitSearch();
}
/**
* 退出搜索,进行清理
*/
private void exitSearch() {
switch ( mSearchType ) {
case SearchConstants.SEARCH_TYPE_COMMON:
try {
mUiController.popBackStackImmediate();
} catch ( Exception e ) {
e.printStackTrace();
}
break;
case SearchConstants.SEARCH_TYPE_MULTI_HOME:
case SearchConstants.SEARCH_TYPE_MULTI_COMPANY:
if ( !mActionSuccess ) {
// 通过搜索框搜索,在设置成功之前点击返回按钮,返回到未搜索状态
if ( mUiMode != SearchConstants.UI_MODE_MULTI || mSearchResult.getVisibility() == View.VISIBLE ) {
multiSearchUI();
return;
}
}
try {
mUiController.popBackStackImmediate();
mUiController.onSettingsMode();
} catch ( Exception e ) {
e.printStackTrace();
}
break;
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
if ( mSearchPresenter != null ) {
mSearchPresenter.onDestroy( getViewLifecycleOwner() );
getLifecycle().removeObserver( mSearchPresenter );
mSearchPresenter = null;
}
mSearchBox.setTag( null );
mUiController = null;
if ( mSearchBoxRightDrawableBitmap != null ) {
try {
mSearchBoxRightDrawableBitmap.recycle();
} catch ( Exception e ) {
}
}
if ( mPoiAdapter != null ) {
mPoiAdapter.clear();
}
mPoiAdapter = null;
mSearchBoxRightDrawableBitmap = null;
removeChoicePointMarker();
AMapLocationManager.getInstance( mContext ).release();
}
}

View File

@@ -0,0 +1,289 @@
package com.mogo.module.navi.ui.search;
import android.app.AlertDialog;
import android.text.Editable;
import android.text.TextUtils;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleOwner;
import com.amap.api.location.AMapLocation;
import com.amap.api.maps.model.CameraPosition;
import com.amap.api.services.core.LatLonPoint;
import com.amap.api.services.geocoder.GeocodeResult;
import com.amap.api.services.geocoder.GeocodeSearch;
import com.amap.api.services.geocoder.RegeocodeAddress;
import com.amap.api.services.geocoder.RegeocodeQuery;
import com.amap.api.services.geocoder.RegeocodeResult;
import com.amap.api.services.help.Inputtips;
import com.amap.api.services.help.InputtipsQuery;
import com.amap.api.services.help.Tip;
import com.mogo.commons.mvp.Presenter;
import com.mogo.module.navi.constants.DataConstants;
import io.reactivex.Single;
import io.reactivex.SingleEmitter;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import java.util.List;
/**
* @author congtaowang
* @since 2019-10-02
* <p>
* 搜搜页逻辑处理
*/
public class SearchPresenter extends Presenter< SearchView >
implements Inputtips.InputtipsListener, GeocodeSearch.OnGeocodeSearchListener {
private Inputtips mSearchEngine;
private GeocodeSearch mGeocodeSearch;
private CompositeDisposable mCompositeDisposable;
private CameraPosition mLastCameraPosition;
public SearchPresenter( SearchView view ) {
super( view );
mCompositeDisposable = new CompositeDisposable();
}
@Override
public void onCreate( @NonNull LifecycleOwner owner ) {
super.onCreate( owner );
attachSearchBoxTextWatcher( mView.getSearchBox() );
switch ( mView.getSearchType() ) {
case SearchConstants.SEARCH_TYPE_COMMON:
loadHistories();
break;
}
}
private void loadHistories() {
}
private void attachSearchBoxTextWatcher( EditText editText ) {
if ( editText == null ) {
return;
}
editText.addTextChangedListener(watcherAdapter);
}
private final TextWatcherAdapter watcherAdapter = new TextWatcherAdapter() {
@Override
public void afterTextChanged( Editable s ) {
// 避免 disable 设置内容时触发
final String input = s.toString();
startSearchPoiByInput( input );
}
};
private void startSearchPoiByInput( String keyword ) {
switch ( mView.getUiMode() ) {
// 如果是普通搜索,清空搜索内容后需要加载缓存历史,否则不需要
case SearchConstants.UI_MODE_COMMON:
if ( TextUtils.isEmpty( keyword ) ) {
loadHistories();
return;
}
break;
case SearchConstants.UI_MODE_MULTI_CHOICE_POINT:
case SearchConstants.UI_MODE_MULTI_MY_LOCATION:
return;
case SearchConstants.UI_MODE_MULTI:
if ( TextUtils.isEmpty( keyword ) ) {
mView.renderSearchPoiResult( null, false );
return;
}
break;
}
final InputtipsQuery searchQuery = new InputtipsQuery( keyword, "" );
if ( mSearchEngine == null ) {
mSearchEngine = new Inputtips( getContext(), searchQuery );
mSearchEngine.setInputtipsListener( this );
} else {
mSearchEngine.setQuery( searchQuery );
}
mSearchEngine.requestInputtipsAsyn();
}
@Override
public void onGetInputtips( List<Tip> list, int i ) {
if ( mView != null ) {
mView.renderSearchPoiResult( list, false );
}
}
/**
* 缓存搜索到的导航地址
*
* @param tip
* @return
*/
public Single cacheSelectPoiItem( Tip tip ) {
return Single.create( emitter -> {
SearchPoi poi = EntityConvertUtils.tipToPoi( tip );
//ignore insert result
final List<Long> output = AMapDatabase.getInstance( getContext() ).getSearchPoiDao().insert( poi );
emitter.onSuccess( output );
} ).subscribeOn( Schedulers.io() ).observeOn( AndroidSchedulers.mainThread() );
}
public void addDisposable( Disposable disposable ) {
mCompositeDisposable.add( disposable );
}
public void deleteAllCachedPoi() {
new AlertDialog.Builder( getContext() )
.setMessage( "清空历史记录?" )
.setPositiveButton( "立即清空", ( dlg, which ) -> {
dlg.dismiss();
deleteAllCachedPoiImpl();
} )
.setNegativeButton( "取消", ( dlg, which ) -> {
dlg.dismiss();
} )
.create()
.show();
}
private void deleteAllCachedPoiImpl() {
final Disposable disposable = AMapDatabase.getInstance( getContext() )
.getSearchPoiDao()
.getAll()
.map( input -> {
return AMapDatabase.getInstance( getContext() ).getSearchPoiDao().deleteAll( input );
} )
.subscribeOn( Schedulers.io() )
.observeOn( AndroidSchedulers.mainThread() )
.subscribe( count -> {
view.renderSearchPoiResult( null, false );
} );
mCompositeDisposable.add( disposable );
}
/**
* 缓存搜索位置为常用地址设置
*
* @param tip
* @return
*/
public Single cacheCommonAddressPoi( Tip tip ) {
return Single.<List<Long>>create( emitter -> {
SearchPoi poi = EntityConvertUtils.tipToPoi( tip );
emitterCommonAddress( emitter, poi );
} ).subscribeOn( Schedulers.io() ).observeOn( AndroidSchedulers.mainThread() );
}
/**
* 缓存地理位置常用地址设置
*
* @param location
* @return
*/
public Single cacheCommonAddressPoi( AMapLocation location ) {
return Single.
<List<Long>>create( emitter -> {
SearchPoi poi = EntityConvertUtils.aMapLocation2Poi( location );
emitterCommonAddress( emitter, poi );
} )
.subscribeOn( Schedulers.io() )
.observeOn( AndroidSchedulers.mainThread() );
}
/**
* 缓存反地理位置编码常用地址设置
*
* @param address
* @return
*/
public Single cacheCommonAddressPoi( RegeocodeAddress address ) {
return Single.
<List<Long>>create( emitter -> {
SearchPoi poi = EntityConvertUtils.geocodeAddress2Poi( address, mLastCameraPosition );
emitterCommonAddress( emitter, poi );
} )
.subscribeOn( Schedulers.io() )
.observeOn( AndroidSchedulers.mainThread() );
}
private void emitterCommonAddress( SingleEmitter<List<Long>> emitter, SearchPoi poi ) {
String poiId = null;
switch ( view.getSearchType() ) {
case SearchConstants.SEARCH_TYPE_MULTI_HOME:
poiId = DataConstants.POI_ID_HOME;
break;
case SearchConstants.SEARCH_TYPE_MULTI_COMPANY:
poiId = DataConstants.POI_ID_COMPANY;
break;
}
if ( TextUtils.isEmpty( poiId ) ) {
emitter.onError( new IllegalArgumentException( "设置类型错误,请重试" ) );
return;
}
if ( poi == null ) {
emitter.onError( new IllegalArgumentException( "位置类型转换错误,请重试" ) );
return;
}
poi.setpId( poiId );
poi.setType( view.getSearchType() );
//ignore insert result
final List<Long> output = AMapDatabase.getInstance( getContext() ).getSearchPoiDao().insert( poi );
notifyAIAssistCommonAddressChanged();
emitter.onSuccess( output );
}
private void notifyAIAssistCommonAddressChanged() {
if ( view.getSearchType() == SearchConstants.SEARCH_TYPE_MULTI_HOME ) {
AddressHelper.notifyHomeAddressChanged( getContext() );
} else if ( view.getSearchType() == SearchConstants.SEARCH_TYPE_MULTI_COMPANY ) {
AddressHelper.notifyCompanyAddressChanged( getContext() );
}
}
private void startSearchPoiByPoint( CameraPosition position ) {
if ( position == null ) {
return;
}
RegeocodeQuery
query = new RegeocodeQuery( new LatLonPoint( position.target.latitude, position.target.longitude ), 200, GeocodeSearch.AMAP );
if ( mGeocodeSearch == null ) {
mGeocodeSearch = new GeocodeSearch( getContext() );
mGeocodeSearch.setOnGeocodeSearchListener( this );
}
mGeocodeSearch.getFromLocationAsyn( query );
}
@Override
public void onRegeocodeSearched( RegeocodeResult regeocodeResult, int resultID ) {
if ( resultID == 1000 ) { // success
view.renderChoicePointResult( regeocodeResult.getRegeocodeAddress() );
} else {
view.renderChoicePointResult( null );
}
}
@Override
public void onGeocodeSearched( GeocodeResult geocodeResult, int resultID ) {
}
@Override
public void onDestroy( @NonNull LifecycleOwner owner ) {
super.onDestroy( owner );
if ( view.getSearchBox() != null ) {
view.getSearchBox().removeTextChangedListener( watcherAdapter );
}
if ( mCompositeDisposable != null && !mCompositeDisposable.isDisposed() ) {
mCompositeDisposable.dispose();
mCompositeDisposable = null;
}
CameraChangedLiveData.getInstance().removeAllObserver();
mSearchEngine = null;
mGeocodeSearch = null;
mLastCameraPosition = null;
}
}

View File

@@ -0,0 +1,39 @@
package com.mogo.module.navi.ui.search;
/**
* @author congtaowang
* @since 2019-12-08
* <p>
* 搜索工具类
*/
public class SearchUtils {
/**
* @param searchType
* @return
*/
public static int checkAndResetSearchType( int searchType ) {
switch ( searchType ) {
case SearchConstants.SEARCH_TYPE_COMMON:
case SearchConstants.SEARCH_TYPE_MULTI_HOME:
case SearchConstants.SEARCH_TYPE_MULTI_COMPANY:
break;
default:
searchType = SearchConstants.SEARCH_TYPE_COMMON;
break;
}
return searchType;
}
public static String getSearchTypeActionName( int searchType ) {
switch ( searchType ) {
case SearchConstants.SEARCH_TYPE_COMMON:
return null;
case SearchConstants.SEARCH_TYPE_MULTI_HOME:
return "设为家";
case SearchConstants.SEARCH_TYPE_MULTI_COMPANY:
return "设为公司";
}
return null;
}
}

View File

@@ -0,0 +1,46 @@
package com.mogo.module.navi.ui.search;
import android.widget.EditText;
import com.amap.api.services.geocoder.RegeocodeAddress;
import com.amap.api.services.help.Tip;
import com.mogo.commons.mvp.IView;
import java.util.List;
/**
* @author congtaowang
* @since 2019-10-02
* <p>
* 描述
*/
public interface SearchView extends IView {
EditText getSearchBox();
/**
* @param datums
* @param showDelete 是否显示清空历史记录项
*/
void renderSearchPoiResult(List<Tip> datums, boolean showDelete);
int getSearchType();
/**
* 当前是哪一个操作
*
* @return
*/
int getUiMode();
/**
* 显示逆地理位置编码结果
*
* @param address
*/
void renderChoicePointResult(RegeocodeAddress address);
/**
* 选点完毕后marker动画
*/
void startJumpAnimation();
}

View File

@@ -0,0 +1,83 @@
package com.mogo.module.navi.wrapper;
import android.os.Parcel;
import android.os.Parcelable;
import com.amap.api.navi.model.AMapCalcRouteResult;
/**
* @author congtaowang
* @since 2019-09-27
* <p>
* 描述
*/
public class AMapCalcRouteResultWrapper implements Parcelable {
private final int errorCode;
private final int[] routeId;
private final int calcRouteType;
private final String errorDetail;
public AMapCalcRouteResultWrapper( AMapCalcRouteResult result ) {
errorCode = result.getErrorCode();
routeId = result.getRouteid();
calcRouteType = result.getCalcRouteType();
errorDetail = result.getErrorDetail();
}
public AMapCalcRouteResult parse() {
final AMapCalcRouteResult inst = new AMapCalcRouteResult();
inst.setErrorCode( getErrorCode() );
inst.setRouteid( getRouteId() );
inst.setCalcRouteType( getCalcRouteType() );
inst.setErrorDetail( getErrorDetail() );
return inst;
}
public int getErrorCode() {
return errorCode;
}
public int[] getRouteId() {
return routeId;
}
public int getCalcRouteType() {
return calcRouteType;
}
public String getErrorDetail() {
return errorDetail;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel( Parcel dest, int flags ) {
dest.writeInt( this.errorCode );
dest.writeIntArray( this.routeId );
dest.writeInt( this.calcRouteType );
dest.writeString( this.errorDetail );
}
protected AMapCalcRouteResultWrapper( Parcel in ) {
this.errorCode = in.readInt();
this.routeId = in.createIntArray();
this.calcRouteType = in.readInt();
this.errorDetail = in.readString();
}
public static final Creator< AMapCalcRouteResultWrapper > CREATOR = new Creator< AMapCalcRouteResultWrapper >() {
@Override
public AMapCalcRouteResultWrapper createFromParcel( Parcel source ) {
return new AMapCalcRouteResultWrapper( source );
}
@Override
public AMapCalcRouteResultWrapper[] newArray( int size ) {
return new AMapCalcRouteResultWrapper[size];
}
};
}

View File

@@ -0,0 +1,99 @@
package com.mogo.module.navi.wrapper;
import com.amap.api.navi.model.AMapNaviCameraInfo;
/**
* @author congtaowang
* @since 2019-10-10
* <p>
* 描述
*/
public class AMapNaviCameraInfoWrapper {
private int cameraDistance;
private double x;
private double y;
/**
* {@link AMapNaviCameraInfo#getCameraType()}
* {@link com.amap.api.navi.enums.CameraType#SPEED} 测速摄像
* {@link com.amap.api.navi.enums.CameraType#SURVEILLANCE}监控摄像
* {@link com.amap.api.navi.enums.CameraType#TRAFFICLIGHT} 闯红灯拍照
* {@link com.amap.api.navi.enums.CameraType#BREAKRULE} 违章拍照
* {@link com.amap.api.navi.enums.CameraType#BUSWAY} 公交专用道摄像头
* {@link com.amap.api.navi.enums.CameraType#EMERGENCY}应急车道拍照
* {@link com.amap.api.navi.enums.CameraType#BICYCLE}非机动车道(暂未使用)
* {@link com.amap.api.navi.enums.CameraType#INTERVALVELOCITYSTART}区间测速起始
* {@link com.amap.api.navi.enums.CameraType#INTERVALVELOCITYEND}区间测速解除
*/
private int cameraType;
private int cameraSpeed;
private int averageSpeed;
private int reasonableSpeedInRemainDist;
private int distance;
private int[] speed;
public AMapNaviCameraInfoWrapper( AMapNaviCameraInfo info ) {
if ( info != null ) {
cameraDistance = info.getCameraDistance();
x = info.getX();
y = info.getY();
cameraType = info.getCameraType();
cameraSpeed = info.getCameraSpeed();
averageSpeed = info.getAverageSpeed();
reasonableSpeedInRemainDist = info.getReasonableSpeedInRemainDist();
distance = info.getDistance();
speed = info.getSpeed();
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append( "cameraDistance" ).append( "=" ).append( cameraDistance ).append( "\n" );
builder.append( "cameraType" ).append( "=" ).append( cameraType ).append( "\n" );
builder.append( "cameraSpeed" ).append( "=" ).append( cameraSpeed ).append( "\n" );
builder.append( "averageSpeed" ).append( "=" ).append( averageSpeed ).append( "\n" );
builder.append( "reasonableSpeedInRemainDist" ).append( "=" ).append( reasonableSpeedInRemainDist ).append( "\n" );
builder.append( "distance" ).append( "=" ).append( distance ).append( "\n" );
if ( speed != null ) {
builder.append( "speed" ).append( "=" ).append( speed ).append( "\n" );
}
return builder.toString();
}
public int getCameraDistance() {
return cameraDistance;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public int getCameraType() {
return cameraType;
}
public int getCameraSpeed() {
return cameraSpeed;
}
public int getAverageSpeed() {
return averageSpeed;
}
public int getReasonableSpeedInRemainDist() {
return reasonableSpeedInRemainDist;
}
public int getDistance() {
return distance;
}
public int[] getSpeed() {
return speed;
}
}

View File

@@ -0,0 +1,148 @@
package com.mogo.module.navi.wrapper;
import android.os.Parcel;
import android.os.Parcelable;
import com.amap.api.navi.model.AMapNaviLocation;
import com.amap.api.navi.model.NaviLatLng;
/**
* @author congtaowang
* @since 2019-09-26
* <p>
* 描述
*/
public class AMapNaviLocationWrapper implements Parcelable {
private final float accuracy;
private final double altitude;
private final float bearing;
private final float speed;
private final long time;
private final int matchStatus;
private final NaviLatLng coord;
private final int curStepIndex;
private final int curLinkIndex;
private final int curPointIndex;
private final int type;
public AMapNaviLocationWrapper( AMapNaviLocation aMapNaviLocation ) {
this.accuracy = aMapNaviLocation.getAccuracy();
this.altitude = aMapNaviLocation.getAltitude();
this.bearing = aMapNaviLocation.getBearing();
this.speed = aMapNaviLocation.getSpeed();
this.time = aMapNaviLocation.getTime();
this.matchStatus = aMapNaviLocation.getMatchStatus();
this.coord = aMapNaviLocation.getCoord();
this.curStepIndex = aMapNaviLocation.getCurStepIndex();
this.curLinkIndex = aMapNaviLocation.getCurLinkIndex();
this.curPointIndex = aMapNaviLocation.getCurPointIndex();
this.type = aMapNaviLocation.getType();
}
public AMapNaviLocation parse() {
final AMapNaviLocation inst = new AMapNaviLocation();
inst.setAccuracy( getAccuracy() );
inst.setAltitude( getAltitude() );
inst.setBearing( getBearing() );
inst.setSpeed( getSpeed() );
inst.setTime( getTime() );
inst.setMatchStatus( getMatchStatus() );
inst.setCoord( getCoord() );
inst.setCurStepIndex( getCurStepIndex() );
inst.setCurLinkIndex( getCurLinkIndex() );
inst.setCurPointIndex( getCurPointIndex() );
inst.setType( getType() );
return inst;
}
public float getAccuracy() {
return accuracy;
}
public double getAltitude() {
return altitude;
}
public float getBearing() {
return bearing;
}
public float getSpeed() {
return speed;
}
public long getTime() {
return time;
}
public int getMatchStatus() {
return matchStatus;
}
public NaviLatLng getCoord() {
return coord;
}
public int getCurStepIndex() {
return curStepIndex;
}
public int getCurLinkIndex() {
return curLinkIndex;
}
public int getCurPointIndex() {
return curPointIndex;
}
public int getType() {
return type;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel( Parcel dest, int flags ) {
dest.writeFloat( this.accuracy );
dest.writeDouble( this.altitude );
dest.writeFloat( this.bearing );
dest.writeFloat( this.speed );
dest.writeLong( this.time );
dest.writeInt( this.matchStatus );
dest.writeParcelable( this.coord, flags );
dest.writeInt( this.curStepIndex );
dest.writeInt( this.curLinkIndex );
dest.writeInt( this.curPointIndex );
dest.writeInt( this.type );
}
protected AMapNaviLocationWrapper( Parcel in ) {
this.accuracy = in.readFloat();
this.altitude = in.readDouble();
this.bearing = in.readFloat();
this.speed = in.readFloat();
this.time = in.readLong();
this.matchStatus = in.readInt();
this.coord = in.readParcelable( NaviLatLng.class.getClassLoader() );
this.curStepIndex = in.readInt();
this.curLinkIndex = in.readInt();
this.curPointIndex = in.readInt();
this.type = in.readInt();
}
public static final Creator< AMapNaviLocationWrapper >
CREATOR = new Parcelable.Creator< AMapNaviLocationWrapper >() {
@Override
public AMapNaviLocationWrapper createFromParcel( Parcel source ) {
return new AMapNaviLocationWrapper( source );
}
@Override
public AMapNaviLocationWrapper[] newArray( int size ) {
return new AMapNaviLocationWrapper[size];
}
};
}

View File

@@ -0,0 +1,104 @@
package com.mogo.module.navi.wrapper;
import android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;
import com.amap.api.maps.model.LatLng;
import com.amap.api.navi.model.AMapNaviLocation;
import com.amap.api.navi.model.NaviLatLng;
/**
* @author congtaowang
* @since 2019-09-26
* <p>
* 经纬度
*/
public class LatLngWrapper implements Parcelable {
private final double lat;
private final double lng;
public LatLngWrapper( double lat, double lng ) {
this.lat = lat;
this.lng = lng;
}
public double getLat() {
return lat;
}
public double getLng() {
return lng;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel( Parcel dest, int flags ) {
dest.writeDouble( this.lat );
dest.writeDouble( this.lng );
}
protected LatLngWrapper( Parcel in ) {
this.lat = in.readDouble();
this.lng = in.readDouble();
}
public static final Creator< LatLngWrapper > CREATOR = new Creator< LatLngWrapper >() {
@Override
public LatLngWrapper createFromParcel( Parcel source ) {
return new LatLngWrapper( source );
}
@Override
public LatLngWrapper[] newArray( int size ) {
return new LatLngWrapper[size];
}
};
public static LatLngWrapper from( LatLng latlng ) {
return new LatLngWrapper( latlng.latitude, latlng.longitude );
}
public static LatLngWrapper from( NaviLatLng latlng ) {
return new LatLngWrapper( latlng.getLatitude(), latlng.getLongitude() );
}
public static LatLngWrapper from( AMapNaviLocation latlng ) {
return from( latlng.getCoord() );
}
public LatLng parseLatLngInst() {
return new LatLng( getLat(), getLng() );
}
public static LatLng parse( NaviLatLng latLng ) {
if ( latLng == null ) {
return null;
}
return new LatLng( latLng.getLatitude(), latLng.getLongitude() );
}
public static LatLng parse( Location location ) {
if ( location == null ) {
return null;
}
return new LatLng( location.getLatitude(), location.getLongitude() );
}
public static LatLng parse( double lat, double lng ) {
if ( isRightLatLng( lat, lng ) ) {
return new LatLng( lat, lng );
}
return null;
}
public static boolean isRightLatLng( double lat, double lng ) {
return lat > 0 && lat < 90 && lng > 0 && lng < 180;
}
}

View File

@@ -0,0 +1,116 @@
package com.mogo.module.navi.wrapper;
import android.os.Parcel;
import android.os.Parcelable;
import com.amap.api.navi.model.NaviInfo;
/**
* @author congtaowang
* @since 2019-09-29
* <p>
* 导航信息对象
*/
public class NaviInfoWrapper implements Parcelable {
private static final NaviInfoWrapper INST = new NaviInfoWrapper();
private int nextIconType;
private int nextDistance;
private String nextRoad;
private int surplusDistance;
private int surplusTime;
private int currentSpeed;
private NaviInfoWrapper() {
}
public static NaviInfoWrapper getInst() {
return INST;
}
public synchronized NaviInfoWrapper fillWith( NaviInfo naviInfo ) {
INST.nextIconType = naviInfo.getIconType();
INST.nextDistance = naviInfo.getCurStepRetainDistance();
INST.nextRoad = naviInfo.getNextRoadName();
INST.surplusDistance = naviInfo.getPathRetainDistance();
INST.surplusTime = naviInfo.getPathRetainTime();
INST.currentSpeed = naviInfo.getCurrentSpeed();
return INST;
}
public NaviInfoWrapper( NaviInfo naviInfo ) {
this.nextIconType = naviInfo.getIconType();
this.nextDistance = naviInfo.getCurStepRetainDistance();
this.nextRoad = naviInfo.getNextRoadName();
this.surplusDistance = naviInfo.getPathRetainDistance();
this.surplusTime = naviInfo.getPathRetainTime();
this.currentSpeed = naviInfo.getCurrentSpeed();
}
public synchronized int getNextIconType() {
return nextIconType;
}
public synchronized int getNextDistance() {
return nextDistance;
}
public synchronized String getNextRoad() {
return nextRoad;
}
public synchronized int getSurplusDistance() {
return surplusDistance;
}
public synchronized int getSurplusTime() {
return surplusTime;
}
public synchronized int getCurrentSpeed() {
return currentSpeed;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel( Parcel dest, int flags ) {
dest.writeInt( getNextIconType() );
dest.writeInt( getNextDistance() );
dest.writeString( getNextRoad() );
dest.writeInt( getSurplusDistance() );
dest.writeInt( getSurplusTime() );
dest.writeInt( getCurrentSpeed() );
}
protected NaviInfoWrapper( Parcel in ) {
this.nextIconType = in.readInt();
this.nextDistance = in.readInt();
this.nextRoad = in.readString();
this.surplusDistance = in.readInt();
this.surplusTime = in.readInt();
this.currentSpeed = in.readInt();
}
public static final Creator< NaviInfoWrapper > CREATOR = new Parcelable.Creator< NaviInfoWrapper >() {
@Override
public NaviInfoWrapper createFromParcel( Parcel source ) {
return new NaviInfoWrapper( source );
}
@Override
public NaviInfoWrapper[] newArray( int size ) {
return new NaviInfoWrapper[size];
}
};
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
return builder.toString();
}
}

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="130dp">
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_marginLeft="130dp"
android:layout_marginRight="60dp"
android:background="#802D3256"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<ImageView
android:id="@+id/iv_position"
android:layout_width="30dp"
android:layout_height="42dp"
android:layout_marginLeft="130dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_position"
android:layout_width="0dp"
android:layout_height="42dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="28dp"
android:layout_marginRight="60dp"
android:textColor="@color/white"
android:textSize="30sp"
app:layout_constraintLeft_toRightOf="@id/iv_position"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_position_des"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="2dp"
android:layout_marginRight="60dp"
android:ellipsize="marquee"
android:singleLine="true"
android:textColor="@color/white_7f"
android:textSize="22sp"
app:layout_constraintLeft_toRightOf="@id/iv_position"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_position" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
<color name="bg_gray_common_page">#ff080625</color>
<color name="white">#FFFFFF</color>
<color name="white_7f">#7FFFFFFF</color>
<color name="arrow_color">#00BFFF</color>
<color name="white_30">#4DFFFFFF</color>
<color name="txt_gray_day">#4D080625</color>
<color name="bg_common">#080625</color>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="tag_position" type="id"/>
</resources>