简介
这是一个运用Java(以后还会推出Kotlin版别)语言,从0开发一个Android渠道,挨近企业级的项目(我的云音乐),包括了根底内容,高档内容,项目封装,项appetite目重构等知识;主要是运用体系功用,流行的第三方结构,第三方服务,完结挨近企业级商业级项目。
功用点
隐私协议对话框 发动json格式怎么打开界面和动态处理权限 引导界面和广告 轮宫颈癌播图和侧滑菜单 主页杂乱列表和列表排序 音乐播映和音乐列表管理 大局音乐操控条 桌面歌词和自定义款式 大局媒体操控中心 谈论和回复谈论 谈论富文本点击 谈论提醒人和论题 朋json解析友圈动态列表公积金和发布 高德地图定位和途径规划 阿里云OSS上传二维码图案 视频播映和操控 QQ/微信登录和分享 商城/购物车微信付出宝付出 文本和图appointment片谈天 音讯离线推送 主动和手动检查更新 内存泄漏和优化 …
开发环境概述
2022年5月开发完结的,所以全部都是最新的,均匀每3年会从头制造,现在已经是第三版Go了。
JDK17 Android 12/13 最低兼容版别:Android 6.0 Android Studio 2021.1
编译和运行
用最新AS翻开MyCloudMusicAndroidJava目录,然后等候彻底编译成功,由于是企业级项目,所以第三方依靠许多,同时代码量也许多,所以必需求确认彻底编译成功,才干运行。
项目目录结approach构
├── MyCloudMusicAndroidJava │ ├── LRecyclerview //第三方Recyclerview结构 │ ├── LetterIndexView //相似微信通讯录字母索引 │ ├── app //云音乐项目 │ ├── build.gradle │ ├── common.gradle //通用项目装备文件 │ ├── config //装备目录,例如签名 │ ├── glidepalette //Glide画板,用来从网络图片提取色彩 │ ├── gradle │ ├── gradle.properties │ ├── gradlew │ ├── gradlew.bat │ ├── keystore.properties │ ├── local.properties │ ├── settings.gradle │ ├── super-j //公用Java语言扩展 │ ├── super-player-tencent //腾讯开源的超级播映器 │ ├── super-speech-baidu //百度语音辨认
依靠结构
内容太多,只列出部分。
//分页组件版别 //这儿能够检查最新版别:https://developer.android.google.cn/jetpack/androidx/releases/paging def paging_version = "3.1.1" //增加所有libs目录里边的jar,aar implementation fileTree(dir: 'libs', include: ['*.jar','*.aar']) //官方兼容组件,像AppCompatActivity便是该依靠里边的 implementation 'androidx.appcompat:appcompat:1.4.1' //Material Design组件,像FloatingActionButton便是该依靠里边的 implementation 'com.google.android.material:material:1.4.0' //官方供给的约束布局,像ConstraintLayout便是该依靠里边的 implementation 'androidx.constraintlayout:constraintlayout:2.1.0' //UI结构,主要是用他的东西类,也能够单独拷贝出来 //https://qmuiteam.com/android/get-started implementation 'com.qmuiteam:qmui:2.0.1' //动态处理权限 //https://github.com/permissions-dispatcher/PermissionsDispatcher implementation "com.github.permissions-dispatcher:permissionsdispatcher:4.8.0" annotationProcessor "com.github.permissions-dispatcher:permissionsdispatcher-processor:4.8.0" //api:依靠会传递到其他运用本模块的项目 implementation project(path: ':super-j') ... //运用gson解析json //https://github.com/google/gson implementation 'com.google.code.gson:gson:2.9.0' //主动开释RxJava相关资源 //https://github.com/uber/AutoDispose implementation "com.uber.autodispose2:autodispose-androidx-lifecycle:2.1.1" //banner轮播图结构 //https://github.com/youth5201314/banner implementation 'io.github.youth5201314:banner:2.2.2' //图片加载结构,还引证他意图是,coil有些功用欠好完成 //https://github.com/bumptech/glide implementation 'com.github.bumptech.glide:glide:+' annotationProcessor 'com.github.bumptech.glide:compiler:+' implementation 'androidx.recyclerview:recyclerview:1.2.1' //给控件增加未读音讯数红点 //https://github.com/bingoogolapple/BGABadgeView-Android implementation 'com.github.bingoogolapple.BGABadgeView-Android:api:1.2.0' annotationProcessor 'com.github.bingoogolapple.BGABadgeView-Android:compiler:1.2.0' //webview进展条 //https://github.com/youlookwhat/WebProgress implementation 'com.github.youlookwhat:WebProgress:1.2.0' //日志结构 //https://github.com/JakeWharton/timber implementation 'com.jakewharton.timber:timber:5.0.1' implementation "androidx.media:media:+" //和Glide合作处理图片 //能够完成许多作用 //含糊;圆角;圆 //咱们这儿是用它完成含糊作用 //https://github.com/wasabeef/glide-transformations implementation 'jp.wasabeef:glide-transformations:+' //圆形图片控件 //https://github.com/hdodenhof/CircleImageView implementation 'de.hdodenhof:circleimageview:+' //下载结构 //https://github.com/ixuea/android-downloader implementation 'com.ixuea:android-downloader:3.0.0' //阿里云oss //官方文档:https://help.aliyun.com/document_detail/32043.html //sdk地址:https://github.com/aliyun/aliyun-oss-android-sdk implementation 'com.aliyun.dpa:oss-android-sdk:+' //高德地图,这儿引证的是3d //https://lbs.amap.com/api/android-sdk/guide/create-project/android-studio-create-project#gradle_sdk implementation 'com.amap.api:3dmap:+' //定位功用 implementation 'com.amap.api:location:+' //百度语音相关技术,现在主要用在收货地址修改界面,语音输入收货地址 //https://ai.baidu.com/ai-doc/SPEECH/Pkgt4wwdx#%E9%9B%86%E6%88%90%E6%8C%87%E5%8D%97 implementation project(path: ':super-speech-baidu') //TextView显现富文本,现在主要用在产品概况界面,显现富文本产品描述 //https://github.com/wangchenyan/html-text implementation 'com.github.wangchenyan:html-text:+' //Hutool是一个小而全的Java东西类库 // 经过静态办法封装,下降相关API的学习本钱 // 提高工作效率,使Java拥有函数式语言般的优雅 //https://github.com/looly/hutool implementation 'cn.hutool:hutool-all:5.7.14' //付出宝付出 //https://opendocs.alipay.com/open/204/105296 implementation 'com.alipay.sdk:alipaysdk-android:+@aar' //融云IM //https://docs.rongcloud.cn/v4/5X/views/im/ui/guide/quick/include/android.html implementation 'cn.rongcloud.sdk:im_lib:+' //微信付出 //官方sdk下载文档:https://developers.weixin.qq.com/doc/oplatform/Downloads/Android_Resource.html //官方集成文档:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5 implementation 'com.tencent.mm.opensdk:wechat-sdk-android:+' //内存泄漏检测东西 //https://github.com/square/leakcanary //只有调试形式下才增加该依靠 debugImplementation 'com.squareup.leakcanary:leakcanary-android:+' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
用户协议对话框

运用自定义DialogFragment完成,内容是放到字符串文件中的,其中的链接是HTML标签,设置后就能够点击艺术签名设计了,然后修正默许对话框宽度,由于默许的有点窄。
public class TermServiceDialogFragment extends BaseViewModelDialogFragment<FragmentDialogTermServiceBinding> {
...
@Override
protected void initViews() {
super.initViews();
//点击弹窗外边不能封闭
setCancelable(false);
SuperTextUtil.setLinkColor(binding.content, getActivity().getColor(R.color.link));
}
@Override
protected void initListeners() {
super.initListeners();
binding.primary.setOnClickListener(view -> {
dismiss();
onAgreementClickListener.onClick(view);
});
binding.disagree.setOnClickListener(view -> {
dismiss();
SuperProcessUtil.killApp();
});
}
@Override
public void onResume() {
super.onResume();
//修正宽度,默许比AlertDialog.Builder显现对话框宽度窄,看着欠好看
//参阅:https://stackoverflow.com/questions/12478520/how-to-set-dialogfragments-width-and-height
ViewGroup.LayoutParams params = getDialog().getWindow().getAttributes();
params.width = (int) (ScreenUtil.getScreenWith(getContext()) * 0.9);
params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
getDialog().getWindow().setAttributes((android.view.WindowManager.LayoutParams) params);
}
}
动态权限

高版别必需求动态处理权限,这儿在发动界面恳求了一些权限,但引荐在用到的时分才获取,写法差不多,这儿运用第三方结构完成,当然也能够直接运用体系API完成。
/** * 权限授权了就会调用该办法 * 恳求相机权限意图是扫描二维码,摄影 */ @NeedsPermission({ Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION }) void onPermissionGranted() { //假如有权限就进入下一步 prepareNext(); } /** * 显现权限授权对话框 * 意图是提示用户 */ @OnShowRationale({ Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION }) void showRequestPermission(PermissionRequest request) { new AlertDialog.Builder(getHostActivity()) .setMessage(R.string.permission_hint) .setPositiveButton(R.string.allow, (dialog, which) -> request.proceed()) .setNegativeButton(R.string.deny, (dialog, which) -> request.cancel()).show(); } /** * 拒绝了权限调用 */ @OnPermissionDenied({ Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION }) void showDenied() { //退出运用 finish(); } /** * 再次获取权限的提示 */ @OnNeverAskAgain({ Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION }) void showNeverAsk() { //持续恳求权限 checkPermission(); } /** * 授权后回调 * * @param requestCode * @param permissions * @param grantResults */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); //将授权成果传递到结构 SplashActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults); }
引导界面

/**
* 引导界面适配器
*/
public class GuideAdapter extends BaseFragmentStatePagerAdapter<Integer> {
/***
* @param context 上下文
* @param fm Fragment管理器
*/
public GuideAdapter(Context context, @NonNull FragmentManager fm) {
super(context, fm);
}
/**
* 回来当时方位Fragment
*
* @param position
* @return
*/
@NonNull
@Override
public Fragment getItem(int position) {
return GuideFragment.newInstance(getData(position));
}
}
/**
* 引导界面Fragment
*/
public class GuideFragment extends BaseViewModelFragment<FragmentGuideBinding> {
...
@Override
protected void initDatum() {
super.initDatum();
int data = getArguments().getInt(Constant.ID);
binding.icon.setImageResource(data);
}
}
广告界面


完成图片广告和视频广告,广告数据是在主页是缓存到本地,意图是在发动界面加载更快,由于实在项目中,大部分项目发动页面广告时刻总共就5秒,假如龚俊太长了approve用户体会欠好json数据,假如是从网络恳求,那么网络或许就耗时2秒左右,所以导致就美哟多少时刻显现广告了。
下载广告
private void downloadAd(Ad data) {
if (SuperNetworkUtil.isWifiConnected(getHostActivity())) {
//wifi才下载
sp.setSplashAd(data);
//判别文件是否存在,假如存在就不下载
File targetFile = FileUtil.adFile(getHostActivity(), data.getIcon());
if (targetFile.exists()) {
return;
}
new Thread(
new Runnable() {
@Override
public void run() {
try {
//FutureTarget会堵塞
//所以需求在子线程调用
FutureTarget<File> target = Glide.with(getHostActivity().getApplicationContext())
.asFile()
.load(ResourceUtil.resourceUri(data.getIcon()))
.submit();
//获取下载的文件
File file = target.get();
//将文件拷贝到咱们需求的方位
FileUtils.moveFile(file, targetFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}
).start();
}
}
显现广告
/** * 显现视频广告 * * @param data */ private void showVideoAd(File data) { SuperViewUtil.show(binding.video); SuperViewUtil.show(binding.preload); //在要用到的时分在初始化,更节省资源,当然播映器控件也能够在这儿动态创立 //设置播映监听器 //创立 player 目标 player = new TXVodPlayer(getHostActivity()); //静音,当然也能够在界面上增加静音切换按钮 player.setMute(true); //要害 player 目标与界面 view player.setPlayerView(binding.video); //设置播映监听器 player.setVodListener(this); //铺满 binding.video.setRenderMode(TXLiveConstants.RENDER_MODE_FULL_FILL_SCREEN); //敞开硬件加速 player.enableHardwareDecode(true); player.startPlay(data.getAbsolutePath()); }
显现图片便是显现本地图片了,没什么难点,就不贴代码了。
主页/歌单概况/黑胶唱片界面

主页没有顶部是轮播图json,然后是能够左右的菜单,接下来json怎么读是热门歌单,引荐单曲,终究是主页排序模块;全体上运用RecycerView完成,轮播图:
Banner bannerView = holder.getView(R.id.banner);
BannerImageAdapter<Ad> bannerImageAdapter = new BannerImageAdapter<Ad>(data.getData()) {
@Override
public void onBindView(BannerImageHolder holder, Ad data, int position, int size) {
ImageUtil.show(getContext(), (ImageView) holder.itemView, data.getIcon());
}
};
bannerView.setAdapter(bannerImageAdapter);
bannerView.setOnBannerListener(onBannerListener);
bannerView.setBannerRound(DensityUtil.dip2px(getContext(), 10));
//增加生命周期观察者
bannerView.addBannerLifecycleObserver(fragment);
bannerView.setIndicator(new CircleIndicator(getContext()));
引荐歌单
//设置标题,将标题放到每个具体的item上,优点是便利全体排序 holder.setText(R.id.title, R.string.recommend_sheet); //显现更多容器 holder.setVisible(R.id.more, true); holder.getView(R.id.more).setOnClickListener(v -> { }); RecyclerView listView = holder.getView(R.id.list); if (listView.getAdapter() == null) { //设置显现3列 GridLayoutManager layoutManager = new GridLayoutManager(listView.getContext(), 3); listView.setLayoutManager(layoutManager); sheetAdapter = new SheetAdapter(R.layout.item_sheet); //item点击 sheetAdapter.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(@NonNull BaseQuickAdapter<?, ?> adapter, @NonNull View view, int position) { if (discoveryAdapterListener != null) { discoveryAdapterListener.onSheetClick((Sheet) adapter.getItem(position)); } } }); listView.setAdapter(sheetAdapter); GridDividerItemDecoration itemDecoration = new GridDividerItemDecoration(getContext(), (int) DensityUtil.dip2px(getContext(), 5F)); listView.addItemDecoration(itemDecoration); } sheetAdapter.setNewInstance(data.getData());
歌单概况
顶部是歌单二维码防伪信息,经过header完成艺术漆,底部是列表,显现歌单内容的音乐,点击音乐进入黑胶唱片播映界二维码扫描面。
//增加头部 adapter.addHeaderView(createHeaderView());
/** * 显现数据的办法 * * @param holder * @param data */ @Override protected void convert(@NonNull BaseViewHolder holder, Song data) { //显现方位 holder.setText(R.id.index, String.valueOf(holder.getLayoutPosition() + offset)); //显现标题 holder.setText(R.id.title, data.getTitle()); //显现信息 holder.setText(R.id.info, data.getSinger().getNickname()); if (offset != 0) { holder.setImageResource(R.id.more, R.drawable.close); holder.getView(R.id.more) .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SuperDialog.newInstance(fragmentManager) .setTitleRes(R.string.confirm_delete) .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //查询下载使命 DownloadInfo downloadInfo = AppContext.getInstance().getDownloadManager().getDownloadById(data.getId()); if (downloadInfo != null) { //从下载结构删去 AppContext.getInstance().getDownloadManager().remove(downloadInfo); } else { AppContext.getInstance().getOrm().deleteSong(data); } //从适配器中删去 removeAt(holder.getAdapterPosition()); } }).show(); } }); } else { //是否下载 DownloadInfo downloadInfo = AppContext.getInstance().getDownloadManager().getDownloadById(data.getId()); if (downloadInfo != null && downloadInfo.getStatus() == DownloadInfo.STATUS_COMPLETED) { //下载完结了 //显现下载完结了图标 holder.setGone(R.id.download, false); } else { holder.setGone(R.id.download, true); } } //处理修改状况 if (isEditing()) { holder.setVisible(R.id.index, false); holder.setVisible(R.id.check, true); holder.setVisible(R.id.more, false); if (isSelected(holder.getLayoutPosition())) { holder.setImageResource(R.id.check, R.drawable.ic_checkbox_selected); } else { holder.setImageResource(R.id.check, R.drawable.ic_checkbox); } } else { holder.setVisible(R.id.index, true); holder.setVisible(R.id.check, false); holder.setVisible(R.id.more, true); } }
黑胶工龄越长退休金越多吗唱片
上面是黑胶唱片,和网易云音乐差不多,随龚俊着音乐翻滚或暂停,顶部是操控相关,音乐播映逻辑是封装到MusicPlay艺术签名erManager中:
/** * 播映管理器默许完成 */ public class MusicPlayerManagerImpl implements MusicPlayerManager, MediaPlayer.OnCompletionListener, AudioManager.OnAudioFocusChangeListener { ... /** * 获取播映管理器 * getInstance:办法名能够随意取 * 只是在Java这边大部分项目都取这个姓名 * * @return */ public synchronized static MusicPlayerManager getInstance(Context context) { if (instance == null) { instance = new MusicPlayerManagerImpl(context); } return instance; } @Override public void play(String uri, Song data) { //保存信息 this.uri = uri; this.data = data; //开释播映器 player.reset(); //获取音频焦点 if (!requestAudioFocus()) { return; } playNow(); } private void playNow() { isPrepare = true; try { if (uri.startsWith("content://")) { //内容供给者格式 //本地音乐 //uri示例:content://media/external/audio/media/23 player.setDataSource(context, Uri.parse(uri)); } else { //设置数据源 player.setDataSource(uri); } //同步准备 //实在项目中或许会运用异步 //由于假如网络欠好 //同步或许会卡住 player.prepare(); // player.prepareAsync(); //开端播映器 player.start(); //回调监听器 publishPlayingStatus(); //发动播映进展告诉 startPublishProgress(); prepareLyric(data); } catch (IOException e) { //TODO 播映过错处理 } } @Override public void pause() { if (isPlaying()) { //假如在播映就暂停 player.pause(); ListUtil.eachListener(listeners, musicPlayerListener -> musicPlayerListener.onPaused(data)); stopPublishProgress(); } } @Override public void resume() { if (!isPlaying()) { //获取音频焦点 if (!requestAudioFocus()) { return; } resumeNow(); } } private void resumeNow() { //假如没有播映就播映 player.start(); //回调监听器 publishPlayingStatus(); //发动进展告诉 startPublishProgress(); } @Override public void addMusicPlayerListener(MusicPlayerListener listener) { if (!listeners.contains(listener)) { listeners.add(listener); } //发动进展告诉 startPublishProgress(); } @Override public void removeMusicPlayerListener(MusicPlayerListener listener) { listeners.remove(listener); } @Override public void seekTo(int progress) { player.seekTo(progress); } /** * 发布播映中状况 */ private void publishPlayingStatus() { // for (MusicPlayerListener listener : listeners) { // listener.onPlaying(data); // } //运用重构后的办法 ListUtil.eachListener(listeners, musicPlayerListener -> musicPlayerListener.onPlaying(data)); } /** * 播映完毕了回调 * * @param mp */ @Override public void onCompletion(MediaPlayer mp) { isPrepare = false; //回调监听器 ListUtil.eachListener(listeners, listener -> listener.onCompletion(mp)); } @Override public void setLooping(boolean looping) { player.setLooping(looping); } /** * 音频焦点改变了回调 * * @param focusChange */ @Override public void onAudioFocusChange(int focusChange) { Timber.d("onAudioFocusChange %s", focusChange); switch (focusChange) { case AudioManager.AUDIOFOCUS_GAIN: //获取到焦点了 if (resumeOnFocusGain) { if (isPrepare) { resumeNow(); } else { playNow(); } resumeOnFocusGain = false; } break; case AudioManager.AUDIOFOCUS_LOSS: //永久失掉焦点,例如:其他运用恳求时,也是播映音乐 if (isPlaying()) { pause(); } break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: //暂时性失掉焦点,例如:通话了,或许呼叫了语音帮手等恳求 if (isPlaying()) { resumeOnFocusGain = true; pause(); } break; } } }
音乐列表逻appreciate辑封装到MusicL艺术活动istManager:
public class MusicListManagerImpl implements MusicListManager, MusicPlayerListener {
@Override
public void setDatum(List<Song> datum) {
//将本来数据playList标志设置为false
DataUtil.changePlayListFlag(this.datum, false);
//保存到数据库
saveAll();
//清空本来的数据
this.datum.clear();
//增加新的数据
this.datum.addAll(datum);
//更改播映列表标志
DataUtil.changePlayListFlag(this.datum, true);
//保存到数据库
saveAll();
sendPlayListChangedEvent(0);
}
/**
* 保存播映列表
*/
private void saveAll() {
getOrm().saveAll(datum);
}
private LiteORMUtil getOrm() {
return LiteORMUtil.getInstance(this.context);
}
@Override
public void play(Song data) {
//当时音乐黑胶唱片翻滚
data.setRotate(true);
//符号已经播映了
isPlay = true;
//保存数据
this.data = data;
if (StringUtils.isNotBlank(data.getPath())) {
//本地音乐
//不拼接地址
musicPlayerManager.play(data.getPath(), data);
} else {
//判别是否有下载目标
DownloadInfo downloadInfo = AppContext.getInstance().getDownloadManager().getDownloadById(data.getId());
if (downloadInfo != null && downloadInfo.getStatus() == DownloadInfo.STATUS_COMPLETED) {
//下载完结了
//播映本地音乐
musicPlayerManager.play(downloadInfo.getPath(), data);
Timber.d("play offline %s %s %s", data.getTitle(), downloadInfo.getPath(), data.getUri());
} else {
//播映在线音乐
String path = ResourceUtil.resourceUri(data.getUri());
musicPlayerManager.play(path, data);
Timber.d("play online %s %s", data.getTitle(), path);
}
}
//设置终究播映音乐的Id
sp.setLastPlaySongId(data.getId());
}
@Override
public void pause() {
musicPlayerManager.pause();
}
@Override
public Song next() {
if (datum.size() == 0) {
//假如没有音乐了
//直接回来null
return null;
}
//音乐索引
int index = 0;
//判别循环形式
switch (model) {
case MODEL_LOOP_RANDOM:
//随机循环
//在0~datum.size()中
//不包括datum.size()
index = new Random().nextInt(datum.size());
break;
default:
//找到当时音乐索引
index = datum.indexOf(data);
if (index != -1) {
//找到了
//假如当时播映是列表终究一个
if (index == datum.size() - 1) {
//终究一首音乐
//那就从0开端播映
index = 0;
} else {
index++;
}
} else {
//抛出反常
//由于正常状况下是能找到的
throw new IllegalArgumentException("Cant'found current song");
}
break;
}
return datum.get(index);
}
@Override
public void delete(int position) {
//获取要删去的音乐
Song song = datum.get(position);
if (song.getId().equals(data.getId())) {
//删去的音乐便是当时播映的音乐
//应该中止当时播映
pause();
//并播映下一首音乐
Song next = next();
if (next.getId().equals(data.getId())) {
//找到了自己
//没有歌曲能够播映了
data = null;
//TODO Bug 随机循环的状况下有或许获取到自己
} else {
play(next);
}
}
//直接删去
datum.remove(song);
//从数据库中删去
getOrm().deleteSong(song);
sendPlayListChangedEvent(position);
}
private void sendPlayListChangedEvent(int position) {
EventBus.getDefault().post(new MusicPlayListChangedEvent(position));
}
/**
* 播映完毕了回调
*
* @param mp
*/
@Override
public void onCompletion(MediaPlayer mp) {
if (model == MODEL_LOOP_ONE) {
//假如是单曲循环
//就不会处理了
//由于咱们运用了MediaPlayer的循环形式
//假如运用的第三方结构
//假如没有循环形式
//那就要在这儿持续播映当时音乐
} else {
Song data = next();
if (data != null) {
play(data);
}
}
}
...
}
外界一致运用播映列表管理器播映音乐,上一曲下一曲:
//播映按钮点击 binding.play.setOnClickListener(v -> { playOrPause(); }); //下一曲按钮点击 binding.next.setOnClickListener(v -> { getMusicListManager().play(getMusicListManager().next()); }); //播映列表按钮点击 binding.listButton.setOnClickListener(v -> { MusicPlayListDialogFragment.show(getSupportFragmentManager()); });
媒体操控器/桌面歌词/艺术字体转换器在线转换器桌面Widget

private void showLyricData() { binding.lyricList.setData(getMusicListManager().getData().getParsedLyric()); }
桌面歌词运用两个LyricView显现两行歌词,桌面歌词运用的是大局悬浮窗API艺术漆,所以要先判别是否有权限,没宫颈癌有需求先获取权限,json格式怎么打开然后才干显现,封装到GlobalLyricManagerImpl中:
/** * 大局(桌面)歌词管理器完成 */ public class GlobalLyricManagerImpl implements GlobalLyricManager, MusicPlayerListener, GlobalLyricView.OnGlobalLyricDragListener, GlobalLyricView.GlobalLyricListener { public GlobalLyricManagerImpl(Context context) { this.context = context.getApplicationContext(); //初始化偏好设置东西类 sp = PreferenceUtil.getInstance(this.context); //初始化音乐播映管理器 musicPlayerManager = MusicPlayerService.getMusicPlayerManager(this.context); //增加播映监听器 musicPlayerManager.addMusicPlayerListener(this); //初始化窗口管理器 initWindowManager(); //从偏好设置中获取是否要显现大局歌词 if (sp.isShowGlobalLyric()) { //创立大局歌词View initGlobalLyricView(); //假如本来确定了歌词 if (sp.isGlobalLyricLock()) { //确定歌词 lock(); } } } public synchronized static GlobalLyricManagerImpl getInstance(Context context) { if (instance == null) { instance = new GlobalLyricManagerImpl(context); } return instance; } /** * 确定大局歌词 */ private void lock() { //保存大局歌词确定状况 sp.setGlobalLyricLock(true); //设置大局歌词控件状况 setGlobalLyricStatus(); //显现简略形式 globalLyricView.simpleStyle(); //更新布局 updateView(); //显现解锁大局歌词告诉 NotificationUtil.showUnlockGlobalLyricNotification(context); //注册接纳解锁大局歌词广告接纳器 registerUnlockGlobalLyricReceiver(); } /** * 注册接纳解锁大局歌词广告接纳器 */ private void registerUnlockGlobalLyricReceiver() { if (unlockGlobalLyricBroadcastReceiver == null) { //创立播送接受者 unlockGlobalLyricBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Constant.ACTION_UNLOCK_LYRIC.equals(intent.getAction())) { //歌词解锁事情 unlock(); } } }; IntentFilter intentFilter = new IntentFilter(); //只监听歌词解锁事情 intentFilter.addAction(Constant.ACTION_UNLOCK_LYRIC); //注册 context.registerReceiver(unlockGlobalLyricBroadcastReceiver, intentFilter); } } /** * 解锁歌词 */ private void unlock() { //设置没有确定歌词 sp.setGlobalLyricLock(false); //设置歌词状况 setGlobalLyricStatus(); //解锁后显现标准款式 globalLyricView.normalStyle(); //更新view updateView(); //清除歌词解锁告诉 NotificationUtil.clearUnlockGlobalLyricNotification(context); //免除接纳大局歌词事情播送接受者 unregisterUnlockGlobalLyricReceiver(); } /** * 免除接纳大局歌词事情播送接受者 */ private void unregisterUnlockGlobalLyricReceiver() { if (unlockGlobalLyricBroadcastReceiver != null) { context.unregisterReceiver(unlockGlobalLyricBroadcastReceiver); unlockGlobalLyricBroadcastReceiver = null; } } @Override public void show() { //检查大局悬浮窗权限 if (!Settings.canDrawOverlays(context)) { Intent intent = new Intent(context, SplashActivity.class); intent.setAction(Constant.ACTION_LYRIC); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); return; } //初始化大局歌词控件 initGlobalLyricView(); //设置显现了大局歌词 sp.setShowGlobalLyric(true); WidgetUtil.onGlobalLyricShowStatusChanged(context, isShowing()); } private boolean hasGlobalLyricView() { return globalLyricView != null; } /** * 大局歌词拖拽回调 * * @param y y轴方向上移动的间隔 */ @Override public void onGlobalLyricDrag(int y) { layoutParams.y = y - SizeUtil.getStatusBarHeight(context); //更新view updateView(); //保存歌词y坐标 sp.setGlobalLyricViewY(layoutParams.y); } ... }
显现艺术字和躲藏只json格式需求调用Go该管理器的相关办法就行了。
媒体操控器jsonobject
运用了能够经过体系媒体操控器,告诉栏,锁屏界面,耳机,蓝牙耳机艺术字体转换器在线转换器等设备操控媒体播映暂停,只需求把媒体信息更新到体系:json格式怎么打开
MusicPlayerServiapplece
/** * 更新媒体信息 * * @param data * @param icon */ public void updateMetaData(Song data, Bitmap icon) { MediaMetadataCompat.Builder metaData = new MediaMetadataCompat.Builder() //标题 .putString(MediaMetadataCompat.METADATA_KEY_TITLE, data.getTitle()) //艺术家,也便是歌手 .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, data.getSinger().getNickname()) //专辑 .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, "专辑") //专辑艺术家 .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, "专辑艺术家") //时长 .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, data.getDuration()) //封面 .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, icon); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //播映列表长度 metaData.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, musicListManager.getDatum().size()); } mediaSession.setMetadata(metaData.build()); }
接纳媒体操控
/** * 媒体回调 */ private MediaSessionCompat.Callback callback = new MediaSessionCompat.Callback() { @Override public void onPlay() { musicListManager.resume(); } @Override public void onPause() { musicListManager.pause(); } @Override public void onSkipToNext() { musicListManager.play(musicListManager.next()); } @Override public void onSkipToPrevious() { musicListManager.play(musicListManager.previous()); } @Override public void onSeekTo(long pos) { musicListManager.seekTo((int) pos); } };
桌面艺术设计专业Widget
创立布局,然后注册,终究便是更新信息:
public class MusicWidget extends AppWidgetProvider {
/**
* 增加,从头运行运用,周期时刻,都会调用
*
* @param context
* @param appWidgetManager
* @param appWidgetIds
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
//测验发动service
ServiceUtil.startService(context.getApplicationContext(), MusicPlayerService.class);
//获取播映列表管理器
MusicListManager musicListManager = MusicPlayerService.getListManager(context.getApplicationContext());
//获取当时播映的音乐
final Song data = musicListManager.getData();
final int N = appWidgetIds.length;
// 循环处理每一个,由于桌面上或许增加多个
for (int i = 0; i < N; i++) {
int appWidgetId = appWidgetIds[i];
// 创立长途控件,所有对view的操作都必须经过该view供给的办法
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.music_widget);
//由于这是在桌面的控件里边显现咱们的控件,所以不能直接经过setOnClickListener设置监听器
//这儿发送的动作在MusicReceiver处理
PendingIntent iconPendingIntent = IntentUtil.createMainActivityPendingIntent(context, Constant.ACTION_MUSIC_PLAYER_PAGE);
//这儿直接发动service,也能够用播送接纳
PendingIntent previousPendingIntent = IntentUtil.createMusicPlayerServicePendingIntent(context, Constant.ACTION_PREVIOUS);
PendingIntent playPendingIntent = IntentUtil.createMusicPlayerServicePendingIntent(context, Constant.ACTION_PLAY);
PendingIntent nextPendingIntent = IntentUtil.createMusicPlayerServicePendingIntent(context, Constant.ACTION_NEXT);
PendingIntent lyricPendingIntent = IntentUtil.createMusicPlayerServicePendingIntent(context, Constant.ACTION_LYRIC);
//设置点击事情
views.setOnClickPendingIntent(R.id.icon, iconPendingIntent);
views.setOnClickPendingIntent(R.id.previous, previousPendingIntent);
views.setOnClickPendingIntent(R.id.play, playPendingIntent);
views.setOnClickPendingIntent(R.id.next, nextPendingIntent);
views.setOnClickPendingIntent(R.id.lyric, lyricPendingIntent);
if (data == null) {
//当时没有播映音乐
appWidgetManager.updateAppWidget(appWidgetId, views);
} else {
//有播映音乐
views.setTextViewText(R.id.title, String.format("%s - %s", data.getTitle(), data.getSinger().getNickname()));
views.setProgressBar(R.id.progress, (int) data.getDuration(), (int) data.getProgress(), false);
//显现图标
RequestOptions options = new RequestOptions();
options.centerCrop();
Glide.with(context)
.asBitmap()
.load(ResourceUtil.resourceUri(data.getIcon()))
.apply(options)
.into(new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
//显现封面
views.setImageViewBitmap(R.id.icon, resource);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
//显现默许图片
views.setImageViewBitmap(R.id.icon, BitmapFactory.decodeResource(context.getResources(), R.drawable.placeholder));
appWidgetManager.updateAppWidget(appWidgetId, views);
}
});
}
}
}
}
登录/注册/验证码登录

登录注册没有多大难度,用户名和暗码登录,便是把信息传递到服务端,能够加密后在传输,服务端判别登录成功,回来一个符号,客户端保存,其他需求的登录的接口带上;验证码登录便是用验证码替代暗码,发送验证码都是服务端发送,客户端只需求调用接口。
谈论

下拉刷新和下拉加载更多
中心逻辑就只需求更改page就行了
//下拉刷新监听器 binding.refresh.setOnRefreshListener(new OnRefreshListener() { @Override public void onRefresh(RefreshLayout refreshlayout) { loadData(); } }); //上拉加载更多 binding.refresh.setOnLoadMoreListener(new OnLoadMoreListener() { @Override public void onLoadMore(RefreshLayout refreshlayout) { loadMore(); } }); @Override protected void loadData(boolean isPlaceholder) { super.loadData(isPlaceholder); isRefresh = true; pageMeta = null; loadMore(); }
提醒人二维码和论题点击
经过正则表达式,找到特殊文本,然后运用富文本完成点击。
holder.setText(R.id.content, processContent(data.getContent())); /** * 处理文本点击事情 * 这部分能够用监听器回调到Activity中处理 * * @param content * @return */ private SpannableString processContent(String content) { //设置点击事情 SpannableString result = RichUtil.processContent(getContext(), content, new RichUtil.OnTagClickListener() { @Override public void onTagClick(String data, RichUtil.MatchResult matchResult) { String clickText = RichUtil.removePlaceholderString(data); Timber.d("processContent mention click %s", clickText); UserDetailActivity.startWithNickname(getContext(), clickText); } }, (data, matchResult) -> { String clickText = RichUtil.removePlaceholderString(data); Timber.d("processContent hash tag %s", clickText); }); //回来成果 return result; }
挑选老友
对数据分组,然后显现右jsonobject侧索引,挑选了经过EventBus发送到谈论界面。
adapter.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(@NonNull BaseQuickAdapter<?, ?> adapter, @NonNull View view, int position) { Object data = adapter.getItem(position); if (data instanceof User) { if (Constant.STYLE_FRIEND_SELECT == style) { EventBus.getDefault().post(new SelectedFriendEvent((User) data)); //封闭界面 finish(); } else { startActivityExtraId(UserDetailActivity.class, ((User) data).getId()); } } } }); }
视频和appstore播映

实在项艺术漆目中视频播映大部分都是用第三方服务,例如:阿里云视频服务,腾讯视频服务,由于他们供给一条龙服务,包括审核,转码,CDN,安全,播映器等,这儿用不到这么多功用,所以运用了第三方播映器播映普通mp4,这运用饺子播映器结构。
GSYVideoOptionBuilder videoOption = new GSYVideoOptionBuilder(); videoOption // .setThumbImageView(imageView) //小屏时不接触滑动 .setIsTouchWiget(false) //音频焦点抵触时是否开释 .setReleaseWhenLossAudio(true) .setRotateViewAuto(false) .setLockLand(false) .setAutoFullWithSize(true) .setSeekOnStart(seek) .setNeedLockFull(true) .setUrl(ResourceUtil.resourceUri(data.getUri())) .setCacheWithPlay(false) //全屏切换时不运用动画 .setShowFullAnimation(false) .setVideoTitle(data.getTitle()) //设置右下角 显现切换到全屏 的按键资源 .setEnlargeImageRes(R.drawable.full_screen) //设置右下角 显现退出全屏 的按键资源 .setShrinkImageRes(R.drawable.normal_screen) .setVideoAllCallBack(new GSYSampleCallBack() { @Override public void onPrepared(String url, Object... objects) { super.onPrepared(url, objects); //开端播映了才干旋转和全屏 orientationUtils.setEnable(true); isPlay = true; } @Override public void onQuitFullscreen(String url, Object... objects) { super.onQuitFullscreen(url, objects); if (orientationUtils != null) { orientationUtils.backToProtVideo(); } } }).setLockClickListener(new LockClickListener() { @Override public void onClick(View view, boolean lock) { if (orientationUtils != null) { //合作下方的onConfigurationChanged orientationUtils.setEnable(!lock); } } }).build(binding.player); //开端播映 binding.player.startPlayLogic();
用户概况/更改材料

用户概况顶部显现用户信息,老友数量,下面别离显现创立的歌单,保藏的歌单,发json格式布的动appear态,相似微信朋友二维码支付代理圈,右上角能够更改用户材料;全体采用CoordinatorLayo艺术字体转换器在线转换器ut+TabLayo二维码ut+ViewPager+Fragment完成。
public Fragment getItem(int position) { switch (position) { case 0: return UserDetailSheetFragment.newInstance(userId); case 1: return FeedFragment.newInstance(userId); default: return UserDetailAboutFragment.newInstance(userId); } } /** * 回来标题 * * @param position * @return */ @Nullable @Override public CharSequence getPageTitle(int position) { //获取字符串id int resourceId = titleIds[position]; //获取字符串 return context.getResources().getString(resourceId); }
发布动态/挑选方位/途径规划

挑选方位
/**
* 查找该方位的poi,便利用户挑选,也便利其他人找
* Point Of Interest,兴趣点)
*/
private void searchPOI(LatLng data, String keyword) {
try {
Timber.d("searchPOI %s %s", data, keyword);
binding.progress.setVisibility(View.VISIBLE);
adapter.setNewInstance(new ArrayList<>());
// 第一个参数表明一个Latlng,第二参数表明范围多少米,第三个参数表明是火系坐标系仍是GPS原生坐标系
// val query = RegeocodeQuery(
// LatLonPoint(data.latitude, data.longitude)
// , 1000F, GeocodeSearch.AMAP
// )
//
// geocoderSearch.getFromLocationAsyn(query)
//keyWord表明查找字符串,
//第二个参数表明POI查找类型,二者选填其一,选用POI查找类型时主张填写类型代码,码表能够参阅下方(而非文字)
//cityCode表明POI查找区域,能够是城市编码也能够是城市称号,也能够传空字符串,空字符串代表全国在全国范围内进行查找
PoiSearch.Query query = new PoiSearch.Query(keyword, "");
query.setPageSize(10); // 设置每页最多回来多少条poiitem
query.setPageNum(0); //设置查询页码
PoiSearch poiSearch = new PoiSearch(this, query);
poiSearch.setOnPoiSearchListener(this);
//设置周边查找的中心点以及半径
if (data != null) {
poiSearch.setBound(new PoiSearch.SearchBound(
new LatLonPoint(
data.latitude,
data.longitude
), 1000
));
}
poiSearch.searchPOIAsyn();
} catch (Exception e) {
e.printStackTrace();
}
}
高德地图途径规划
/** * 运用高德地图途径规划 * * @param context * @param slat 起点纬度 * @param slon 起点经度 * @param sname 起点称号 可不填(0,0,null) * @param dlat 结尾纬度 * @param dlon 结尾经度 * @param dname 结尾称号 必填 * 官方文档:https://lbs.amap.com/api/amap-mobile/guide/android/route */ public static void openAmapRoute( Context context, double slat, double slon, String sname, double dlat, double dlon, String dname ) { StringBuilder builder = new StringBuilder("amapuri://route/plan?"); //第三方调用运用称号 builder.append("sourceApplication="); builder.append(context.getString(R.string.app_name)); //开端信息 if (slat != 0.0) { builder.append("&sname=").append(sname); builder.append("&slat=").append(slat); builder.append("&slon=").append(slon); } //完毕信息 builder.append("&dlat=").append(dlat) .append("&dlon=").append(dlon) .append("&dname=").append(dname) .append("&dev=0") .append("&t=0"); startActivity(context, Constant.PACKAGE_MAP_AMAP, builder.toString()); }
谈天/离线推送

登录谈天服务器
/** * 衔接谈天服务器 * * @param data */ private void connectChat(Session data) { RongIMClient.connect(data.getChatToken(), new RongIMClient.ConnectCallback() { /** * 成功回调 * @param userId 当时用户 ID */ @Override public void onSuccess(String userId) { Timber.d("connect chat success %s", userId); } /** * 过错回调 * @param errorCode 过错码 */ @Override public void onError(RongIMClient.ConnectionErrorCode errorCode) { Timber.e("connect chat error %s", errorCode); if (errorCode.equals(RongIMClient.ConnectionErrorCode.RC_CONN_TOKEN_INCORRECT)) { //从 APP 服务获取新 token,并重连 } else { //无法衔接 IM 服务器,请根据相应的过错码作出对应处理 } //由于咱们这个运用,不是相似微信那样纯谈天运用,所以谈天服务器衔接失利,也让进入运用 //实在项目中按照需求完成就行了 SuperToast.show(R.string.error_message_login); } /** * 数据库回调. * @param databaseOpenStatus 数据库翻开状况. DATABASE_OPEN_SUCCESS 数据库翻开成功; DATABASE_OPEN_ERROR 数据库翻开失利 */ @Override public void onDatabaseOpened(RongIMClient.DatabaseOpenStatus databaseOpenStatus) { } }); }
设置音讯监听
chatClient.addOnReceiveMessageListener(new OnReceiveMessageWrapperListener() { @Override public void onReceivedMessage(Message message, ReceivedProfile profile) { //该办法的调用不再主线程 Timber.e("chat onReceived %s", message); if (EventBus.getDefault().hasSubscriberForEvent(NewMessageEvent.class)) { //假如有监听该事情,表明在谈天界面,或许会话界面 EventBus.getDefault().post(new NewMessageEvent(message)); } else { handler.obtainMessage(0, message).sendToTarget(); } //发送音讯未读数改变了告诉 EventBus.getDefault().post(new MessageUnreadCountChangedEvent()); } });
发送文本音讯
发送图片等其他音讯也是差不多。
private void sendTextMessage() { String content = binding.input.getText().toString().trim(); if (StringUtils.isEmpty(content)) { SuperToast.show(R.string.hint_enter_message); return; } TextMessage textMessage = TextMessage.obtain(content); RongIMClient.getInstance().sendMessage(Conversation.ConversationType.PRIVATE, targetId, textMessage, null, MessageUtil.createPushData(MessageUtil.getContent(textMessage), sp.getUserId()), new IRongCallback.ISendMessageCallback() { @Override public void onAttached(Message message) { // 音讯成功存到本地数据库的回调 Timber.d("sendTextMessage onAttached %s", message); } @Override public void onSuccess(Message message) { // 音讯发送成功的回调 Timber.d("sendTextMessage success %s", message); //清空输入框 clearInput(); addMessage(message); } @Override public void onError(Message message, RongIMClient.ErrorCode errorCode) { // 音讯发送失利的回调 Timber.e("sendTextMessage onError %s %s", message, errorCode); } }); }
离线推送
先敞开SDK离线推送,还要别离去厂商那儿申请推送装备,这儿只完成了小米推送,其他的华为推送,OPPappstoreO推送等差不多;然后把推送,或许二维码生成器在线点击都一致代理到主界面,然后再处理。
private void postRun(Intent intent) { String action = intent.getAction(); if (Constant.ACTION_CHAT.equals(action)) { //本地显现的音讯告诉点击 //要跳转到谈天界面 String id = intent.getStringExtra(Constant.ID); startActivityExtraId(ChatActivity.class, id); } else if (Constant.ACTION_PUSH.equals(action)) { //谈天告诉点击 String id = intent.getStringExtra(Constant.PUSH); startActivityExtraId(ChatActivity.class, id); } }
商城/订单/付出/购物车json是什么意思


学到这儿,咱们不能说熟悉,那么看到上面的界面,那么大体要能完成出来。
产品概况富文本
//概况
HtmlText.from(data.getDetail())
.setImageLoader(new HtmlImageLoader() {
@Override
public void loadImage(String url, final Callback callback) {
Glide.with(getHostActivity())
.asBitmap()
.load(url)
.into(new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
callback.onLoadComplete(resource);
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
callback.onLoadFailed();
}
});
}
@Override
public Drawable getDefaultDrawable() {
return ContextCompat.getDrawable(getHostActivity(), R.drawable.placeholder);
}
@Override
public Drawable getErrorDrawable() {
return ContextCompat.getDrawable(getHostActivity(), R.drawable.placeholder_error);
}
@Override
public int getMaxWidth() {
return ScreenUtil.getScreenWith(getHostActivity());
}
@Override
public boolean fitWidth() {
return true;
}
})
.setOnTagClickListener(new OnTagClickListener() {
@Override
public void onImageClick(Context context, List<String> imageUrlList, int position) {
// image click
}
@Override
public void onLinkClick(Context context, String url) {
// link click
Timber.d("onLinkClick %s", url);
}
})
.into(binding.detail);
付出
客户端先集成工龄差一年工资差多少微信,付出宝SDK,然后恳求服务端获取付出json文件是干什么的信息,设置到SDK,终究便是处理付出成果。
/** * 处理付出宝付出 * * @param data */ private void processAlipay(String data) { PayUtil.alipay(getHostActivity(), data); } /** * 处理微信付出 * * @param data */ private void processWechat(WechatPay data) { //把服务端回来的参数 //设置到对应的字段 PayReq request = new PayReq(); request.appId = data.getAppid(); request.partnerId = data.getPartnerid(); request.prepayId = data.getPrepayid(); request.nonceStr = data.getNoncestr(); request.timeStamp = data.getTimestamp(); request.packageValue = data.getPackageValue(); request.sign = data.getSign(); AppContext.getInstance().getWxapi().sendReq(request); }
处理付出成果
/** * 付出宝付出状况改变了 * * @param event */ @Subscribe(threadMode = ThreadMode.MAIN) public void onAlipayStatusChanged(AlipayStatusChangedEvent event) { String resultStatus = event.getData().getResultStatus(); if ("9000".equals(resultStatus)) { //本地付出成功 //不能依靠本地付出成果 //必定要以服务端为准 showLoading(R.string.hint_pay_wait); //延时3秒 //由于付出宝回调咱们服务端或许有延迟 binding.primary.postDelayed(() -> { checkPayStatus(); }, 3000); } else if ("6001".equals(resultStatus)) { //付出取消 SuperToast.show(R.string.error_pay_cancel); } else { //付出失利 SuperToast.show(R.string.error_pay_failed); } }
语音辨认输入地址
这儿运用百度语音辨认SDK,先集成,然后初始化,终究是监听辨认成果:
/**
* 百度语音辨认事情监听器
* <p>
* https://ai.baidu.com/ai-doc/SPEECH/4khq3iy52
*/
EventListener voiceRecognitionEventListener = new EventListener() {
/**
* 事情回调
* @param name 回调事情称号
* @param params 回调参数
* @param data 数据
* @param offset 开端方位
* @param length 长度
*/
@Override
public void onEvent(String name, String params, byte[] data, int offset, int length) {
String result = "name: " + name;
if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_READY)) {
// 引擎就绪,能够说话,一般在收到此事情后经过UI告诉用户能够说话了
setStopVoiceRecognition();
} else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL)) {
// 一句话的临时成果,终究成果及语义成果
if (params == null || params.isEmpty()) {
return;
}
// 辨认相关的成果都在这儿
try {
JSONObject paramObject = new JSONObject(params);
//获取第一个成果
JSONArray resultsRecognition = paramObject.getJSONArray("results_recognition");
String voiceRecognitionResult = resultsRecognition.getString(0);
//能够根据result_type是临时成果,仍是终究成果
binding.input.setText(voiceRecognitionResult);
result += voiceRecognitionResult;
} catch (JSONException e) {
e.printStackTrace();
}
} else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_FINISH)) {
//一句话辨认完毕(或许含有过错信息) 。终究辨认的文字成果在ASR_PARTIAL事情中
if (params.contains(""error":0")) {
} else if (params.contains(""error":7")) {
SuperToast.show(R.string.voice_error_no_result);
} else {
//其他过错
SuperToast.show(getString(R.string.voice_error, params));
}
} else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_EXIT)) {
//辨认完毕,资源开释
setStartVoiceRecognition();
}
Timber.d("baidu voice recognition onEvent %s", result);
}
};
百度OCR
运用百度OCR从图片中辨认文本,主要是辨认地址,相似顺丰大众号输入地址时辨认功用。
private void recognitionImage(String data) {
GeneralBasicParams param = new GeneralBasicParams();
param.setDetectDirection(true);
param.setImageFile(new File(data));
// 调用通用文字辨认服务
OCR.getInstance(getApplicationContext()).recognizeGeneralBasic(param, new OnResultListener<GeneralResult>() {
/**
* 成功
* @param result
*/
@Override
public void onResult(GeneralResult result) {
StringBuilder builder = new StringBuilder();
for (WordSimple it : result.getWordList()) {
builder.append(it.getWords());
//每一项之间,增加空格,便利OCR失利
builder.append(" ");
}
binding.input.setText(builder.toString());
}
/**
* 失利
* @param error
*/
@Override
public void onError(OCRError error) {
SuperToast.show(getString(R.string.ocr_error, error.getMessage(), error.getErrorCode()));
}
});
}
还有一些功用,例如:快捷枸杞方式二维码是谁发明等就不在贴代码了。
评论(0)