Commit 398aff6b authored by qiujianhui's avatar qiujianhui

create haptic demo and README.md

parent a6412477
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
# Default ignored files
/shelf/
/workspace.xml
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="Android Studio default JDK" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ASMIdeaPluginConfiguration">
<asm skipDebug="false" skipFrames="false" skipCode="false" expandFrames="false" />
<groovy codeStyle="LEGACY" />
</component>
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
\ No newline at end of file
HapticDemo
\ No newline at end of file
# 一、概述
HapticPlayer是一套振动波形的生成框架,可以通过参数或者HE格式为你的线性马达机器定制出丰富的振动效果。
# 二、支持设备
Xiaomi-高压线性马达设备
如小米9 pro 5G,小米10全系列(小米10、小米10pro等),小米11全系列(小米11、小米11pro等),小米12全系列(小米12、小米12s、小米12s ultra等),小米mix4,小米mix fold,红米K系列(红米K30S至尊纪念版、K40全系列、K50全系列),红米Note系列(红米Note10 pro、红米Note11全系列)
# 三、API接口
#### android.os.DynamicEffect
| 接口 | 说明 |
| --------------------------------- | ------------------------------------------------------------ |
| DynamicEffect create(String json) | 系统实现一种扩展的振动效果android.os.DynamicEffect。其中create为静态方法,参数为振动效果描述的json字符串,用来创建DynamicEffect。 |
#### android.os.HapticPlayer
HapticPlayer为振动接口的封装(注:带\*的接口系统侧可能会不支持,调用后系统侧不会进行处理)
| 接口 | 说明 |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| HapticPlayer (DynamicEffect effect) | 构造方法,effect为创建的振动效果DynamicEffect |
| boolean isAvailable() | 判断系统是否支持 |
| void start(int loop) | 开始播放效果@param: loop 循环次数, 1不循环,大于1循环次数,-1无限循环; |
| void start(int loop, int interval, int amplitude) | 开始播放效果@param loop 循环次数, 1不循环,大于1循环次数,-1无限循环;@param interval 循环间隔, 0-1000, 每次震动循环播放的间隔,单位ms;@param amplitude 振动强度,1-255,1最小,255最大。此参数用于修饰HE文件,进行整体的强度信号调整/缩减; |
| void start(int loop, int interval, int amplitude, int freq) | 开始播放效果@param loop 循环次数, 1不循环,大于1循环次数,-1无限循环;@param interval 循环间隔, 0-1000, 每次震动循环播放的间隔,单位ms;@param amplitude 振动强度, 1-255,1最小,255最大。此参数用于修饰HE文件,进行整体的强度信号调整/缩减;@param freq 振动频率, 此参数用于修饰HE文件,进行整体的频率信号进行调整; |
| *void updateInterval(int interval) | 更新播放效果循环的时间间隔@param interval 循环间隔,interval取值区间[0,1000],单位ms |
| *void updateAmplitude(int amplitude) | 更新播放效果的振动强度,对整体效果的强度信号进行放大、缩小@param amplitude 振动强度,amplitude取值区间[1,255] |
| *void updateFrequency(int freq) | 更新播放效果的频率,对整体效果的频率信号进行放大、缩小@param freq 振动频率 |
| *void updateParameter(int interval, int amplitude, int freq) | 更新播放效果相关参数interval\|amplitude == -1时,表示不更新@param interval 循环间隔@param amplitude 振动强度@param freq 振动频率, 预留 |
| public void stop() | 停止播放振动效果 |
#### 调用示例
```
if (HapticPlayer.isAvailable()) {
DynamicEffect effect = DynamicEffect.create("{xxx: xxxx}");
HapticPlayer player = new HapticPlayer(effect);
player.start(0);
player.stop();
}
```
#### HE文件格式
```
{
"Metadata": {
"Version": 1, // 版本号,整形
"Created": "2020-07-08", // 创建时间,String类型
"Description": "game haptic" // 震动效果描述,String类型
},
"Pattern":
[
{
"Event": {
"Type": "continuous", // 事件类型: continuous->持续震动。transient->简短震动
"RelativeTime": 0, // 相对开始时间, 整形, 单位ms
"Duration": 300, // 持续震动类型参数:持续时间。整形, 单位ms
"Parameters": {
"Intensity": 80, // 震动强度, 整形, [0,100]。0->平台支持的最小值, 100->平台支持的最大值。
"Frequency": 50, // 震动频率, 整形, [0,100]。0->平台支持的最小值, 100->平台支持的最大值。
"Curve": [ // 持续震动类型参数:曲线。实现上保证平滑过渡效果
{"Time": 0,"Intensity": 0, "Frequency": 25}, // 起始点,必须。time为RelativeTime,Intensity必须取值为0。
{"Time": 100, "Intensity": 0.7, "Frequency": -30},
{"Time": 200, "Intensity": 0.75, "Frequency": 90},
{"Time": 300,"Intensity": 0, "Frequency": 50} // 结束点,必须。time为Duration,Intensity必须取值为0。
]
}
}
},
{
"Event": {
"Type": "transient", // 事件类型: continuous->持续震动, transient->简短震动
"RelativeTime": 400, // 相对开始时间, 整形, 单位ms
"Parameters": {
"Intensity": 80, // 震动强度, 整形, [0,100]。0->平台支持的最小值, 100->平台支持的最大值。
"Frequency": 40 // 震动频率, 整形, [0,100]。0->平台支持的最小值, 100->平台支持的最大值。
}
}
}
]
}
```
详细说明如下:
1)He文件为json格式,包含“Metadata”字典和“Pattern”数组两部分内容;
2)Metadata字段包括振动效果的版本“Version”,创建时间“Created”,描述“Description”等基本信息;
3)具体的振动效果由“Pattern”字段描述,“Pattern”字段内容是一个“Event”数组,每个“Event”描述一个振动效果片段,不重叠;
4)“Event”效果片段
“Type”描述振动效果类型。包含两种类型:持续的振动类型”continuous”。简短振动类型” transient”。
“RelativeTime”,相对效果文件的开始时间,整形, 单位ms。
“Duration”,持续振动类型的持续时间,整形, 单位ms。
“Parameters”字段包括振动强度“Intensity”,振动频率“Frequency”。
“Curve”为效果曲线数组参数,用来描述持续振动的动态振动效果曲线,实现侧来实现动态变化效果的平滑过渡。其中起始点和结束点是必须的,都可调整频率值。中间为可选的控制点,可调频率及强度值。
“Curve“中Time为相对event的相对时间,Intensity对 Parameters中的Intensity进行修饰,取值范围为\[0,1\], 与Parameters中的Intensity相乘。Frequency对Parameters中的Frequency进行修饰,取值范围为\[-100,100\],与Parameters中的Frequency进行相加。
# 四、接入指南
#### 准备开发环境
1. 为project/build.gradle添加maven仓库
```
示例:AGP版本7.2.1 ,gradle7.3.3 maven依赖配置(*注意AGP低版本在根目录中的build.gradle中配置该maven依赖)
maven {
allowInsecureProtocol true
url 'http://nexus.itgsa.com:5566/repository/release/'
credentials {
username 'developer'
password 'developer!@#'
}
}
```
1. 为app添加sdk, project/app/build.gradle
```
dependencies {
implementation ‘gsai.sdk:hestub:1.0.0'
}
```
1. 添加振动权限(小米侧需要)
```
project/app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.VIBRATE"></uses-permission>
```
# 五、兼容性设计
使用implementation的构建方式将sdk代码嵌入项目中。若系统不存在HapticPlayer或DynamicEffect实现时,将由stubcode中的isAvailable返回false避免兼容性问题,在开发时应先进行判断后再使用:
```
if(HapticPlayer.isAvailable()) {
do something your own
}
```
# 六、场景问题及处理
构建遇到以下错误:
FAILURE: Build failed with an exception.
\* What went wrong:
Could not determine the dependencies of task ':app:compileDebugJavaWithJavac'.
\> Could not resolve all task dependencies for configuration ':app:debugCompileClasspath'.
\> Could not resolve gsai.sdk:roiencodestub:1.0.0-SNAPSHOT.
Required by:
​ project :app
\> Could not resolve gsai.sdk:roiencodestub:1.0.0-SNAPSHOT.
\> Unable to load Maven meta-data from https://nexus.itgsa.com:5566/repository/gsai-snapshot/gsai/sdk/roiencodestub/1.0.0-SNAPSHOT/maven-metadata.xml.
\> Could not get resource 'https://nexus.itgsa.com:5566/repository/gsai-snapshot/gsai/sdk/roiencodestub/1.0.0-SNAPSHOT/maven-metadata.xml'.
\> Could not GET 'https://nexus.itgsa.com:5566/repository/gsai-snapshot/gsai/sdk/roiencodestub/1.0.0-SNAPSHOT/maven-metadata.xml'.
\> Unsupported or unrecognized SSL message
网络问题,请更换网络环境。
/build
\ No newline at end of file
plugins {
id 'com.android.application'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.vivo.hapticdemo"
minSdk 24
targetSdk 32
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'gsai.sdk:hestub:1.0.0'
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.vivo.hapticdemo">
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.HapticDemo">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
\ No newline at end of file
{
"Metadata": {
"Created": "2020-08-10",
"Description": "Haptic editor design",
"Version": 1
},
"Pattern": [
{
"Event": {
"Duration": 500,
"Parameters": {
"Curve": [
{
"Frequency": 60,
"Intensity": 0,
"Time": 0
},
{
"Frequency": 60,
"Intensity": 0.7,
"Time": 200
},
{
"Frequency": 39,
"Intensity": 0.5,
"Time": 300
},
{
"Frequency": 40,
"Intensity": 0,
"Time": 500
}
],
"Frequency": 50,
"Intensity": 100
},
"Type": "continuous",
"RelativeTime": 50
}
}
]
}
\ No newline at end of file
{
"Metadata": {
"Created": "2020-08-10",
"Description": "Haptic editor design",
"Version": 1
},
"Pattern": [
{
"Event": {
"Parameters": {
"Frequency": 5,
"Intensity": 100
},
"Type": "transient",
"RelativeTime": 0
}
}
]
}
\ No newline at end of file
package com.vivo.hapticdemo;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ImageView;
@SuppressLint("AppCompatCustomView")
public class DragView extends ImageView {
private int width;
private int height;
private int maxWidth;
private int maxHeight;
private Context context;
private float downX;
private float downY;
public void setMaxMoveWidthAndHeight(int maxWidth, int maxHeight) {
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
}
private OnViewMoveUpListener onViewMoveUpListener;
private OnViewMoveUpListener onViewMoveUpSbListener;
public void setOnViewMoveUpListener(OnViewMoveUpListener onViewMoveUpListener) {
this.onViewMoveUpListener = onViewMoveUpListener;
}
public void setOnViewMoveUpSbListener(OnViewMoveUpListener onViewMoveUpListener) {
this.onViewMoveUpSbListener = onViewMoveUpListener;
}
public DragView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getMeasuredWidth();
height = getMeasuredHeight();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if (this.isEnabled()) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setPressed(true);
downX = event.getX();
downY = event.getY();
if (null != onViewMoveUpListener) {
onViewMoveUpListener.onViewDown();
}
break;
case MotionEvent.ACTION_MOVE:
float moveX = event.getX() - downX;
float moveY = event.getY() - downY;
int left, right, top, bottom;
if (Math.abs(moveX) > 0 || Math.abs(moveY) > 0) {
left = (int) (getLeft() + moveX);
right = left + width;
top = (int) (getTop() + moveY);
bottom = top + height;
if (left > -width / 2 && left < maxWidth - width / 2 && top > -width / 2 && top < maxHeight - width / 2) {
this.layout(left, top, right, bottom);
if (null != onViewMoveUpListener) {
onViewMoveUpListener.onViewMove();
}
if (null != onViewMoveUpSbListener) {
onViewMoveUpSbListener.onViewMove();
}
}
}
break;
case MotionEvent.ACTION_UP:
if (null != onViewMoveUpListener) {
onViewMoveUpListener.onViewMoveUp();
}
setPressed(false);
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);
break;
}
return true;
}
return false;
}
public interface OnViewMoveUpListener {
void onViewMoveUp();
void onViewMove();
void onViewDown();
}
}
package com.vivo.hapticdemo;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;
public class DrawRelativeLayout extends View {
private int mDrawType = 0; // 绘制类型
private Paint mPaint = new Paint(); // 创建一个画笔对象
private float mInstensity, mSharpness, mAttack, mRelease, mDuration;
public DrawRelativeLayout(Context context) {
this(context, null);
}
public DrawRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint.setAntiAlias(true); // 设置画笔为无锯齿
mPaint.setDither(true); // 设置画笔为防抖动
mPaint.setColor(getResources().getColor(R.color.colorPrimary)); // 设置画笔的颜色
mPaint.setStrokeWidth(3); // 设置画笔的线宽
mPaint.setStyle(Style.FILL); // 设置画笔的类型。STROKE表示空心,FILL表示实心
mPaint.setColor(getResources().getColor(R.color.colorPrimary));
}
// onDraw方法在绘制下级视图之前调用
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float ctrl_X1;
float ctrl_Y1;
float ctrl_X2;
float ctrl_Y2;
int width = getMeasuredWidth(); // 获得布局的实际宽度
int height = getMeasuredHeight(); // 获得布局的实际高度
int left = getLeft();
int right = getRight();
int top = getTop();
int bot = getBottom();
right = right - left;
bot = bot - top;
left = 0;
top = 0;
if (width > 0 && height > 0) {
Path path1 = new Path();
if (mDrawType == 0) {
mSharpness = 1 - mSharpness;
ctrl_X1 = right / (4f + mSharpness * 4);
ctrl_Y1 = (bot * (1 - mInstensity) + (bot - bot * (1 - mInstensity)) / 2) +
mSharpness * (bot - (bot * (1 - mInstensity) + (bot - bot * (1 - mInstensity)) / 2));
ctrl_X2 = right / (4f + mSharpness * 4);
ctrl_Y2 = (bot * (1 - mInstensity) + (bot - bot * (1 - mInstensity)) / 2) -
mSharpness * (bot - (bot * (1 - mInstensity) + (bot - bot * (1 - mInstensity)) / 2));
path1.reset();
path1.moveTo(left, bot);
path1.cubicTo(ctrl_X1, ctrl_Y1, ctrl_X2, ctrl_Y2, right / 2f, bot * (1 - mInstensity));
ctrl_X1 = (3 + mSharpness * 4) * right / (4f + mSharpness * 4);
ctrl_Y1 = (bot * (1 - mInstensity) + (bot - bot * (1 - mInstensity)) / 2) -
mSharpness * (bot - (bot * (1 - mInstensity) + (bot - bot * (1 - mInstensity)) / 2));
ctrl_X2 = (3 + mSharpness * 4) * right / (4f + mSharpness * 4);
ctrl_Y2 = (bot * (1 - mInstensity) + (bot - bot * (1 - mInstensity)) / 2) +
mSharpness * (bot - (bot * (1 - mInstensity) + (bot - bot * (1 - mInstensity)) / 2));
path1.cubicTo(ctrl_X1, ctrl_Y1, ctrl_X2, ctrl_Y2, right, bot);
path1.lineTo(left, bot);
path1.close();
} else if (mDrawType == 1) {
path1.moveTo(left, bot);
path1.lineTo(right * mAttack, top);
path1.lineTo(right * mAttack + (right - right * mAttack) * (1 - mRelease), top);
path1.lineTo(right, bot);
path1.lineTo(left, bot);
path1.close();
}
canvas.drawPath(path1, mPaint);
}
}
// dispatchDraw方法在绘制下级视图之前调用
protected void dispatchDraw(Canvas canvas) {
}
// 设置绘制类型
public void setDrawType(float instensity, float sharpness) {
// 背景置为白色,目的是把画布擦干净
mInstensity = instensity;
mSharpness = sharpness;
mDrawType = 0;
// 立即重新绘图,此时会触发onDraw方法和dispatchDraw方法
invalidate();
}
// 设置绘制类型
public void setDrawFade(float attack, float release, float duration) {
// 背景置为白色,目的是把画布擦干净
mAttack = attack;
mRelease = release;
mDuration = duration;
mDrawType = 1;
// 立即重新绘图,此时会触发onDraw方法和dispatchDraw方法
invalidate();
}
}
package com.vivo.hapticdemo;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
public class DrawWaveView extends RelativeLayout implements DragView.OnViewMoveUpListener {
public static final String TAG = "DrawWaveView";
public static final int INITIALVALUE = -1;
private RelativeLayout mDrawLayout;
private DragView dragViewLeft, dragViewRight;
int mPointStart_x = 48, mPointStart_y = 471;
int mPoint0_x = 48, mPoint0_y = 471;
int mPoint1_x, mPoint1_y;
int mPoint2_x, mPoint2_y;
int mPoint3_x = INITIALVALUE, mPoint3_y = 471;
int mPointEnd_x = INITIALVALUE, mPointEnd_y = 471;
private int mPadding;
private int mHeight;
private int mWidth;
private Paint mPaint = new Paint();
private Path mPath = new Path();
private float downX, downY; //点击时的x坐标,点击时的y坐标
public DrawWaveView(Context context) {
super(context);
init(context);
}
public DrawWaveView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public DrawWaveView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
Log.d(TAG, "init enter");
setWillNotDraw(false);
View view = LayoutInflater.from(context).inflate(R.layout.draw_wave_layout, this);
mDrawLayout = view.findViewById(R.id.draw_layout);
dragViewLeft = view.findViewById(R.id.drag_view_left);
dragViewRight = view.findViewById(R.id.drag_view_right);
dragViewLeft.setOnViewMoveUpListener(this);
dragViewRight.setOnViewMoveUpListener(this);
mPaint.setAntiAlias(true); // 设置画笔为无锯齿
mPaint.setDither(true); // 设置画笔为防抖动
mPaint.setColor(getResources().getColor(R.color.colorPrimary)); // 设置画笔的颜色
mPaint.setStrokeWidth(3); // 设置画笔的线宽
mPaint.setStyle(Paint.Style.FILL); // 设置画笔的类型。STROKE表示空心,FILL表示实心
mPaint.setColor(getResources().getColor(R.color.colorPrimary));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d(TAG, "onMeasure enter");
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mPadding = (int) getResources().getDimension(R.dimen.drag_layout_padding);
mHeight = mDrawLayout.getMeasuredHeight();
mWidth = mDrawLayout.getMeasuredWidth();
Log.d(TAG, "onMeasure height:" + mHeight);
Log.d(TAG, "onMeasure width:" + mWidth);
dragViewLeft.setMaxMoveWidthAndHeight(mWidth, mHeight);
dragViewRight.setMaxMoveWidthAndHeight(mWidth, mHeight);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d(TAG, "onDraw");
int width = mDrawLayout.getMeasuredWidth();
int height = mDrawLayout.getMeasuredHeight();
int left = mDrawLayout.getLeft();
int right = mDrawLayout.getRight();
int top = mDrawLayout.getTop();
int bottom = mDrawLayout.getBottom();
int dragWidth = dragViewLeft.getMeasuredWidth();
int dragHeight = dragViewLeft.getMeasuredHeight();
int leftDragLeft = dragViewLeft.getLeft();
int leftDragRight = dragViewLeft.getRight();
int leftDragTop = dragViewLeft.getTop();
int leftDragBottom = dragViewLeft.getBottom();
int rightDragLeft = dragViewRight.getLeft();
int rightDragRight = dragViewRight.getRight();
int rightDragTop = dragViewRight.getTop();
int rightDragBottom = dragViewRight.getBottom();
if (dragViewLeft.isPressed()) {
if (leftDragLeft > rightDragLeft) {
rightDragLeft = leftDragLeft;
rightDragRight = leftDragLeft + dragWidth;
dragViewRight.layout(rightDragLeft, rightDragTop, rightDragRight, rightDragBottom);
}
} else if (dragViewRight.isPressed()) {
if (leftDragLeft > rightDragLeft) {
leftDragLeft = rightDragLeft;
leftDragRight = rightDragLeft + dragWidth;
dragViewLeft.layout(leftDragLeft, leftDragTop, leftDragRight, leftDragBottom);
}
}
right = right - left + mPadding;
bottom = bottom - top + mPadding;
left = mPadding;
top = mPadding;
setPointStart(left, mPointStart_y);
setPoint0(mPoint0_x, mPoint0_y);
setPoint1(leftDragLeft + mPadding + dragWidth / 2, leftDragTop + mPadding + dragHeight / 2);
setPoint2(rightDragLeft + mPadding + dragWidth / 2, rightDragTop + mPadding + dragHeight / 2);
if (mPoint3_x == INITIALVALUE || mPointEnd_x == INITIALVALUE) {
mPoint3_x = mDrawLayout.getRight();
mPointEnd_x = mDrawLayout.getRight();
}
setPoint3(mPoint3_x, mPoint3_y);
setPointEnd(mPointEnd_x, mPointEnd_y);
setPath();
canvas.drawPath(mPath, mPaint);
}
public void setPointStart(int pointStart_x, int pointStart_y) {
mPointStart_x = mDrawLayout.getLeft();
mPointStart_y = pointStart_y;
}
public void setPoint0(int point_x, int point_y) {
mPoint0_x = point_x;
mPoint0_y = point_y;
}
public void setPoint1(int point_x, int point_y) {
mPoint1_x = point_x;
mPoint1_y = point_y;
}
public void setPoint2(int point_x, int point_y) {
mPoint2_x = point_x;
mPoint2_y = point_y;
}
public void setPoint3(int point_x, int point_y) {
mPoint3_x = point_x;
mPoint3_y = point_y;
}
public void setPointEnd(int pointEnd_x, int pointEnd_y) {
mPointEnd_x = pointEnd_x;
mPointEnd_y = pointEnd_y;
}
public void setPath() {
mPath.reset();
mPath.moveTo(mDrawLayout.getLeft(), mDrawLayout.getBottom());
mPath.lineTo(mPoint0_x, mPoint0_y);
mPath.lineTo(mPoint1_x, mPoint1_y);
mPath.lineTo(mPoint2_x, mPoint2_y);
mPath.lineTo(mPoint3_x, mPoint3_y);
mPath.lineTo(mPointEnd_x, mHeight + mPadding);
mPath.close();
}
public void drawWithPoints(int pointStart_y, int point1_x, int point1_y, int point2_x, int point2_y, int pointEnd_y, int duration) {
Log.d(TAG, "drawWithPoints enter pointStart_y = " + pointStart_y + ", pointEnd_y = " + pointEnd_y);
Log.d(TAG, "drawWithPoints enter point1_x = " + point1_x + ", point1_y = " + point1_y + ", point2_x = " + point2_x + ", point2_y = " + point2_y);
int Left_l = point1_x * mWidth / 5000 - dragViewLeft.getMeasuredWidth() / 2;
int Left_t = ((100 - point1_y) * mHeight / 100) - dragViewLeft.getMeasuredHeight() / 2;
int Left_r = Left_l + dragViewLeft.getMeasuredWidth();
int Left_b = Left_t + dragViewLeft.getMeasuredHeight();
dragViewLeft.layout(Left_l, Left_t, Left_r, Left_b);
int Right_l = point2_x * mWidth / 5000 - dragViewLeft.getMeasuredWidth() / 2;
int Right_t = ((100 - point2_y) * mHeight / 100) - dragViewLeft.getMeasuredHeight() / 2;
int Right_r = Right_l + dragViewLeft.getMeasuredWidth();
int Right_b = Right_t + dragViewLeft.getMeasuredHeight();
dragViewRight.layout(Right_l, Right_t, Right_r, Right_b);
setPoint0(mDrawLayout.getLeft(), ((100 - pointStart_y) * mHeight / 100) + mPadding);
setPoint3(mDrawLayout.getLeft() + duration * mDrawLayout.getWidth() / 5000, ((100 - pointEnd_y) * mHeight / 100) + mPadding);
setPointEnd(mDrawLayout.getLeft() + duration * mDrawLayout.getWidth() / 5000, mDrawLayout.getBottom());
invalidate();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
float downX = ev.getX(); // 点击触屏时的x坐标 用于离开屏幕时的x坐标作计算
float downY = ev.getY(); // 点击触屏时的y坐标 用于离开屏幕时的y坐标作计算
if (downX > mDrawLayout.getLeft() && downX < mDrawLayout.getRight() && downY > mDrawLayout.getTop() && downY < mDrawLayout.getBottom()) {
Log.d(TAG, "dispatchTouchEvent : in mDrawLayout,ingonre");
getParent().requestDisallowInterceptTouchEvent(true);
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float downX = event.getX(); // 点击触屏时的x坐标 用于离开屏幕时的x坐标作计算
float downY = event.getY(); // 点击触屏时的y坐标 用于离开屏幕时的y坐标作计算
if (downX > mDrawLayout.getLeft() && downX < mDrawLayout.getRight() && downY > mDrawLayout.getTop() && downY < mDrawLayout.getBottom()) {
Log.d(TAG, "onTouchEvevt : in mDrawLayout,ingonre");
return true;
}
return super.onTouchEvent(event);
}
@Override
public void onViewMoveUp() {
}
@Override
public void onViewMove() {
invalidate();
}
@Override
public void onViewDown() {
}
}
This diff is collapsed.
package com.vivo.hapticdemo;
import android.view.View;
import android.view.ViewGroup;
import androidx.viewpager.widget.PagerAdapter;
import java.util.ArrayList;
public class ModeAdapter extends PagerAdapter {
private ArrayList<PageInfo> mViewList;
public ModeAdapter(ArrayList<PageInfo> mView) {
mViewList = mView;
}
public boolean isViewFromObject(View arg0, Object arg1) {
// TODO Auto-generated method stub
return arg0 == arg1;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return mViewList.size();
}
public void destroyItem(ViewGroup container, int position,
Object object) {
// TODO Auto-generated method stub
container.removeView(mViewList.get(position).view);
}
public Object instantiateItem(ViewGroup container, int position) {
// TODO Auto-generated method stub
container.addView(mViewList.get(position).view);
return mViewList.get(position).view;
}
public CharSequence getPageTitle(int position) {
return mViewList.get(position).title;
}
}
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/drag_point_size" />
<size
android:width="@dimen/drag_point_size"
android:height="@dimen/drag_point_size" />
<solid android:color="@color/colorFoth" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="@dimen/drag_layout_bg_stoke"
android:color="@color/colorFoth" />
<!-- <solid android:color="@color/colorbg"/>-->
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="3dp" />
<solid android:color="#ECF0F1" />
</shape>
</item>
<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<corners android:radius="3dp" />
<solid android:color="#C6CACE" />
</shape>
</clip>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="3dp" />
<solid android:color="@color/colorPrimary" />
</shape>
</clip>
</item>
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<!-- solid表示远的填充色 -->
<solid android:color="@color/colorPrimary" />
<!-- stroke则代表远的边框线 -->
<stroke
android:width="1dp"
android:color="@color/colorPrimary" />
<!-- size控制高宽 -->
<size
android:height="20dp"
android:width="20dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 指定了形状内部的填充颜色 -->
<solid android:color="#f2f1f6" />
<!-- 指定了形状边线的粗细与颜色 -->
<stroke
android:width="1dp"
android:color="#f2f1f6" />
<!-- 指定了形状四个圆角的半径 -->
<corners
android:bottomLeftRadius="20sp"
android:bottomRightRadius="20sp"
android:topLeftRadius="20sp"
android:topRightRadius="20sp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 指定了形状内部的填充颜色 -->
<solid android:color="@color/colorDisableBtn" />
<!-- 指定了形状边线的粗细与颜色 -->
<stroke
android:width="1dp"
android:color="@color/colorDisableBtn" />
<!-- 指定了形状四个圆角的半径 -->
<corners
android:bottomLeftRadius="20dp"
android:bottomRightRadius="20dp"
android:topLeftRadius="20dp"
android:topRightRadius="20dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 指定了形状内部的填充颜色 -->
<solid android:color="@color/colorFir" />
<!-- 指定了形状边线的粗细与颜色 -->
<stroke
android:width="1dp"
android:color="@color/colorFir" />
<!-- 指定了形状四个圆角的半径 -->
<corners
android:bottomLeftRadius="20dp"
android:bottomRightRadius="20dp"
android:topLeftRadius="20dp"
android:topRightRadius="20dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 指定了形状内部的填充颜色 -->
<solid android:color="@color/colorFith" />
<!-- 指定了形状边线的粗细与颜色 -->
<stroke
android:width="1dp"
android:color="@color/colorFith" />
<!-- 指定了形状四个圆角的半径 -->
<corners
android:bottomLeftRadius="20dp"
android:bottomRightRadius="20dp"
android:topLeftRadius="20dp"
android:topRightRadius="20dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 指定了形状内部的填充颜色 -->
<solid android:color="@color/colorFoth" />
<!-- 指定了形状边线的粗细与颜色 -->
<stroke
android:width="1dp"
android:color="@color/colorFoth" />
<!-- 指定了形状四个圆角的半径 -->
<corners
android:bottomLeftRadius="20dp"
android:bottomRightRadius="20dp"
android:topLeftRadius="20dp"
android:topRightRadius="20dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 指定了形状内部的填充颜色 -->
<solid android:color="@color/colorSec" />
<!-- 指定了形状边线的粗细与颜色 -->
<stroke
android:width="1dp"
android:color="@color/colorSec" />
<!-- 指定了形状四个圆角的半径 -->
<corners
android:bottomLeftRadius="20dp"
android:bottomRightRadius="20dp"
android:topLeftRadius="20dp"
android:topRightRadius="20dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 指定了形状内部的填充颜色 -->
<solid android:color="@color/colorThi" />
<!-- 指定了形状边线的粗细与颜色 -->
<stroke
android:width="1dp"
android:color="@color/colorThi" />
<!-- 指定了形状四个圆角的半径 -->
<corners
android:bottomLeftRadius="20dp"
android:bottomRightRadius="20dp"
android:topLeftRadius="20dp"
android:topRightRadius="20dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 指定了形状内部的填充颜色 -->
<solid android:color="@color/colorPrimary" />
<!-- 指定了形状边线的粗细与颜色 -->
<stroke
android:width="1dp"
android:color="@color/colorPrimary" />
<!-- 指定了形状四个圆角的半径 -->
<corners
android:bottomLeftRadius="20dp"
android:bottomRightRadius="20dp"
android:topLeftRadius="20dp"
android:topRightRadius="20dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 指定了形状内部的填充颜色 -->
<solid android:color="#f2f1f6" />
<!-- 指定了形状边线的粗细与颜色 -->
<stroke
android:width="1dp"
android:color="@color/colorPrimary" />
<!-- 指定了形状四个圆角的半径 -->
<corners
android:bottomLeftRadius="0sp"
android:bottomRightRadius="0sp"
android:topLeftRadius="0sp"
android:topRightRadius="0sp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="@drawable/switch_on"/>
<item android:drawable="@drawable/switch_off"/>
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.viewpager.widget.ViewPager
android:id="@+id/vp_head"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.viewpager.widget.PagerTabStrip
android:id="@+id/haptic_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorPrimary" />
</androidx.viewpager.widget.ViewPager>
</LinearLayout>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/drag_layout_padding">
<RelativeLayout
android:id="@+id/draw_layout"
android:layout_width="375dp"
android:layout_height="141dp"
android:background="@drawable/draw_layout_bg">
<com.vivo.hapticdemo.DragView
android:id="@+id/drag_view_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="125dp"
android:src="@drawable/drag_point_up" />
<com.vivo.hapticdemo.DragView
android:id="@+id/drag_view_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="250dp"
android:src="@drawable/drag_point_up" />
</RelativeLayout>
</RelativeLayout>
\ No newline at end of file
This diff is collapsed.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.vivo.hapticdemo.DrawRelativeLayout
android:id="@+id/drl_short"
android:layout_width="match_parent"
android:layout_height="200sp"
android:layout_marginTop="20sp"
android:layout_marginLeft="20sp"
android:layout_marginRight="20sp"
android:background="@drawable/shape_rect">
</com.vivo.hapticdemo.DrawRelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50sp"
android:orientation="horizontal"
android:layout_marginTop="20sp"
android:visibility="gone"
android:layout_marginLeft="20sp"
android:layout_marginRight="20sp"
android:background="@drawable/shape_brk_backgrount">
<TextView
android:id="@+id/tv_brake"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="brake status"
android:layout_marginLeft="20dp"
android:layout_marginTop="10sp"
android:layout_alignParentLeft="true"
android:textSize="18sp"
android:textColor="#aaaaaa"/>
<CheckBox
android:id="@+id/ck_status"
android:layout_width="60dp"
android:layout_height="30dp"
android:checked="true"
android:layout_marginRight="20dp"
android:layout_marginTop="10sp"
android:background="@drawable/switch_selector"
android:button="@null"
android:layout_alignParentRight="true"
android:layout_gravity="right"/>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv_relativetime"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="relativetime: 0"
android:textSize="15sp"
android:layout_marginTop="20dp"
android:layout_marginLeft="20dp"
android:textColor="#bbbbbb"/>
<SeekBar
android:id="@+id/sb_relativetime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/text1"
android:maxHeight="2dp"
android:minHeight="2dp"
android:paddingBottom="3dp"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:paddingTop="3dp"
android:progressDrawable="@drawable/seek_bar_progress_shape"
android:thumb="@drawable/seekbar_thumb_shape"
android:max="100" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv_intensity"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="intensity: 0"
android:layout_marginLeft="20dp"
android:textSize="15sp"
android:textColor="#bbbbbb"/>
<SeekBar
android:id="@+id/sb_intensity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/text1"
android:maxHeight="2dp"
android:minHeight="2dp"
android:paddingBottom="3dp"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:paddingTop="3dp"
android:progressDrawable="@drawable/seek_bar_progress_shape"
android:thumb="@drawable/seekbar_thumb_shape"
android:max="100" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv_frequency"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="frequency: 0"
android:textSize="15sp"
android:layout_marginLeft="20dp"
android:textColor="#bbbbbb"/>
<SeekBar
android:id="@+id/sb_frequency"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/text1"
android:maxHeight="2dp"
android:minHeight="2dp"
android:paddingBottom="3dp"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:paddingTop="3dp"
android:progressDrawable="@drawable/seek_bar_progress_shape"
android:thumb="@drawable/seekbar_thumb_shape"
android:max="100" />
</LinearLayout>
<Button
android:id="@+id/btn_short"
android:layout_width="match_parent"
android:layout_height="70sp"
android:layout_marginTop="40sp"
android:layout_marginLeft="20sp"
android:layout_marginRight="20sp"
android:background="@drawable/shape_but"
android:text="Tap"
android:textColor="#ffffff"
android:textSize="15sp"/>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.HapticDemo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/black</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#03A9F4</color>
<color name="purple_700">#2196F3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="colorPrimary">#299EE3</color>
<color name="colorFir">#81b7dc</color>
<color name="colorSec">#81dca7</color>
<color name="colorThi">#dc8189</color>
<color name="colorFoth">#45cdc0</color>
<color name="colorFith">#d481dc</color>
<color name="colorDisableBtn">#d2cecd</color>
<color name="colorbg">#F4F3F3</color>
<color name="app_color_theme_1">#EF5362</color> <!-- Grapefruit -->
<color name="app_color_theme_2">#FE6D4B</color> <!-- Bittersweet -->
<color name="app_color_theme_3">#FFCF47</color> <!-- Sunflower -->
<color name="app_color_theme_4">#9FD661</color> <!-- Grass -->
<color name="app_color_theme_5">#3FD0AD</color> <!-- Mint -->
<color name="app_color_theme_6">#2BBDF3</color> <!-- Aqua -->
<color name="app_color_theme_7">#5A9AEF</color> <!-- Blue Jeans -->
<color name="app_color_theme_8">#AC8FEF</color> <!-- Lavender -->
<color name="app_color_theme_9">#EE85C1</color> <!-- Pink Rose -->
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="drag_point_size">16dp</dimen>
<dimen name="drag_layout_padding">16dp</dimen>
<dimen name="drag_layout_bg_stoke">1dp</dimen>
</resources>
<resources>
<string name="app_name">HapticDemo</string>
</resources>
\ No newline at end of file
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.HapticDemo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>
\ No newline at end of file
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.0.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
\ No newline at end of file
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1024m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
android.injected.studio.version.check=false
\ No newline at end of file
#Thu Aug 11 14:58:11 CST 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
maven {
allowInsecureProtocol true
url 'http://nexus.itgsa.com:5566/repository/release/'
credentials {
username 'developer'
password 'developer!@#'
}
}
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven {
allowInsecureProtocol true
url 'http://nexus.itgsa.com:5566/repository/release/'
credentials {
username 'developer'
password 'developer!@#'
}
}
}
}
rootProject.name = "hapticdemo"
include ':app'
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment