diff --git a/app/build.gradle b/app/build.gradle index 938470f..5864302 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,7 +24,7 @@ android { } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support:design:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.1.0' @@ -50,5 +50,20 @@ dependencies { //个人信息的那个条条 compile 'com.akexorcist:RoundCornerProgressBar:2.0.3' //wave - compile 'com.gelitenight.waveview:waveview:1.0.0' + implementation 'me.itangqi.waveloadingview:library:0.3.5' + //计步 + implementation project(':todaystepcounterlib') + //打分ui + compile 'com.github.CB-ysx:CBRatingBar:3.0.1' +// //蛛网 +// implementation 'me.panpf:spider-web-score-view:1.0.1' + //折线 + implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3' + //searchview + compile 'com.miguelcatalan:materialsearchview:1.4.0' + //floatbuttom + compile 'com.nightonke:boommenu:2.1.1' + //recycler and card + implementation 'com.android.support:recyclerview-v7:26.1.0' + implementation 'com.android.support:cardview-v7:26.1.0' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 978e5b9..8bb7fa1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,9 +17,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/NutritionMaster.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/NutritionMaster.java index ec33f70..ff65488 100644 --- a/app/src/main/java/com/example/ninefourone/nutritionmaster/NutritionMaster.java +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/NutritionMaster.java @@ -1,6 +1,8 @@ package com.example.ninefourone.nutritionmaster; +import android.app.Activity; import android.app.Application; +import android.os.Bundle; import com.orhanobut.logger.AndroidLogAdapter; import com.orhanobut.logger.Logger; @@ -12,6 +14,7 @@ import com.orhanobut.logger.Logger; public class NutritionMaster extends Application { public static NutritionMaster mInstance; + private int appCount = 0; @Override public void onCreate() { @@ -25,10 +28,55 @@ public class NutritionMaster extends Application { */ private void init() { Logger.addLogAdapter(new AndroidLogAdapter()); - Logger.d("Logger初始化成功"); + registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + + } + + @Override + public void onActivityStarted(Activity activity) { + appCount++; + } + + @Override + public void onActivityResumed(Activity activity) { + + } + + @Override + public void onActivityPaused(Activity activity) { + + } + + @Override + public void onActivityStopped(Activity activity) { + appCount--; + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + + } + + @Override + public void onActivityDestroyed(Activity activity) { + + } + }); } public static NutritionMaster getInstance() { return mInstance; } + + /** + * app是否在前台 + * + * @return true前台,false后台 + */ + public boolean isForeground() { + return appCount > 0; + } + } diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/adapter/CardAdapter.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/adapter/CardAdapter.java new file mode 100644 index 0000000..a2dd942 --- /dev/null +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/adapter/CardAdapter.java @@ -0,0 +1,63 @@ +package com.example.ninefourone.nutritionmaster.adapter; + +import android.content.Context; +import android.os.Build; +import android.support.annotation.RequiresApi; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.example.ninefourone.nutritionmaster.R; +import com.example.ninefourone.nutritionmaster.bean.DailyCard; +import com.orhanobut.logger.Logger; + +import java.util.ArrayList; + +/** + * Created by ScorpioMiku on 2018/9/2. + */ + +public class CardAdapter extends RecyclerView.Adapter { + private Context context; + private ArrayList mList; + + + public CardAdapter(Context context, ArrayList mList) { + this.context = context; + this.mList = mList; + } + + + @Override + public CardHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(context).inflate(R.layout.card_item, parent, false); + CardHolder cardHolder = new CardHolder(view); + return cardHolder; + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public void onBindViewHolder(CardHolder holder, int position) { + holder.bindView(mList.get(position).getPictureId(), mList.get(position).getTitle(), context); + } + + @Override + public int getItemCount() { + return mList.size(); + } + + /** + * 右划 + */ + public void swipe2Right() { + Logger.d("右划"); + } + + /** + * 左划 + */ + public void swipe2left() { + Logger.d("左划"); + } +} diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/adapter/CardHolder.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/adapter/CardHolder.java new file mode 100644 index 0000000..43d40d0 --- /dev/null +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/adapter/CardHolder.java @@ -0,0 +1,44 @@ +package com.example.ninefourone.nutritionmaster.adapter; + +import android.content.Context; +import android.os.Build; +import android.support.annotation.RequiresApi; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import com.example.ninefourone.nutritionmaster.R; + +import java.util.ArrayList; + +import butterknife.BindView; +import butterknife.ButterKnife; + +/** + * Created by ScorpioMiku on 2018/9/2. + */ + +public class CardHolder extends RecyclerView.ViewHolder { + @BindView(R.id.iv_photo) + ImageView ivPhoto; + @BindView(R.id.tv_name) + TextView tvName; + @BindView(R.id.tv_sign) + TextView tvSign; + + + public CardHolder(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public void bindView(int picId, String text, Context context) { + tvName.setText(text); + ivPhoto.setImageDrawable(context.getDrawable(picId)); + + } + + +} diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/adapter/HomePagerAdapter.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/adapter/HomePagerAdapter.java index b2cced9..f705e2e 100644 --- a/app/src/main/java/com/example/ninefourone/nutritionmaster/adapter/HomePagerAdapter.java +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/adapter/HomePagerAdapter.java @@ -6,7 +6,7 @@ import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import com.example.ninefourone.nutritionmaster.R; -import com.example.ninefourone.nutritionmaster.modules.viewpagerfragments.page1.Page1; +import com.example.ninefourone.nutritionmaster.modules.viewpagerfragments.customization.CustomizationFragment; import com.example.ninefourone.nutritionmaster.modules.viewpagerfragments.bodyinformation.BodyInformationFragment; import com.example.ninefourone.nutritionmaster.modules.viewpagerfragments.page3.Page3; @@ -30,7 +30,7 @@ public class HomePagerAdapter extends FragmentPagerAdapter { if (fragments[position] == null) { switch (position) { case 0: - fragments[position] = Page1.getInstance(); + fragments[position] = CustomizationFragment.getInstance(); break; case 1: fragments[position] = BodyInformationFragment.getInstance(); diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/base/BaseActivity.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/base/BaseActivity.java index d9f9592..d1f44e1 100644 --- a/app/src/main/java/com/example/ninefourone/nutritionmaster/base/BaseActivity.java +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/base/BaseActivity.java @@ -23,6 +23,8 @@ public abstract class BaseActivity extends AppCompatActivity { initToolBar(); } + + /** * 设置布局layout * diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/bean/DailyCard.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/bean/DailyCard.java new file mode 100644 index 0000000..b689b43 --- /dev/null +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/bean/DailyCard.java @@ -0,0 +1,44 @@ +package com.example.ninefourone.nutritionmaster.bean; + +/** + * Created by ScorpioMiku on 2018/9/3. + */ + +public class DailyCard { + /*** + * 每日卡片bean类 + */ + private String title; + private String description; + private int pictureId; + + public DailyCard(String title, String description, int pictureId) { + this.title = title; + this.description = description; + this.pictureId = pictureId; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public int getPictureId() { + return pictureId; + } + + public void setPictureId(int pictureId) { + this.pictureId = pictureId; + } +} diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/bean/User.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/bean/User.java new file mode 100644 index 0000000..54c888c --- /dev/null +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/bean/User.java @@ -0,0 +1,14 @@ +package com.example.ninefourone.nutritionmaster.bean; + +/** + * Created by ScorpioMiku on 2018/8/30. + */ + +public class User { + private float height; + private float weight; + private float BMI; + private String sex; + private int age; + private String job; +} diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/camera/CameraPreview.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/camera/CameraPreview.java new file mode 100644 index 0000000..0a26aa8 --- /dev/null +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/camera/CameraPreview.java @@ -0,0 +1,63 @@ +package com.example.ninefourone.nutritionmaster.camera; + +import android.content.Context; +import android.hardware.Camera; +import android.view.SurfaceView; +import android.view.SurfaceHolder; + + +import com.orhanobut.logger.Logger; + +import java.io.IOException; + +/** + * Created by ScorpioMiku on 2018/9/3. + */ + +public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { + + private SurfaceHolder mHolder; + private Camera camera; + + public CameraPreview(Context context, Camera camera) { + super(context); + this.camera = camera; + mHolder = getHolder(); + mHolder.addCallback(this); +// mHolder.setType(); + } + + + @Override + public void surfaceCreated(SurfaceHolder holder) { + try { + camera.setPreviewDisplay(holder); + camera.startPreview(); + } catch (IOException e) { + Logger.e(e.getMessage()); + } + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + if (mHolder.getSurface() == null) { + return; + } + try { + camera.stopPreview(); + } catch (Exception e) { + e.printStackTrace(); + } + try { + camera.setPreviewDisplay(mHolder); + camera.startPreview(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + + } +} diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/camera/FoodMaterialCamera.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/camera/FoodMaterialCamera.java new file mode 100644 index 0000000..2477de1 --- /dev/null +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/camera/FoodMaterialCamera.java @@ -0,0 +1,197 @@ +package com.example.ninefourone.nutritionmaster.camera; + +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageManager; +import android.hardware.Camera; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.view.Surface; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import com.example.ninefourone.nutritionmaster.R; +import com.example.ninefourone.nutritionmaster.utils.MessageUtils; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; + +/** + * Created by ScorpioMiku on 2018/9/3. + */ + +public class FoodMaterialCamera extends AppCompatActivity { + @BindView(R.id.camera_preview) + FrameLayout mCameraLayout; + @BindView(R.id.results_text_view) + TextView resultsTextView; + @BindView(R.id.more_take_photo_button_capture) + ImageView moreTakePhotoButtonCapture; + @BindView(R.id.more_takephoto_ok) + ImageView moreTakephotoOk; + @BindView(R.id.more_camera_cover_linearlayout) + FrameLayout moreCameraCoverLinearlayout; + + private Camera mCamera; + private CameraPreview mPreview; + private int mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK; + + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + //取消toolbar + requestWindowFeature(Window.FEATURE_NO_TITLE); + //设置全屏 + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + //注意:上面两个设置必须写在setContentView前面 + setContentView(R.layout.cameras_layout); + ButterKnife.bind(this); + + if (!checkCameraHardware(this)) { + MessageUtils.MakeToast("不支持相机"); + } else { + openCamera(); + } + + setCameraDisplayOrientation(this, mCameraId, mCamera); + } + + /** + * 检查当前设备是否有相机 + * + * @param context + * @return + */ + private boolean checkCameraHardware(Context context) { + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_CAMERA)) { + return true; + } else { + return false; + } + } + + + /** + * 打开相机 + */ + private void openCamera() { + if (null == mCamera) { + mCamera = getCameraInstance(); + mPreview = new CameraPreview(this, mCamera); +// mPreview.setOnTouchListener(new View.OnTouchListener() { +// @Override +// public boolean onTouch(View v, MotionEvent event) { +// mCamera.autoFocus(null); +// return false; +// } +// }); + mCameraLayout.addView(mPreview); + mCamera.startPreview(); + } + } + + /** + * 获取相机 + * + * @return + */ + private Camera getCameraInstance() { + Camera c = null; + try { + c = Camera.open(); + Camera.Parameters mParameters = c.getParameters(); + mParameters.setPictureSize(720, 1280); + c.setParameters(mParameters); + } catch (Exception e) { + e.printStackTrace(); + } + + return c; + } + + /** + * 对焦回调,对焦完成后进行拍照 + */ + private Camera.AutoFocusCallback mAutoFocusCallback = new Camera.AutoFocusCallback() { + @Override + public void onAutoFocus(boolean success, Camera camera) { + if (success) { + mCamera.takePicture(null, null, mPictureCallback); + } + } + }; + + /** + * 拍照回调 + */ + private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() { + @Override + public void onPictureTaken(final byte[] data, Camera camera) { + MessageUtils.MakeToast("拍照!"); + mCamera.startPreview(); + } + }; + + + /** + * 两个按钮的事件 + * + * @param view + */ + @OnClick({R.id.more_take_photo_button_capture, R.id.more_takephoto_ok}) + public void onViewClicked(View view) { + switch (view.getId()) { + case R.id.more_take_photo_button_capture: + mCamera.autoFocus(mAutoFocusCallback); + break; + case R.id.more_takephoto_ok: + break; + } + } + + //将相机设置成竖屏 + public static void setCameraDisplayOrientation(Activity activity, int cameraId, Camera camera) { + + int degrees = 0; + + //可以获得摄像头信息 + Camera.CameraInfo info = new Camera.CameraInfo(); + Camera.getCameraInfo(cameraId, info); + + //获取屏幕旋转方向 + int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); + + switch (rotation) { + case Surface.ROTATION_0: + degrees = 0; + break; + case Surface.ROTATION_90: + degrees = 90; + break; + case Surface.ROTATION_180: + degrees = 180; + break; + case Surface.ROTATION_270: + degrees = 270; + break; + } + int result; + if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { + result = (info.orientation + degrees) % 360; + result = (360 - result) % 360; + } else { + result = (info.orientation - degrees + 360) % 360; + } + camera.setDisplayOrientation(result); + } +} diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/cardconfig/CardConfig.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/cardconfig/CardConfig.java new file mode 100644 index 0000000..9e3dc07 --- /dev/null +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/cardconfig/CardConfig.java @@ -0,0 +1,25 @@ +package com.example.ninefourone.nutritionmaster.cardconfig; + +import android.content.Context; +import android.util.TypedValue; + +/** + * 初始化一些配置信息、固定数据 + */ +public class CardConfig { + //屏幕上最多同时显示几个Item + public static int MAX_SHOW_COUNT; + + //每一级Scale相差0.05f,translationY相差15dp,translationZ相差0.5dp左右 + public static float SCALE_GAP; + public static int TRANS_Y_GAP; + public static int TRANS_Z_GAP; + + public static void initConfig(Context context) { + MAX_SHOW_COUNT = 4; + SCALE_GAP = 0.05f; + //这里是把dp转换成px + TRANS_Y_GAP = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15f, context.getResources().getDisplayMetrics()); + TRANS_Z_GAP = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.5f, context.getResources().getDisplayMetrics()); + } +} diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/cardconfig/CardItemTouchCallBack.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/cardconfig/CardItemTouchCallBack.java new file mode 100644 index 0000000..6fd06c0 --- /dev/null +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/cardconfig/CardItemTouchCallBack.java @@ -0,0 +1,256 @@ +package com.example.ninefourone.nutritionmaster.cardconfig; + +import android.graphics.Canvas; +import android.os.Build; +import android.support.annotation.RequiresApi; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; +import android.util.Log; +import android.view.View; + +import com.example.ninefourone.nutritionmaster.adapter.CardAdapter; +import com.example.ninefourone.nutritionmaster.adapter.CardHolder; + +import java.util.List; + +/** + * Created by ScorpioMiku on 2018/9/2. + */ + +public class CardItemTouchCallBack extends ItemTouchHelper.Callback { + + private static final String TAG = "CardItemTouchCallBack"; + private RecyclerView mRecyclerView; + private CardAdapter mAdapter; + private List mDatas; + + public CardItemTouchCallBack(RecyclerView recyclerView, CardAdapter adapter, List datas) { + this.mRecyclerView = recyclerView; + this.mAdapter = adapter; + this.mDatas = datas; + } + + /** + * 是否开启长按拖拽 + * true,开启 + * false,不开启长按退拽 + * + * @return + */ + @Override + public boolean isLongPressDragEnabled() { + return false; + } + + /** + * 是否开启滑动 + * true,开启 + * false,不开启长按退拽 + * + * @return + */ + @Override + public boolean isItemViewSwipeEnabled() { + return true; + } + + /** + * ItemTouchHelper支持设置事件方向,并且必须重写当前getMovementFlags来指定支持的方向 + * dragFlags 表示拖拽的方向,有六个类型的值:LEFT、RIGHT、START、END、UP、DOWN + * swipeFlags 表示滑动的方向,有六个类型的值:LEFT、RIGHT、START、END、UP、DOWN + * 最后要通过makeMovementFlags(dragFlag,swipe)创建方向的Flag + */ + @Override + public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { + /** + * 由于我们不需要长按拖拽,所以直接传入0即可,传入0代表不监听 + */ + int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.UP | ItemTouchHelper.DOWN; + return makeMovementFlags(0, swipeFlags); + } + + /** + * 长按item就可以拖动,然后拖动到其他item的时候触发onMove + * 这里我们不需要 + * + * @param recyclerView + * @param viewHolder 拖动的viewholder + * @param target 目标位置的viewholder + * @return + */ + @Override + public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { + return false; + } + + /** + * 把item滑走(飞出屏幕)的时候调用 + * + * @param viewHolder 滑动的viewholder + * @param direction 滑动的方向 + */ + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { + //这里来判断画出的方向,左边是4,右边是8,然后可以做一些数据操作 + Log.d(TAG, "onSwiped: " + direction); + switch (direction) { + case 4: + Log.d(TAG, "onSwiped: 左边滑出"); + mAdapter.swipe2left(); + break; + case 8: + Log.d(TAG, "onSwiped: 右边滑出"); + mAdapter.swipe2Right(); + break; + } + //移除这条数据 + Object remove = mDatas.remove(viewHolder.getLayoutPosition()); + + /** 这个位置可以用来加载数据,当滑到还剩4个或者多少个时可以在后面加载数据,添加到mDatas中*/ + //这里就为了方便,直接循环了,把移除的元素再添加到末尾 + mDatas.add(mDatas.size(), remove); + + //刷新 + mAdapter.notifyDataSetChanged(); + //复位 + viewHolder.itemView.setRotation(0); + if (viewHolder instanceof CardHolder) { + CardHolder holder = (CardHolder) viewHolder; + } + } + + /** + * 只要拖动、滑动了item,就会触发这个方法,而且是动的过程中会一直触发 + * 所以动画效果就是在这个方法中来实现的 + * + * @param c + * @param recyclerView + * @param viewHolder + * @param dX + * @param dY + * @param actionState + * @param isCurrentlyActive + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, + float dX, float dY, int actionState, boolean isCurrentlyActive) { + super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); + double swipeValue = Math.sqrt(dX * dX + dY * dY); //滑动离中心的距离 + double fraction = swipeValue / (mRecyclerView.getWidth() * 0.5f); + //边界修正 最大为1 + if (fraction > 1) { + fraction = 1; + } + + /** + * 调整每个子view的缩放、位移之类的 + */ + int childCount = recyclerView.getChildCount(); //拿到子view的数量 + isUpOrDown(mRecyclerView.getChildAt(childCount - 1)); + for (int i = 0; i < childCount; i++) { + /** 拿到子view 注意这里,先绘制的i=0,所以最下面一层view的i=0,最上面的i=3*/ + View childView = recyclerView.getChildAt(i); + int level = childCount - i - 1; //转换一下,level代表层数,最上面是第0层 + if (level > 0) { + //下面层,每一层的水平方向都要增大 + childView.setScaleX((float) (1 - CardConfig.SCALE_GAP * level + fraction * CardConfig.SCALE_GAP)); + if (level < CardConfig.MAX_SHOW_COUNT - 1) { + //1 2层 + childView.setScaleY((float) (1 - CardConfig.SCALE_GAP * level + fraction * CardConfig.SCALE_GAP)); + childView.setTranslationY((float) (CardConfig.TRANS_Y_GAP * level - fraction * CardConfig.TRANS_Y_GAP)); + childView.setTranslationZ((float) (CardConfig.TRANS_Z_GAP * (CardConfig.MAX_SHOW_COUNT - 1 - level) + + fraction * CardConfig.TRANS_Z_GAP)); + } else { + //最下面一层,3层,这层不用变,所以这里不用写 + } + } else { + //第0层 + //拿到水平方向的偏移比率 + float xFraction = dX / (mRecyclerView.getWidth() * 0.5f); + //边界修正,有正有负,因为旋转有两个方向 + if (xFraction > 1) { + xFraction = 1; + } else if (xFraction < -1) { + xFraction = -1; + } + //第一层左右滑动的时候稍微有点旋转 + childView.setRotation(xFraction * 15); //这里最多旋转15度 + + if (viewHolder instanceof CardHolder) { + CardHolder holder = (CardHolder) viewHolder; + if (dX > 0) { + //右滑,显示爱心 +// holder.iv_love.setAlpha(xFraction); + } else if (dX < 0) { + //左滑,显示叉,注意这里xFraction为负数,所以要取反 +// holder.iv_del.setAlpha(-xFraction); + } else { +// holder.iv_love.setAlpha(0f); +// holder.iv_del.setAlpha(0f); + } + } + } + } + } + + @Override + public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) { +// Log.i(TAG, "getSwipeThreshold: "); +// if (isUpOrDown(viewHolder.itemView)) { //如果是向上或者向下滑动 +// return Float.MAX_VALUE; //就返回阈值为很大 +// } + return super.getSwipeThreshold(viewHolder); + } + + /** + * 获得逃脱(swipe)速度 + * + * @param defaultValue + * @return + */ + @Override + public float getSwipeEscapeVelocity(float defaultValue) { + Log.d(TAG, "getSwipeEscapeVelocity: " + defaultValue); + View topView = mRecyclerView.getChildAt(mRecyclerView.getChildCount() - 1); + if (isUpOrDown(topView)) { //如果是向上或者向下滑动 + return Float.MAX_VALUE; //就返回阈值为很大 + } + return super.getSwipeEscapeVelocity(defaultValue); + } + + + /** + * 获得swipe的速度阈值 + * + * @param defaultValue + * @return + */ + @Override + public float getSwipeVelocityThreshold(float defaultValue) { + Log.d(TAG, "getSwipeVelocityThreshold: " + defaultValue); + View topView = mRecyclerView.getChildAt(mRecyclerView.getChildCount() - 1); + if (isUpOrDown(topView)) { //如果是向上或者向下滑动 + return Float.MAX_VALUE; //就返回阈值为很大 + } + return super.getSwipeVelocityThreshold(defaultValue); + } + + /** + * 判断是否是向上滑或者向下滑 + */ + private boolean isUpOrDown(View topView) { + float x = topView.getX(); + float y = topView.getY(); + int left = topView.getLeft(); + int top = topView.getTop(); + if (Math.pow(x - left, 2) > Math.pow(y - top, 2)) { + //水平方向大于垂直方向 +// Log.i(TAG, "isUpOrDown: 不是"); + return false; + } else { + return true; +// Log.i(TAG, "isUpOrDown: 是"); + } + } +} diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/cardconfig/SwipeCardLayoutManager.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/cardconfig/SwipeCardLayoutManager.java new file mode 100644 index 0000000..f55e40f --- /dev/null +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/cardconfig/SwipeCardLayoutManager.java @@ -0,0 +1,98 @@ +package com.example.ninefourone.nutritionmaster.cardconfig; + +import android.os.Build; +import android.support.annotation.RequiresApi; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.view.ViewGroup; + +/** + * Created by ScorpioMiku on 2018/9/2. + */ + +public class SwipeCardLayoutManager extends RecyclerView.LayoutManager { + @Override + public RecyclerView.LayoutParams generateDefaultLayoutParams() { + return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } + + /** + * 在这里面给子view布局,也就是item + * + * @param recycler + * @param state + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + //在布局之前,将所有的子View先Detach掉,放入到Scrap缓存中 + detachAndScrapAttachedViews(recycler); + + //拿到总item数量 + int itemCount = getItemCount(); + if (itemCount < 1) {//没有item当然就没必要布局了 + return; + } + int bottomPosition; //用来记录最底层view的postion + + if (itemCount < CardConfig.MAX_SHOW_COUNT) { //如果不足最大数量(4个) + //那么最底层就是最后一条数据对应的position + bottomPosition = itemCount - 1; + } else { + //否则最底层就是第MAX_SHOW_COUNT(4)条数据对应的position + bottomPosition = CardConfig.MAX_SHOW_COUNT - 1; + } + + /** + * 这里开始布局且绘制子view + * 注意:这里要先从最底层开始绘制,因为后绘制的才能覆盖先绘制的, + * 滑动的时候是滑最上面一层的,也就是后绘制的 + * position也是层数 + */ + for (int position = bottomPosition; position >= 0; position--) { + //根据position找recycler要itemview + View view = recycler.getViewForPosition(position); + //将子View添加至RecyclerView中 + addView(view); + //测量子view并且把Margin也作为子控件的一部分 + measureChildWithMargins(view, 0, 0); + //宽度空隙 getWidth()得到Recycler控件的宽度,getDecoratedMeasuredWidth(view)拿到子view的宽度 + int widthSpace = getWidth() - getDecoratedMeasuredWidth(view); + //高度空隙 + int heightSpace = getHeight() - getDecoratedMeasuredHeight(view); + //给子view布局,这里居中了 + layoutDecoratedWithMargins(view, widthSpace / 2, 0, + widthSpace / 2 + getDecoratedMeasuredWidth(view), + getDecoratedMeasuredHeight(view) + 0); + + /** + * 下面要调整每一层itemview的的大小及Y轴和Z轴的偏移 + * 最上面一层(第0层)的Scale为1,translationY为0 + * 依次往下,每层比上面一层: + * (1)Scale相差0.05f + * (2)translationY相差7dp + * (3)translationZ相差1dp + * + * 注意:最后一层,除了水平方向的大小其他都与上一层一样,所以要特殊判断 + */ + if (position > 0) { //大于0就是不是最上面那层 + //依次往下,每层都要水平方向缩小 + view.setScaleX(1 - CardConfig.SCALE_GAP * position); + if (position < CardConfig.MAX_SHOW_COUNT - 1) { + //如果,不是最后一层,就都要调整 + view.setScaleY(1 - CardConfig.SCALE_GAP * position); //垂直方向缩小 + view.setTranslationY(CardConfig.TRANS_Y_GAP * position); //向下平移 + view.setTranslationZ(CardConfig.TRANS_Z_GAP * (CardConfig.MAX_SHOW_COUNT - 1 - position)); //Z轴方向的平移 + } else { + //否则,就是最后一层,与上一层保持一致 + view.setScaleY(1 - CardConfig.SCALE_GAP * (position - 1)); //垂直方向缩小 + view.setTranslationY(CardConfig.TRANS_Y_GAP * (position - 1)); //向下平移 + view.setTranslationZ(CardConfig.TRANS_Z_GAP * (CardConfig.MAX_SHOW_COUNT - 1 - (position - 1))); //Z轴方向的平移 + } + } else { + //否则,是第0层(最上面那层),只需调整Z轴高度 + view.setTranslationZ(CardConfig.TRANS_Z_GAP * (CardConfig.MAX_SHOW_COUNT - 1)); //Z轴方向的平移 + } + } + } +} diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/modules/MainActivity.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/modules/MainActivity.java index 8c08218..1d2d6ef 100644 --- a/app/src/main/java/com/example/ninefourone/nutritionmaster/modules/MainActivity.java +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/modules/MainActivity.java @@ -1,31 +1,54 @@ package com.example.ninefourone.nutritionmaster.modules; +import android.annotation.SuppressLint; +import android.content.Intent; +import android.os.Build; import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.RequiresApi; +import android.support.v7.widget.Toolbar; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.FrameLayout; import android.widget.LinearLayout; - -import com.ToxicBakery.viewpager.transforms.AccordionTransformer; -import com.ToxicBakery.viewpager.transforms.CubeInTransformer; import com.ToxicBakery.viewpager.transforms.CubeOutTransformer; -import com.ToxicBakery.viewpager.transforms.DepthPageTransformer; -import com.ToxicBakery.viewpager.transforms.FlipHorizontalTransformer; -import com.ToxicBakery.viewpager.transforms.FlipVerticalTransformer; -import com.ToxicBakery.viewpager.transforms.RotateUpTransformer; -import com.ToxicBakery.viewpager.transforms.StackTransformer; -import com.ToxicBakery.viewpager.transforms.TabletTransformer; -import com.ToxicBakery.viewpager.transforms.ZoomInTransformer; +import com.cb.ratingbar.CBRatingBar; import com.example.ninefourone.nutritionmaster.R; import com.example.ninefourone.nutritionmaster.adapter.HomePagerAdapter; import com.example.ninefourone.nutritionmaster.base.BaseActivity; +import com.example.ninefourone.nutritionmaster.camera.FoodMaterialCamera; import com.example.ninefourone.nutritionmaster.ui.NoScrollViewPager; +import com.example.ninefourone.nutritionmaster.utils.MessageUtils; +import com.example.ninefourone.nutritionmaster.utils.PermissionUtils; import com.flyco.tablayout.SlidingTabLayout; +import com.github.mikephil.charting.charts.RadarChart; +import com.github.mikephil.charting.components.Description; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.RadarData; +import com.github.mikephil.charting.data.RadarDataSet; +import com.github.mikephil.charting.data.RadarEntry; +import com.github.mikephil.charting.formatter.IndexAxisValueFormatter; +import com.github.siyamed.shapeimageview.CircularImageView; +import com.miguelcatalan.materialsearchview.MaterialSearchView; import com.mxn.soul.flowingdrawer_core.ElasticDrawer; import com.mxn.soul.flowingdrawer_core.FlowingDrawer; +import com.nightonke.boommenu.BoomButtons.HamButton; +import com.nightonke.boommenu.BoomButtons.OnBMClickListener; +import com.nightonke.boommenu.BoomMenuButton; +import com.orhanobut.logger.Logger; + +import java.util.ArrayList; +import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; + public class MainActivity extends BaseActivity { @BindView(R.id.drawerlayout) @@ -36,6 +59,28 @@ public class MainActivity extends BaseActivity { NoScrollViewPager viewPager; @BindView(R.id.sliding_tab_layout) SlidingTabLayout slidingTabLayout; + @BindView(R.id.bar_cover) + FrameLayout barCover; + @BindView(R.id.cb_rating_bar) + CBRatingBar cbRatingBar; + @BindView(R.id.toolbar_user_avatar) + CircularImageView toolbarUserAvatar; + @BindView(R.id.drawer_user_avatar) + CircularImageView drawerUserAvatar; + // @BindView(R.id.spiderWeb_mainActivity) +// SpiderWebScoreView spiderWebMainActivity; +// @BindView(R.id.layout_mainActivity_circular) +// CircularLayout layoutMainActivityCircular; + // @BindView(R.id.search_button) +// ImageView searchButton; + @BindView(R.id.search_view) + MaterialSearchView searchView; + @BindView(R.id.tool_bar) + Toolbar toolBar; + @BindView(R.id.boom_menu_button) + BoomMenuButton boomMenuButton; + @BindView(R.id.spider_view) + RadarChart spiderView; @Override @@ -47,10 +92,13 @@ public class MainActivity extends BaseActivity { public void initViews(Bundle savedInstanceState) { mDrawer.setTouchMode(ElasticDrawer.TOUCH_MODE_BEZEL); mDrawer.setOnDrawerStateChangeListener(new ElasticDrawer.OnDrawerStateChangeListener() { + @SuppressLint("ResourceAsColor") @Override public void onDrawerStateChange(int oldState, int newState) { if (newState == ElasticDrawer.STATE_CLOSED) { -// Logger.i("Drawer STATE_CLOSED"); + barCover.setVisibility(View.INVISIBLE); + } else { + barCover.setVisibility(View.VISIBLE); } } @@ -59,7 +107,10 @@ public class MainActivity extends BaseActivity { // Logger.i("openRatio=" + openRatio + " ,offsetPixels=" + offsetPixels); } }); + initSpiderView(); initViewPager(); + initSearchView(); + initBMB(); } /** @@ -75,7 +126,6 @@ public class MainActivity extends BaseActivity { viewPager.setPageTransformer(true, new CubeOutTransformer()); slidingTabLayout.setViewPager(viewPager); viewPager.setCurrentItem(1); - } @Override @@ -88,13 +138,34 @@ public class MainActivity extends BaseActivity { } + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); // TODO: add setContentView(...) invocation ButterKnife.bind(this); + Logger.d("oncreate"); + setSupportActionBar(toolBar); + askPermission(); } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + Logger.d("oncreateMenu"); + getMenuInflater().inflate(R.menu.menu_main, menu); + MenuItem item = menu.findItem(R.id.id_action_search); + searchView.setMenuItem(item); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + Logger.d("prepareMenu"); + return super.onPrepareOptionsMenu(menu); + } + + /** * 点击事件 */ @@ -102,4 +173,136 @@ public class MainActivity extends BaseActivity { public void onViewClicked() { mDrawer.openMenu(); } + + + /** + * 初始化蛛网图 + */ + private void initSpiderView() { + float[] scores = {9.1f, 5.5f, 7.7f, 8.9f, 4.6f}; + String[] flags = {"糖分", "淡水", "蛋白质", "维生素", "矿物质"}; + + List radarEntries = new ArrayList<>(); + for (int i = 0; i < flags.length; i++) { + RadarEntry radarEntry = new RadarEntry(scores[i], flags[i]); + radarEntries.add(radarEntry); + } + + Description description = new Description(); + description.setText(""); + spiderView.setDescription(description); + + + spiderView.setWebLineWidth(1.5f); + // 内部线条宽度,外面的环状线条 + spiderView.setWebLineWidthInner(1.5f); + // 所有线条WebLine透明度 + spiderView.setWebAlpha(300); + + + Legend legend = spiderView.getLegend(); + legend.setEnabled(false); + + XAxis xAxis = spiderView.getXAxis(); + // X坐标值字体样式 + // xAxis.setTypeface(tf); + // X坐标值字体大小 + xAxis.setTextSize(8f); + ArrayList xVals = new ArrayList(); + for (String flag : flags) { + xVals.add(flag); + } + xAxis.setValueFormatter(new IndexAxisValueFormatter(xVals)); + + + YAxis yAxis = spiderView.getYAxis(); + // Y坐标值字体样式 + // yAxis.setTypeface(tf); + // Y坐标值字体大小 + yAxis.setTextSize(0f); + // Y坐标值是否从0开始 + yAxis.setStartAtZero(true); + // 是否显示y值在图表上 + yAxis.setDrawLabels(false); + yAxis.setAxisLineWidth(2f); + RadarDataSet set = new RadarDataSet(radarEntries, "体质情况"); +// set.setColor(R.color.bar_open); + set.setLineWidth(0.5f); + set.setDrawFilled(true); +// set.setFillColor(R.color.spider_view_color); +// set.resetColors(); + RadarData data = new RadarData(set); + data.setDrawValues(false); + spiderView.setData(data); + spiderView.setTouchEnabled(false); + spiderView.invalidate(); + + + } + + /** + * 初始化SearchView + */ + private void initSearchView() { + searchView.setOnQueryTextListener(new MaterialSearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + MessageUtils.MakeToast(query); + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { +// MessageUtils.MakeToast(newText); + return false; + } + }); + + searchView.setOnSearchViewListener(new MaterialSearchView.SearchViewListener() { + @Override + public void onSearchViewShown() { +// MessageUtils.MakeToast("Shown"); + } + + @Override + public void onSearchViewClosed() { +// MessageUtils.MakeToast("closed"); + } + }); + + } + + /** + * 初始化悬浮按钮 + */ + private void initBMB() { + HamButton.Builder builder = new HamButton.Builder() + .normalImageRes(R.drawable.food_material) + .normalTextRes(R.string.food_meterial_title) + .listener(new OnBMClickListener() { + @Override + public void onBoomButtonClick(int index) { + Intent cameraIntent = new Intent(MainActivity.this, FoodMaterialCamera.class); + startActivity(cameraIntent); + } + }); + boomMenuButton.addBuilder(builder); + HamButton.Builder builder2 = new HamButton.Builder() + .normalImageRes(R.drawable.foods) + .normalTextRes(R.string.food_title); + boomMenuButton.addBuilder(builder2); + } + + /** + * 请求权限 + */ + private void askPermission() { + PermissionUtils.requestCameraPermission(this); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + MessageUtils.MakeToast("权限赋予成功"); + } } diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/modules/viewpagerfragments/bodyinformation/BodyInformationFragment.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/modules/viewpagerfragments/bodyinformation/BodyInformationFragment.java index c36c892..ceda657 100644 --- a/app/src/main/java/com/example/ninefourone/nutritionmaster/modules/viewpagerfragments/bodyinformation/BodyInformationFragment.java +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/modules/viewpagerfragments/bodyinformation/BodyInformationFragment.java @@ -1,20 +1,34 @@ package com.example.ninefourone.nutritionmaster.modules.viewpagerfragments.bodyinformation; -import android.graphics.Color; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.TextView; -import com.akexorcist.roundcornerprogressbar.IconRoundCornerProgressBar; -import com.akexorcist.roundcornerprogressbar.RoundCornerProgressBar; import com.example.ninefourone.nutritionmaster.R; import com.example.ninefourone.nutritionmaster.base.BaseFragment; -import com.gelitenight.waveview.library.WaveView; +import com.example.ninefourone.nutritionmaster.utils.ChartDrawer; +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.data.Entry; +import com.today.step.lib.ISportStepInterface; +import com.today.step.lib.TodayStepManager; +import com.today.step.lib.TodayStepService; + +import java.util.ArrayList; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.Unbinder; +import me.itangqi.waveloadingview.WaveLoadingView; /** * Created by ScorpioMiku on 2018/8/26. @@ -22,13 +36,27 @@ import butterknife.Unbinder; public class BodyInformationFragment extends BaseFragment { - @BindView(R.id.progress_1) - RoundCornerProgressBar progress1; - @BindView(R.id.progress_2) - IconRoundCornerProgressBar progress2; + Unbinder unbinder; - @BindView(R.id.wave_view) - WaveView waveView; + @BindView(R.id.step_text_view) + TextView stepTextView; + @BindView(R.id.waveLoadingView) + WaveLoadingView waveLoadingView; + @BindView(R.id.weight_line_chart) + LineChart weightLineChart; + @BindView(R.id.step_line_chart) + LineChart stepLineChart; + + private int stepCount = 0; + private static final int REFRESH_STEP_WHAT = 0; + + //循环取当前时刻的步数中间的间隔时间 + private long TIME_INTERVAL_REFRESH = 500; + + private Handler mDelayHandler = new Handler(new TodayStepCounterCall()); + + private ISportStepInterface iSportStepInterface; + @Override public int getLayoutResId() { @@ -37,31 +65,8 @@ public class BodyInformationFragment extends BaseFragment { @Override public void initView(Bundle state) { - progress1.setProgressColor(Color.parseColor("#ed3b27")); - progress1.setProgressBackgroundColor(Color.parseColor("#808080")); - progress1.setMax(70); - progress1.setProgress(15); - - int progressColor1 = progress1.getProgressColor(); - int backgroundColor1 = progress1.getProgressBackgroundColor(); - int max1 = (int) progress1.getMax(); - int progress_1 = (int) progress1.getProgress(); - - - progress2.setProgressColor(Color.parseColor("#56d2c2")); - progress2.setProgressBackgroundColor(Color.parseColor("#757575")); - progress2.setIconBackgroundColor(Color.parseColor("#38c0ae")); - progress2.setMax(550); - progress2.setProgress(147); - progress2.setIconImageResource(R.drawable.test_avatar); - - int progressColor2 = progress2.getProgressColor(); - int backgroundColor2 = progress2.getProgressBackgroundColor(); - int headerColor2 = progress2.getColorIconBackground(); - int max2 = (int) progress2.getMax(); - int progress_2 = (int) progress2.getProgress(); - - waveView.setShapeType(WaveView.ShapeType.CIRCLE); + initStepCounter(); + initChart(); } @@ -82,4 +87,91 @@ public class BodyInformationFragment extends BaseFragment { super.onDestroyView(); unbinder.unbind(); } + + /** + * 计步器初始化 + */ + private void initStepCounter() { + TodayStepManager.init(getActivity().getApplication()); + //开启计步 + Intent stepCounterStart = new Intent(getActivity(), TodayStepService.class); + getActivity().startService(stepCounterStart); + getActivity().bindService(stepCounterStart, new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + iSportStepInterface = ISportStepInterface.Stub.asInterface(service); + try { + stepCount = iSportStepInterface.getCurrentTimeSportStep(); + updateStepCount(); + } catch (RemoteException e) { + e.printStackTrace(); + } + mDelayHandler.sendEmptyMessageDelayed(REFRESH_STEP_WHAT, TIME_INTERVAL_REFRESH); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + + } + }, Context.BIND_AUTO_CREATE); + } + + /** + * 改变记步UI中的数字 + */ + private void updateStepCount() { + stepTextView.setText(stepCount + ""); + } + + + /** + * 定时器,修改UI + */ + class TodayStepCounterCall implements Handler.Callback { + + @Override + public boolean handleMessage(Message msg) { + switch (msg.what) { + case REFRESH_STEP_WHAT: { + //每隔500毫秒获取一次计步数据刷新UI + if (null != iSportStepInterface) { + int step = 0; + try { + step = iSportStepInterface.getCurrentTimeSportStep(); + } catch (RemoteException e) { + e.printStackTrace(); + } + if (stepCount != step) { + stepCount = step; + updateStepCount(); + } + } + mDelayHandler.sendEmptyMessageDelayed(REFRESH_STEP_WHAT, TIME_INTERVAL_REFRESH); + break; + } + } + return false; + } + } + + + /** + * 初始化表格 + */ + private void initChart() { + ArrayList weightPointValues = new ArrayList<>(); + for (int i = 1; i < 15; i++) { + int y = (int) (Math.random() * 20); + weightPointValues.add(new Entry(i, y)); + } + ChartDrawer.initSingleLineChart(weightLineChart, weightPointValues, "体重"); + + ArrayList stepPointValues = new ArrayList<>(); + for (int i = 1; i < 15; i++) { + int y = (int) (Math.random() * 20); + stepPointValues.add(new Entry(i, y)); + } + ChartDrawer.initSingleLineChart(stepLineChart, stepPointValues, "步数"); + } + } diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/modules/viewpagerfragments/customization/CustomizationFragment.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/modules/viewpagerfragments/customization/CustomizationFragment.java new file mode 100644 index 0000000..7844d9a --- /dev/null +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/modules/viewpagerfragments/customization/CustomizationFragment.java @@ -0,0 +1,111 @@ +package com.example.ninefourone.nutritionmaster.modules.viewpagerfragments.customization; + +import android.os.Bundle; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.example.ninefourone.nutritionmaster.R; +import com.example.ninefourone.nutritionmaster.adapter.CardAdapter; +import com.example.ninefourone.nutritionmaster.adapter.CardHolder; +import com.example.ninefourone.nutritionmaster.base.BaseFragment; +import com.example.ninefourone.nutritionmaster.bean.DailyCard; +import com.example.ninefourone.nutritionmaster.cardconfig.CardConfig; +import com.example.ninefourone.nutritionmaster.cardconfig.CardItemTouchCallBack; +import com.example.ninefourone.nutritionmaster.cardconfig.SwipeCardLayoutManager; +import com.example.ninefourone.nutritionmaster.utils.ConstantUtils; + +import java.util.ArrayList; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.Unbinder; + +/** + * Created by ScorpioMiku on 2018/8/26. + */ + +public class CustomizationFragment extends BaseFragment { + + @BindView(R.id.card_recycler_view) + RecyclerView cardRecyclerView; + Unbinder unbinder; + + private CardAdapter cardAdapter; + private CardHolder cardHolder; + private ArrayList mDataList = new ArrayList<>(); + + private int[] picList = new int[]{ + R.drawable.monday, + R.drawable.tuesday, + R.drawable.wednesday, + R.drawable.thursday, + R.drawable.friday, + R.drawable.saturday, + R.drawable.sunday + }; + + + @Override + public int getLayoutResId() { + return R.layout.customization; + } + + @Override + public void initView(Bundle state) { + loadData(); + initCardRecyclerView(); + } + + + public static BaseFragment getInstance() { + return new CustomizationFragment(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + // TODO: inflate a fragment view + View rootView = super.onCreateView(inflater, container, savedInstanceState); + unbinder = ButterKnife.bind(this, rootView); + return rootView; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + unbinder.unbind(); + } + + @Override + protected void loadData() { + super.loadData(); + for (int i = 1; i <= 7; i++) { +// mDataList.add("周" + ConstantUtils.arab2Chinese(i) + "美食普"); + DailyCard dailyCard = new DailyCard( + "周" + ConstantUtils.arab2Chinese(i) + "美食普", + "这里放描述", + picList[i - 1] + ); + mDataList.add(dailyCard); + } + } + + /** + * 初始化card recyclerview + */ + private void initCardRecyclerView() { + CardConfig.initConfig(getContext()); + cardRecyclerView.setLayoutManager(new SwipeCardLayoutManager()); + cardAdapter = new CardAdapter(getContext(), mDataList); + cardRecyclerView.setAdapter(cardAdapter); + + CardItemTouchCallBack callBack = new CardItemTouchCallBack(cardRecyclerView, cardAdapter, mDataList); + //2.创建ItemTouchHelper并把callBack传进去 + ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callBack); + //3.与RecyclerView关联起来 + itemTouchHelper.attachToRecyclerView(cardRecyclerView); + } + +} diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/modules/viewpagerfragments/page1/Page1.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/modules/viewpagerfragments/page1/Page1.java deleted file mode 100644 index 24a795d..0000000 --- a/app/src/main/java/com/example/ninefourone/nutritionmaster/modules/viewpagerfragments/page1/Page1.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.example.ninefourone.nutritionmaster.modules.viewpagerfragments.page1; - -import android.os.Bundle; - -import com.example.ninefourone.nutritionmaster.R; -import com.example.ninefourone.nutritionmaster.base.BaseFragment; - -/** - * Created by ScorpioMiku on 2018/8/26. - */ - -public class Page1 extends BaseFragment { - @Override - public int getLayoutResId() { - return R.layout.page_1; - } - - @Override - public void initView(Bundle state) { - - } - - - public static BaseFragment getInstance() { - return new Page1(); - } -} diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/step/StepStarter.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/step/StepStarter.java new file mode 100644 index 0000000..895a9da --- /dev/null +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/step/StepStarter.java @@ -0,0 +1,28 @@ +package com.example.ninefourone.nutritionmaster.step; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import com.example.ninefourone.nutritionmaster.NutritionMaster; +import com.example.ninefourone.nutritionmaster.modules.MainActivity; +import com.orhanobut.logger.Logger; + + +public class StepStarter extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + // TODO: This method is called when the BroadcastReceiver is receiving + // an Intent broadcast. +// throw new UnsupportedOperationException("Not yet implemented"); + NutritionMaster nutritionMaster = (NutritionMaster) context.getApplicationContext(); + if (!nutritionMaster.isForeground()) { + Intent mainIntent = new Intent(context, MainActivity.class); + mainIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(mainIntent); + }else { + Logger.d("已经在计步了"); + } + } +} diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/utils/CalculateUtils.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/utils/CalculateUtils.java new file mode 100644 index 0000000..65f90e8 --- /dev/null +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/utils/CalculateUtils.java @@ -0,0 +1,59 @@ +package com.example.ninefourone.nutritionmaster.utils; + +/** + * Created by ScorpioMiku on 2018/8/29. + */ + +public class CalculateUtils { + /** + * 计算BMI值 BMI值計算公式: BMI = 體重(公斤) / 身高2(公尺2) + * + * @param height + * @param weight + * @return + */ + public static float BMI(float height, float weight) { + if (height > 10) { + height = height / 100; + } + return weight / (height * height); + } + + /** + * 通过身高获得健康的体重 + * + * @param height + * @return + */ + public static float[] standardH2W(float height) { + if (height > 10) { + height = height / 100; + } + float min; + float max; + min = (float) 18.5 * height * height; + max = (float) 14 * height * height; + float[] re = {min, max}; + return re; + } + + /** + * 根据BMI得到体质情况 + * + * @param BMI + * @return + */ + public static String bodyStatus(float BMI) { + if (BMI < 18.5) { + return "轻体重"; + } else if (BMI < 24) { + return "健康体重"; + } else if (BMI < 27) { + return "轻度肥胖"; + } else if (BMI < 30) { + return "中度肥胖"; + } else { + return "重度肥胖"; + } + } +} diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/utils/ChartDrawer.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/utils/ChartDrawer.java new file mode 100644 index 0000000..b8eab0e --- /dev/null +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/utils/ChartDrawer.java @@ -0,0 +1,203 @@ +package com.example.ninefourone.nutritionmaster.utils; + +import android.content.Context; +import android.graphics.Color; + +import com.example.ninefourone.nutritionmaster.R; +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.components.Description; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.IndexAxisValueFormatter; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.orhanobut.logger.Logger; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by ScorpioMiku on 2018/8/30. + */ + +public class ChartDrawer { + + /** + * 创建一条折线 + * + * @param index + * @param datas + * @param linename + * @return + */ + public static void initSingleLineChart(LineChart mLineChart, ArrayList pointValues, String linename) { +// if (index.length != datas.length) { +// MessageUtils.MakeToast("index长度与datas长度不符"); +// Logger.d("长度不符"); +// return; +// } else { +// ArrayList xValues = new ArrayList<>(); +// for (int i = 0; i < datas.length; i++) { +// xValues.add(index[i] + ""); +// } +// ArrayList pointValues = new ArrayList<>(); +// for (int i = 0; i < datas.length; i++) { +// pointValues.add(new Entry(datas[i], i)); +// } +// LineDataSet dataSet = new LineDataSet(pointValues, linename); +// dataSet.setLineWidth(1.75f); +// dataSet.setColor(R.color.colorPrimary); +// +// ArrayList dataSetArrayList = new ArrayList<>(); +// dataSetArrayList.add(dataSet); +// +// LineData lineData = new LineData(dataSetArrayList); + + //表格属性 +// lineChart.setDrawBorders(false); +// lineChart.setDrawGridBackground(false); //表格颜色 +// lineChart.setGridBackgroundColor(Color.GRAY & 0x70FFFFFF); //表格的颜色,设置一个透明度 +// lineChart.setTouchEnabled(true); //可点击 +// lineChart.setDragEnabled(true); //可拖拽 +// lineChart.setScaleEnabled(true); //可缩放 +// lineChart.setPinchZoom(false); +// lineChart.setBackgroundColor(Color.WHITE); //设置背景颜色 +// +// lineChart.setData(lineData); +// +// Legend mLegend = lineChart.getLegend(); //设置标示,就是那个一组y的value的 +// mLegend.setForm(Legend.LegendForm.SQUARE); //样式 +// mLegend.setFormSize(6f); //字体 +// mLegend.setTextColor(Color.GRAY); //颜色 +// lineChart.setVisibleXRange(0, 4); //x轴可显示的坐标范围 +// XAxis xAxis = lineChart.getXAxis(); //x轴的标示 +// xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); //x轴位置 +// xAxis.setTextColor(Color.GRAY); //字体的颜色 +// xAxis.setTextSize(10f); //字体大小 +// xAxis.setGridColor(Color.GRAY);//网格线颜色 +// xAxis.setDrawGridLines(false); //不显示网格线 +// YAxis axisLeft = lineChart.getAxisLeft(); //y轴左边标示 +// YAxis axisRight = lineChart.getAxisRight(); //y轴右边标示 +// axisLeft.setTextColor(Color.GRAY); //字体颜色 +// axisLeft.setTextSize(10f); //字体大小 +// //axisLeft.setAxisMaxValue(800f); //最大值 +// axisLeft.setLabelCount(5, true); //显示格数 +// axisLeft.setGridColor(Color.GRAY); //网格线颜色 +// +// axisRight.setDrawAxisLine(false); +// axisRight.setDrawGridLines(false); +// axisRight.setDrawLabels(false); +// +// //设置动画效果 +// lineChart.animateY(2000, Easing.EasingOption.Linear); +// lineChart.animateX(2000, Easing.EasingOption.Linear); +// lineChart.invalidate(); + mLineChart.setNoDataText("没有数据喔~~"); + //设置是否绘制chart边框的线 + mLineChart.setDrawBorders(true); + //设置chart边框线颜色 + mLineChart.setBorderColor(Color.GRAY); + //设置chart边框线宽度 + mLineChart.setBorderWidth(1f); + //设置chart是否可以触摸 + mLineChart.setTouchEnabled(true); + //设置是否可以拖拽 + mLineChart.setDragEnabled(true); + //设置是否可以缩放 x和y,默认true + mLineChart.setScaleEnabled(false); + //设置是否可以通过双击屏幕放大图表。默认是true + mLineChart.setDoubleTapToZoomEnabled(false); + //设置chart动画 + mLineChart.animateXY(1000, 1000); + + //=========================设置图例========================= + // 像"□ xxx"就是图例 + Legend legend = mLineChart.getLegend(); + legend.setEnabled(false); + + + //=======================设置X轴显示效果================== + XAxis xAxis = mLineChart.getXAxis(); + //是否启用X轴 + xAxis.setEnabled(true); + //是否绘制X轴线 + xAxis.setDrawAxisLine(true); + //设置X轴上每个竖线是否显示 + xAxis.setDrawGridLines(true); + //设置是否绘制X轴上的对应值(标签) + xAxis.setDrawLabels(true); + //设置X轴显示位置 + xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); + //设置竖线为虚线样式 + // xAxis.enableGridDashedLine(10f, 10f, 0f); + //设置x轴标签数 + xAxis.setLabelCount(8,false); + xAxis.setTextSize(5); + //图表第一个和最后一个label数据不超出左边和右边的Y轴 + // xAxis.setAvoidFirstLastClipping(true); + xAxis.setDrawGridLines(false);//设置x轴上每个点对应的线 + + //修改横轴 + //准备好每个点对应的x轴数值 + List list = new ArrayList<>(); + for (int i = 0; i < pointValues.size(); i++) { + list.add(String.valueOf(i+1).concat("号")); + } + xAxis.setValueFormatter(new IndexAxisValueFormatter(list)); + + + YAxis rightAxis = mLineChart.getAxisRight(); + rightAxis.setDrawAxisLine(false); + rightAxis.setDrawGridLines(false); + rightAxis.setEnabled(false); + YAxis leftAxis = mLineChart.getAxisLeft(); + leftAxis.setEnabled(false); + leftAxis.setDrawAxisLine(false); + + //点构成的某条线 + LineDataSet lineDataSet = new LineDataSet(pointValues, "体重"); + //设置该线的颜色 + lineDataSet.setColor(R.color.color_bar_background); + //设置每个点的颜色 + lineDataSet.setCircleColor(0xff0171c9); + //设置该线的宽度 + lineDataSet.setLineWidth(0f); + + //设置每个坐标点的圆大小 + //lineDataSet.setCircleRadius(1f); + //设置是否画圆 + lineDataSet.setDrawCircles(false); + // 设置平滑曲线模式 + // lineDataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER); + //设置线一面部分是否填充颜色 + lineDataSet.setDrawFilled(true); + //设置填充的颜色 + lineDataSet.setFillColor(0x20A0FF); + //设置是否显示点的坐标值 + lineDataSet.setDrawValues(false); + + + //隐藏x轴描述 + Description description = new Description(); + description.setEnabled(false); + mLineChart.setDescription(description); + mLineChart.setDrawBorders(false); + //线的集合(可单条或多条线) + List dataSets = new ArrayList<>(); + dataSets.add(lineDataSet); + //把要画的所有线(线的集合)添加到LineData里 + LineData lineData = new LineData(dataSets); + //把最终的数据setData + mLineChart.setData(lineData); + mLineChart.invalidate(); + + } + + +} diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/utils/ConstantUtils.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/utils/ConstantUtils.java index 9b4ef5d..a982033 100644 --- a/app/src/main/java/com/example/ninefourone/nutritionmaster/utils/ConstantUtils.java +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/utils/ConstantUtils.java @@ -1,8 +1,31 @@ package com.example.ninefourone.nutritionmaster.utils; +import java.util.ArrayList; + /** * Created by ScorpioMiku on 2018/8/26. */ public class ConstantUtils { + public static String arab2Chinese(int number) { + switch (number) { + case 1: + return "一"; + case 2: + return "二"; + case 3: + return "三"; + case 4: + return "四"; + case 5: + return "五"; + case 6: + return "六"; + case 7: + return "日"; + default: + return ""; + } + } + } diff --git a/app/src/main/java/com/example/ninefourone/nutritionmaster/utils/PermissionUtils.java b/app/src/main/java/com/example/ninefourone/nutritionmaster/utils/PermissionUtils.java new file mode 100644 index 0000000..45a0be0 --- /dev/null +++ b/app/src/main/java/com/example/ninefourone/nutritionmaster/utils/PermissionUtils.java @@ -0,0 +1,44 @@ +package com.example.ninefourone.nutritionmaster.utils; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Build; +import android.support.v4.app.ActivityCompat; + +/** + * Created by ScorpioMiku on 2018/9/3. + */ + +public class PermissionUtils { + public static final int REQUEST_CAMERA = 1056; + + /** + * 动态获取相机权限 + * + * @param activity + */ + public static void requestCameraPermission(Activity activity) { + if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) + != PackageManager.PERMISSION_GRANTED) { + /** + * 直接请求四个权限 + */ + ActivityCompat.requestPermissions(activity, + new String[]{ + Manifest.permission.CAMERA, + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE + }, REQUEST_CAMERA); +// MessageUtils.MakeToast("权限赋予成功"); + } else { + /** + * 否则 + */ + } + + } +} diff --git a/app/src/main/res/drawable-v24/selector.xml b/app/src/main/res/drawable-v24/selector.xml new file mode 100644 index 0000000..38a6b39 --- /dev/null +++ b/app/src/main/res/drawable-v24/selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/body_bg.png b/app/src/main/res/drawable/body_bg.png new file mode 100644 index 0000000..ded6ac3 Binary files /dev/null and b/app/src/main/res/drawable/body_bg.png differ diff --git a/app/src/main/res/drawable/food_material.jpg b/app/src/main/res/drawable/food_material.jpg new file mode 100644 index 0000000..af1bc94 Binary files /dev/null and b/app/src/main/res/drawable/food_material.jpg differ diff --git a/app/src/main/res/drawable/food_test.jpg b/app/src/main/res/drawable/food_test.jpg new file mode 100644 index 0000000..59d9a4c Binary files /dev/null and b/app/src/main/res/drawable/food_test.jpg differ diff --git a/app/src/main/res/drawable/foods.jpg b/app/src/main/res/drawable/foods.jpg new file mode 100644 index 0000000..5e7e428 Binary files /dev/null and b/app/src/main/res/drawable/foods.jpg differ diff --git a/app/src/main/res/drawable/friday.jpg b/app/src/main/res/drawable/friday.jpg new file mode 100644 index 0000000..fe6abbb Binary files /dev/null and b/app/src/main/res/drawable/friday.jpg differ diff --git a/app/src/main/res/drawable/ic_bmi.xml b/app/src/main/res/drawable/ic_bmi.xml new file mode 100644 index 0000000..470e941 --- /dev/null +++ b/app/src/main/res/drawable/ic_bmi.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_exchange.xml b/app/src/main/res/drawable/ic_exchange.xml new file mode 100644 index 0000000..2b6e6b5 --- /dev/null +++ b/app/src/main/res/drawable/ic_exchange.xml @@ -0,0 +1,4 @@ + + + diff --git a/app/src/main/res/drawable/ic_height.xml b/app/src/main/res/drawable/ic_height.xml new file mode 100644 index 0000000..803f5c5 --- /dev/null +++ b/app/src/main/res/drawable/ic_height.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/drawable/ic_infor_weight.xml b/app/src/main/res/drawable/ic_infor_weight.xml new file mode 100644 index 0000000..71f19e4 --- /dev/null +++ b/app/src/main/res/drawable/ic_infor_weight.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/drawable/ic_ok.xml b/app/src/main/res/drawable/ic_ok.xml new file mode 100644 index 0000000..400fd38 --- /dev/null +++ b/app/src/main/res/drawable/ic_ok.xml @@ -0,0 +1,4 @@ + + + diff --git a/app/src/main/res/drawable/ic_power.xml b/app/src/main/res/drawable/ic_power.xml new file mode 100644 index 0000000..ab9923f --- /dev/null +++ b/app/src/main/res/drawable/ic_power.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/drawable/ic_record.xml b/app/src/main/res/drawable/ic_record.xml new file mode 100644 index 0000000..d808cb1 --- /dev/null +++ b/app/src/main/res/drawable/ic_record.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_score.xml b/app/src/main/res/drawable/ic_score.xml new file mode 100644 index 0000000..0b3d8a5 --- /dev/null +++ b/app/src/main/res/drawable/ic_score.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml new file mode 100644 index 0000000..6f3f8b5 --- /dev/null +++ b/app/src/main/res/drawable/ic_search.xml @@ -0,0 +1,4 @@ + + + diff --git a/app/src/main/res/drawable/ic_step.xml b/app/src/main/res/drawable/ic_step.xml new file mode 100644 index 0000000..998b680 --- /dev/null +++ b/app/src/main/res/drawable/ic_step.xml @@ -0,0 +1,4 @@ + + + diff --git a/app/src/main/res/drawable/ic_weight.xml b/app/src/main/res/drawable/ic_weight.xml new file mode 100644 index 0000000..28da841 --- /dev/null +++ b/app/src/main/res/drawable/ic_weight.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/icon_black.xml b/app/src/main/res/drawable/icon_black.xml new file mode 100644 index 0000000..0154340 --- /dev/null +++ b/app/src/main/res/drawable/icon_black.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/icon_colorful.png b/app/src/main/res/drawable/icon_colorful.png new file mode 100644 index 0000000..8df889d Binary files /dev/null and b/app/src/main/res/drawable/icon_colorful.png differ diff --git a/app/src/main/res/drawable/monday.jpg b/app/src/main/res/drawable/monday.jpg new file mode 100644 index 0000000..452d4dc Binary files /dev/null and b/app/src/main/res/drawable/monday.jpg differ diff --git a/app/src/main/res/drawable/saturday.jpg b/app/src/main/res/drawable/saturday.jpg new file mode 100644 index 0000000..7757408 Binary files /dev/null and b/app/src/main/res/drawable/saturday.jpg differ diff --git a/app/src/main/res/drawable/sunday.jpg b/app/src/main/res/drawable/sunday.jpg new file mode 100644 index 0000000..bb7e1d8 Binary files /dev/null and b/app/src/main/res/drawable/sunday.jpg differ diff --git a/app/src/main/res/drawable/thursday.jpg b/app/src/main/res/drawable/thursday.jpg new file mode 100644 index 0000000..fa60e1e Binary files /dev/null and b/app/src/main/res/drawable/thursday.jpg differ diff --git a/app/src/main/res/drawable/tuesday.jpg b/app/src/main/res/drawable/tuesday.jpg new file mode 100644 index 0000000..35b4668 Binary files /dev/null and b/app/src/main/res/drawable/tuesday.jpg differ diff --git a/app/src/main/res/drawable/wednesday.jpg b/app/src/main/res/drawable/wednesday.jpg new file mode 100644 index 0000000..374fd97 Binary files /dev/null and b/app/src/main/res/drawable/wednesday.jpg differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 40d848a..c3f12e9 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -11,74 +11,141 @@ app:edMenuSize="230dp" app:edPosition="1"> - + android:layout_height="match_parent" + android:layout_alignParentStart="true" + android:layout_alignParentTop="true"> - + android:layout_height="wrap_content"> + + + + + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + + + + + - + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_drawer_home" /> - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + - + + + + + + - - + android:orientation="horizontal"> + android:layout_width="match_parent" + android:layout_height="match_parent"> - + android:gravity="center" + android:orientation="vertical"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/body_information_fragment.xml b/app/src/main/res/layout/body_information_fragment.xml index c5cf7d4..5a8601f 100644 --- a/app/src/main/res/layout/body_information_fragment.xml +++ b/app/src/main/res/layout/body_information_fragment.xml @@ -1,39 +1,335 @@ - + android:overScrollMode="never" + android:scrollbars="none +"> - - - + android:layout_height="match_parent" + android:orientation="vertical"> - + - + - + - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/cameras_layout.xml b/app/src/main/res/layout/cameras_layout.xml new file mode 100644 index 0000000..8fd2bc9 --- /dev/null +++ b/app/src/main/res/layout/cameras_layout.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/card_item.xml b/app/src/main/res/layout/card_item.xml new file mode 100644 index 0000000..f363533 --- /dev/null +++ b/app/src/main/res/layout/card_item.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/customization.xml b/app/src/main/res/layout/customization.xml new file mode 100644 index 0000000..8ce91da --- /dev/null +++ b/app/src/main/res/layout/customization.xml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/page_1.xml b/app/src/main/res/layout/page_1.xml deleted file mode 100644 index a9ec1dd..0000000 --- a/app/src/main/res/layout/page_1.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/score.xml b/app/src/main/res/layout/score.xml new file mode 100644 index 0000000..ca6abe5 --- /dev/null +++ b/app/src/main/res/layout/score.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml new file mode 100644 index 0000000..c5bb617 --- /dev/null +++ b/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,26 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-xxhdpi/camera_button.png b/app/src/main/res/mipmap-xxhdpi/camera_button.png new file mode 100644 index 0000000..b667458 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/camera_button.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/camera_button_pressed.png b/app/src/main/res/mipmap-xxhdpi/camera_button_pressed.png new file mode 100644 index 0000000..2b6e54e Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/camera_button_pressed.png differ diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 3741717..b2237a2 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -1,8 +1,8 @@ - page1 - page2 + 一周定制 + 今日信息 page3 \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f877446..f3c137e 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,13 +1,24 @@ - #FF03A9F4 - #ff0171c9 + #25d43c + #17a346 #FF00E5FF #FF78909C #FF03A9F4 #FF78909C + #17a346 + #b3b3b3 + #e6e5e5 + #52ec2f + #9ac3f9 + + #ff0000 + + + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index cc6254c..e9c8804 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -12,4 +12,13 @@ 36dp + 120dp + 35dp + 90dp + 200dp + + + 20dp + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index df15394..80a90d1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,8 @@ NutritionMaster + 当前值 + 标准值 + M1080.3,453.7c-77.2,88.2 -214.6,234.4 -214.6,234.4s22,160.2 35.3,269.7c5.9,55.9 -37.5,80.1 -86,58.1 -92.6,-43.4 -233.7,-111 -265.3,-126.4 -32.3,14.7 -174.2,81.6 -267.5,124.9 -49.2,21.3 -92.6,-2.2 -87.5,-58.1 12.5,-109.5 35.3,-269 35.3,-269S91.1,541.9 13.2,453.7c-27.9,-32.3 -9.6,-77.9 44.8,-86 111.7,-19.8 284.4,-51.4 284.4,-51.4s94.8,-163.1 154.3,-263.1C529.8,-7.8 552.6,-0.4 556.3,1c10.3,2.9 26.5,15.4 47,52.2 58.8,99.9 152.1,263.1 152.1,263.1s170.5,31.6 280.7,51.4c53.6,8.1 71.3,54.4 44.1,86z + 食材识别 + 菜品识别 diff --git a/build.gradle b/build.gradle index a1cb46b..14a009b 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,7 @@ allprojects { repositories { google() jcenter() + maven { url 'https://jitpack.io' } } } diff --git a/settings.gradle b/settings.gradle index e7b4def..7691e36 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app' +include ':app', ':todaystepcounterlib' diff --git a/todaystepcounterlib/.gitignore b/todaystepcounterlib/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/todaystepcounterlib/.gitignore @@ -0,0 +1 @@ +/build diff --git a/todaystepcounterlib/build.gradle b/todaystepcounterlib/build.gradle new file mode 100644 index 0000000..af7f3ed --- /dev/null +++ b/todaystepcounterlib/build.gradle @@ -0,0 +1,34 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 26 + defaultConfig { + minSdkVersion 19 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + debug{ + buildConfigField "boolean", "TODAY_STEP_DEBUG", "true" + } + release { + buildConfigField "boolean", "TODAY_STEP_DEBUG", "true" + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(include: ['*.jar'], dir: 'libs') + compile 'com.android.support:appcompat-v7:26.1.0' + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + testCompile 'junit:junit:4.12' + testCompile 'org.robolectric:robolectric:3.0-rc3' + testCompile 'org.mockito:mockito-core:1.+' + compile files('libs/microlog4android-1.0.0.jar') +} diff --git a/todaystepcounterlib/libs/microlog4android-1.0.0.jar b/todaystepcounterlib/libs/microlog4android-1.0.0.jar new file mode 100644 index 0000000..d6fbe99 Binary files /dev/null and b/todaystepcounterlib/libs/microlog4android-1.0.0.jar differ diff --git a/todaystepcounterlib/proguard-rules.pro b/todaystepcounterlib/proguard-rules.pro new file mode 100644 index 0000000..1eec44e --- /dev/null +++ b/todaystepcounterlib/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/jiahongfei/Documents/DeveloperSoftware/adt-bundle-mac-x86_64-20140702/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# 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 diff --git a/todaystepcounterlib/src/main/AndroidManifest.xml b/todaystepcounterlib/src/main/AndroidManifest.xml new file mode 100644 index 0000000..fea9fbf --- /dev/null +++ b/todaystepcounterlib/src/main/AndroidManifest.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/todaystepcounterlib/src/main/aidl/com/today/step/lib/ISportStepInterface.aidl b/todaystepcounterlib/src/main/aidl/com/today/step/lib/ISportStepInterface.aidl new file mode 100644 index 0000000..9913ffa --- /dev/null +++ b/todaystepcounterlib/src/main/aidl/com/today/step/lib/ISportStepInterface.aidl @@ -0,0 +1,35 @@ +// ISportStepInterface.aidl +package com.today.step.lib; + +interface ISportStepInterface { + /** + * 获取当前时间运动步数 + */ + int getCurrentTimeSportStep(); + + /** + * 获取所有步数列表,json格式,如果数据过多建议在线程中获取,否则会阻塞UI线程 + */ + String getTodaySportStepArray(); + + /** + * 根据时间获取步数列表 + * + * @param dateString 格式yyyy-MM-dd + * @return + */ + String getTodaySportStepArrayByDate(String date); + + /** + * 根据时间和天数获取步数列表 + * 例如: + * startDate = 2018-01-15 + * days = 3 + * 获取 2018-01-15、2018-01-16、2018-01-17三天的步数 + * + * @param startDate 格式yyyy-MM-dd + * @param days + * @return + */ + String getTodaySportStepArrayByStartDateAndDays(String date, int days); +} diff --git a/todaystepcounterlib/src/main/assets/microlog.properties b/todaystepcounterlib/src/main/assets/microlog.properties new file mode 100644 index 0000000..d6f16e3 --- /dev/null +++ b/todaystepcounterlib/src/main/assets/microlog.properties @@ -0,0 +1,4 @@ +# This is a simple Microlog configuration file +microlog.level=DEBUG +microlog.appender=LogCatAppender;FileAppender +microlog.formatter=SimpleFormatter diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/BaseClickBroadcast.java b/todaystepcounterlib/src/main/java/com/today/step/lib/BaseClickBroadcast.java new file mode 100644 index 0000000..01dc55b --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/BaseClickBroadcast.java @@ -0,0 +1,12 @@ +package com.today.step.lib; + +import android.content.BroadcastReceiver; + +/** + * 通知栏点击通知 + * Created by jiahongfei on 2017/10/19. + */ + +public abstract class BaseClickBroadcast extends BroadcastReceiver { + +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/DateUtils.java b/todaystepcounterlib/src/main/java/com/today/step/lib/DateUtils.java new file mode 100644 index 0000000..2644d5a --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/DateUtils.java @@ -0,0 +1,72 @@ +package com.today.step.lib; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * @Description: 时间工具类(时间格式转换方便类) + */ +class DateUtils { + + private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat(); + + /** + * 返回一定格式的当前时间 + * + * @param pattern "yyyy-MM-dd HH:mm:ss E" + * @return + */ + public static String getCurrentDate(String pattern) { + SIMPLE_DATE_FORMAT.applyPattern(pattern); + Date date = new Date(System.currentTimeMillis()); + String dateString = SIMPLE_DATE_FORMAT.format(date); + return dateString; + + } + + public static long getDateMillis(String dateString, String pattern) { + long millionSeconds = 0; + SIMPLE_DATE_FORMAT.applyPattern(pattern); + try { + millionSeconds = SIMPLE_DATE_FORMAT.parse(dateString).getTime(); + } catch (ParseException e) { + e.printStackTrace(); + }// 毫秒 + + return millionSeconds; + } + + /** + * 格式化输入的millis + * + * @param millis + * @param pattern yyyy-MM-dd HH:mm:ss E + * @return + */ + public static String dateFormat(long millis, String pattern) { + SIMPLE_DATE_FORMAT.applyPattern(pattern); + Date date = new Date(millis); + String dateString = SIMPLE_DATE_FORMAT.format(date); + return dateString; + } + + /** + * 将dateString原来old格式转换成new格式 + * + * @param dateString + * @param oldPattern yyyy-MM-dd HH:mm:ss E + * @param newPattern + * @return oldPattern和dateString形式不一样直接返回dateString + */ + public static String dateFormat(String dateString, String oldPattern, + String newPattern) { + long millis = getDateMillis(dateString, oldPattern); + if (0 == millis) { + return dateString; + } + String date = dateFormat(millis, newPattern); + return date; + } + +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/ITodayStepDBHelper.java b/todaystepcounterlib/src/main/java/com/today/step/lib/ITodayStepDBHelper.java new file mode 100644 index 0000000..61b4c71 --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/ITodayStepDBHelper.java @@ -0,0 +1,29 @@ +package com.today.step.lib; + +import java.util.List; + +/** + * @author : jiahongfei + * @email : jiahongfeinew@163.com + * @date : 2018/1/22 + * @desc : + */ + +interface ITodayStepDBHelper { + + void createTable(); + + void deleteTable(); + + void clearCapacity(String curDate, int limit); + + boolean isExist(TodayStepData todayStepData); + + void insert(TodayStepData todayStepData); + + List getQueryAll(); + + List getStepListByDate(String dateString); + + List getStepListByStartDateAndDays(String startDate, int days); +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/JobSchedulerService.java b/todaystepcounterlib/src/main/java/com/today/step/lib/JobSchedulerService.java new file mode 100644 index 0000000..d76f95f --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/JobSchedulerService.java @@ -0,0 +1,35 @@ +package com.today.step.lib; + +import android.app.job.JobParameters; +import android.app.job.JobService; +import android.content.Intent; +import android.os.Build; +import android.support.annotation.RequiresApi; + +/** + * + * Created by jiahongfei on 2017/10/13. + */ + +@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) +public class JobSchedulerService extends JobService { + + private static final String TAG = "JobSchedulerService"; + + @Override + public boolean onStartJob(JobParameters params) { + Intent intent = new Intent(getApplication(), TodayStepService.class); + getApplication().startService(intent); + +// Toast.makeText(getApplicationContext(), "onStartJob", Toast.LENGTH_SHORT).show(); + + Logger.e(TAG,"onStartJob"); + + return false; + } + + @Override + public boolean onStopJob(JobParameters params) { + return false; + } +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/Logger.java b/todaystepcounterlib/src/main/java/com/today/step/lib/Logger.java new file mode 100644 index 0000000..249caa6 --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/Logger.java @@ -0,0 +1,66 @@ +package com.today.step.lib; + +import android.text.TextUtils; +import android.util.Log; + +/** + * 日志打印工具类,封装到一起,是为了调用时方便 + * + * @author Administrator + */ +class Logger { + private static final String TAG = "Logger"; + + public static boolean sIsDebug = BuildConfig.TODAY_STEP_DEBUG; + + public static void v(String message) { + if (sIsDebug) + Log.v(TAG, message); + } + + public static void v(String tag, String message) { + if (sIsDebug) + Log.v(tag, message); + } + + public static void d(String message) { + if (sIsDebug) + Log.d(TAG, message); + } + + public static void i(String message) { + if (sIsDebug) + Log.i(TAG, message); + } + + public static void i(String tag, String message) { + if (sIsDebug) + Log.i(tag, message); + } + + public static void w(String message) { + if (sIsDebug) + Log.w(TAG, message); + } + + public static void w(String tag, String message) { + if (sIsDebug) + Log.w(tag, message); + } + + public static void e(String message) { + if (sIsDebug) + Log.e(TAG, message); + } + + public static void e(String tag, String message) { + if (sIsDebug) + Log.e(tag, message); + } + + public static void d(String tag, String message) { + if (!TextUtils.isEmpty(message) && sIsDebug) { + Log.d(TextUtils.isEmpty(tag) ? TAG : tag, message); + } + } +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/Microlog4Android.java b/todaystepcounterlib/src/main/java/com/today/step/lib/Microlog4Android.java new file mode 100644 index 0000000..8f15721 --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/Microlog4Android.java @@ -0,0 +1,35 @@ +package com.today.step.lib; + +import android.content.Context; + +import com.google.code.microlog4android.LoggerFactory; +import com.google.code.microlog4android.appender.FileAppender; +import com.google.code.microlog4android.config.PropertyConfigurator; + +/** + * @author : jiahongfei + * @email : jiahongfeinew@163.com + * @date : 2018/2/6 + * @desc : + */ + +public class Microlog4Android { + + private static final com.google.code.microlog4android.Logger logger = LoggerFactory.getLogger(); + + public void configure(Context context){ + if(null != logger) { + PropertyConfigurator.getConfigurator(context).configure(); + FileAppender appender = (FileAppender) logger.getAppender(1); + appender.setAppend(true); + logger.addAppender(appender); + } + } + + public void error(Object message){ + if(null != logger) { + logger.error(message); + } + } + +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/OnStepCounterListener.java b/todaystepcounterlib/src/main/java/com/today/step/lib/OnStepCounterListener.java new file mode 100644 index 0000000..7cefca4 --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/OnStepCounterListener.java @@ -0,0 +1,20 @@ +package com.today.step.lib; + +/** + * Created by jiahongfei on 2017/6/30. + */ + +interface OnStepCounterListener { + + /** + * 用于显示步数 + * @param step + */ + void onChangeStepCounter(int step); + + /** + * 步数清零监听,由于跨越0点需要重新计步 + */ + void onStepCounterClean(); + +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/PreferencesHelper.java b/todaystepcounterlib/src/main/java/com/today/step/lib/PreferencesHelper.java new file mode 100644 index 0000000..62f4fa4 --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/PreferencesHelper.java @@ -0,0 +1,119 @@ +package com.today.step.lib; + +import android.content.Context; +import android.content.SharedPreferences; + + +/** + * @ClassName: PreferencesHelper + * @Description: (公用类,用于缓存一些key——value类型的数据) + */ + +class PreferencesHelper { + + private static final String TAG = "PreferencesHelper"; + + public static final String APP_SHARD = "today_step_share_prefs"; + + // 上一次计步器的步数 + public static final String LAST_SENSOR_TIME = "last_sensor_time"; + // 步数补偿数值,每次传感器返回的步数-offset=当前步数 + public static final String STEP_OFFSET = "step_offset"; + // 当天,用来判断是否跨天 + public static final String STEP_TODAY = "step_today"; + // 清除步数 + public static final String CLEAN_STEP = "clean_step"; + // 当前步数 + public static final String CURR_STEP = "curr_step"; + //手机关机监听 + public static final String SHUTDOWN = "shutdown"; + //系统运行时间 + public static final String ELAPSED_REALTIMEl = "elapsed_realtime"; + + /** + * Get SharedPreferences + */ + private static SharedPreferences getSharedPreferences(Context context) { + return context.getSharedPreferences(APP_SHARD, Context.MODE_PRIVATE); + } + + public static void setLastSensorStep(Context context, float lastSensorStep){ + Logger.e(TAG, "setLastSensorStep"); + getSharedPreferences(context).edit().putFloat(LAST_SENSOR_TIME,lastSensorStep).commit(); + } + + public static float getLastSensorStep(Context context){ + Logger.e(TAG, "getLastSensorStep"); + return getSharedPreferences(context).getFloat(LAST_SENSOR_TIME,0.0f); + } + + public static void setStepOffset(Context context, float stepOffset){ + Logger.e(TAG, "setStepOffset"); + getSharedPreferences(context).edit().putFloat(STEP_OFFSET,stepOffset).commit(); + } + + public static float getStepOffset(Context context){ + Logger.e(TAG, "getStepOffset"); + return getSharedPreferences(context).getFloat(STEP_OFFSET,0.0f); + } + + public static void setStepToday(Context context, String stepToday){ + Logger.e(TAG, "setStepToday"); + getSharedPreferences(context).edit().putString(STEP_TODAY,stepToday).commit(); + } + + public static String getStepToday(Context context){ + Logger.e(TAG, "getStepToday"); + return getSharedPreferences(context).getString(STEP_TODAY,""); + } + + /** + * true清除步数从0开始,false否 + * @param context + * @param cleanStep + */ + public static void setCleanStep(Context context, boolean cleanStep){ + Logger.e(TAG, "setCleanStep"); + getSharedPreferences(context).edit().putBoolean(CLEAN_STEP,cleanStep).commit(); + } + + /** + * true 清除步数,false否 + * @param context + * @return + */ + public static boolean getCleanStep(Context context){ + Logger.e(TAG, "getCleanStep"); + return getSharedPreferences(context).getBoolean(CLEAN_STEP,true); + } + + public static void setCurrentStep(Context context, float currStep){ + Logger.e(TAG, "setCurrentStep"); + getSharedPreferences(context).edit().putFloat(CURR_STEP,currStep).commit(); + } + + public static float getCurrentStep(Context context){ + Logger.e(TAG, "getCurrentStep"); + return getSharedPreferences(context).getFloat(CURR_STEP,0.0f); + } + + public static void setShutdown(Context context, boolean shutdown){ + Logger.e(TAG, "setShutdown"); + getSharedPreferences(context).edit().putBoolean(SHUTDOWN,shutdown).commit(); + } + + public static boolean getShutdown(Context context){ + Logger.e(TAG, "getShutdown"); + return getSharedPreferences(context).getBoolean(SHUTDOWN, false); + } + + public static void setElapsedRealtime(Context context, long elapsedRealtime){ + Logger.e(TAG, "setElapsedRealtime"); + getSharedPreferences(context).edit().putLong(ELAPSED_REALTIMEl,elapsedRealtime).commit(); + } + + public static long getElapsedRealtime(Context context){ + Logger.e(TAG, "getElapsedRealtime"); + return getSharedPreferences(context).getLong(ELAPSED_REALTIMEl, 0L); + } +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/SportStepJsonUtils.java b/todaystepcounterlib/src/main/java/com/today/step/lib/SportStepJsonUtils.java new file mode 100644 index 0000000..14ebb14 --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/SportStepJsonUtils.java @@ -0,0 +1,57 @@ +package com.today.step.lib; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.List; + +/** + * @author : jiahongfei + * @email : jiahongfeinew@163.com + * @date : 2018/1/31 + * @desc : 用于解析和生成运动步数Json字符串 + */ + +public class SportStepJsonUtils { + + public static final String SPORT_DATE = "sportDate"; + public static final String STEP_NUM = "stepNum"; + public static final String DISTANCE = "km"; + public static final String CALORIE = "kaluli"; + public static final String TODAY = TodayStepDBHelper.TODAY; + + static JSONArray getSportStepJsonArray(List todayStepDataArrayList) { + JSONArray jsonArray = new JSONArray(); + if (null == todayStepDataArrayList || 0 == todayStepDataArrayList.size()) { + return jsonArray; + } + for (int i = 0; i < todayStepDataArrayList.size(); i++) { + TodayStepData todayStepData = todayStepDataArrayList.get(i); + try { + JSONObject subObject = new JSONObject(); + subObject.put(TODAY, todayStepData.getToday()); + subObject.put(SPORT_DATE, todayStepData.getDate()); + subObject.put(STEP_NUM, todayStepData.getStep()); + subObject.put(DISTANCE, getDistanceByStep(todayStepData.getStep())); + subObject.put(CALORIE, getCalorieByStep(todayStepData.getStep())); + jsonArray.put(subObject); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return jsonArray; + } + + // 公里计算公式 + static String getDistanceByStep(long steps) { + return String.format("%.2f", steps * 0.6f / 1000); + } + + // 千卡路里计算公式 + static String getCalorieByStep(long steps) { + return String.format("%.1f", steps * 0.6f * 60 * 1.036f / 1000); + } + + +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/StepAlertManagerUtils.java b/todaystepcounterlib/src/main/java/com/today/step/lib/StepAlertManagerUtils.java new file mode 100644 index 0000000..388f3b1 --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/StepAlertManagerUtils.java @@ -0,0 +1,50 @@ +package com.today.step.lib; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Build; + +import java.util.Calendar; + +import static android.content.Context.ALARM_SERVICE; + +/** + * Created by jiahongfei on 2017/6/18. + */ +class StepAlertManagerUtils { + + private static final String TAG = "StepAlertManagerUtils"; + + /** + * 设置0点分隔Alert,当前天+1天的0点启动 + * + * @param application + */ + public static void set0SeparateAlertManager(Context application) { + + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(System.currentTimeMillis()); + calendar.add(Calendar.DAY_OF_YEAR,1); + String tomorrow = DateUtils.dateFormat(calendar.getTimeInMillis(),"yyyy-MM-dd"); + long timeInMillis = DateUtils.getDateMillis(tomorrow+ " 00:00:00","yyyy-MM-dd HH:mm:ss"); + + Logger.e(TAG, DateUtils.dateFormat(timeInMillis,"yyyy-MM-dd HH:mm:ss")); + + AlarmManager alarmManager = (AlarmManager) application.getSystemService(ALARM_SERVICE); + Intent i1 = new Intent(application, TodayStepAlertReceive.class); + i1.putExtra(TodayStepService.INTENT_NAME_0_SEPARATE, true); + i1.setAction(TodayStepAlertReceive.ACTION_STEP_ALERT); + PendingIntent operation = PendingIntent.getBroadcast(application, 0, i1, PendingIntent.FLAG_UPDATE_CURRENT); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeInMillis, operation); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, operation); + } else { + alarmManager.set(AlarmManager.RTC_WAKEUP, timeInMillis, operation); + } + + } +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepAlertReceive.java b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepAlertReceive.java new file mode 100644 index 0000000..0e2e2fd --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepAlertReceive.java @@ -0,0 +1,30 @@ +package com.today.step.lib; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +/** + * 0点启动app处理步数 + * Created by jiahongfei on 2017/6/18. + */ + +public class TodayStepAlertReceive extends BroadcastReceiver { + + public static final String ACTION_STEP_ALERT = "action_step_alert"; + + @Override + public void onReceive(Context context, Intent intent) { + if (ACTION_STEP_ALERT.equals(intent.getAction())) { + boolean separate = intent.getBooleanExtra(TodayStepService.INTENT_NAME_0_SEPARATE, false); + Intent stepInent = new Intent(context, TodayStepService.class); + stepInent.putExtra(TodayStepService.INTENT_NAME_0_SEPARATE, separate); + context.startService(stepInent); + + StepAlertManagerUtils.set0SeparateAlertManager(context.getApplicationContext()); + + Logger.e("TodayStepAlertReceive","TodayStepAlertReceive"); + } + + } +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepBootCompleteReceiver.java b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepBootCompleteReceiver.java new file mode 100644 index 0000000..14b9133 --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepBootCompleteReceiver.java @@ -0,0 +1,25 @@ +package com.today.step.lib; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +/** + * 开机完成广播 + * + */ +public class TodayStepBootCompleteReceiver extends BroadcastReceiver { + + private static final String TAG = "TodayStepBootCompleteReceiver"; + + @Override + public void onReceive(Context context, Intent intent) { + + Intent todayStepIntent = new Intent(context, TodayStepService.class); + todayStepIntent.putExtra(TodayStepService.INTENT_NAME_BOOT,true); + context.startService(todayStepIntent); + + Logger.e(TAG,"TodayStepBootCompleteReceiver"); + + } +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepCounter.java b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepCounter.java new file mode 100644 index 0000000..14856c5 --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepCounter.java @@ -0,0 +1,226 @@ +package com.today.step.lib; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.os.PowerManager; +import android.os.SystemClock; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +/** + * android4.4 Sensor.TYPE_STEP_COUNTER + * 计步传感器计算当天步数,不需要后台Service + * Created by jiahongfei on 2017/6/30. + */ + +class TodayStepCounter implements SensorEventListener { + + private static final String TAG = "TodayStepCounter"; + + private int sOffsetStep = 0; + private int sCurrStep = 0; + private String mTodayDate; + private boolean mCleanStep = true; + private boolean mShutdown = false; + /** + * 用来标识对象第一次创建, + */ + private boolean mCounterStepReset = true; + + private Context mContext; + private OnStepCounterListener mOnStepCounterListener; + + private boolean mSeparate = false; + private boolean mBoot = false; + + public TodayStepCounter(Context context, OnStepCounterListener onStepCounterListener, boolean separate, boolean boot) { + this.mContext = context; + this.mSeparate = separate; + this.mBoot = boot; + this.mOnStepCounterListener = onStepCounterListener; + + WakeLockUtils.getLock(mContext); + + sCurrStep = (int) PreferencesHelper.getCurrentStep(mContext); + mCleanStep = PreferencesHelper.getCleanStep(mContext); + mTodayDate = PreferencesHelper.getStepToday(mContext); + sOffsetStep = (int) PreferencesHelper.getStepOffset(mContext); + mShutdown = PreferencesHelper.getShutdown(mContext); + Logger.e(TAG, "mShutdown : " + mShutdown); + //开机启动监听到,一定是关机开机了 + if (mBoot || shutdownBySystemRunningTime()) { + mShutdown = true; + PreferencesHelper.setShutdown(mContext, mShutdown); + Logger.e(TAG, "开机启动监听到"); + } + + dateChangeCleanStep(); + + initBroadcastReceiver(); + + updateStepCounter(); + + } + + private void initBroadcastReceiver() { + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_TIME_TICK); + filter.addAction(Intent.ACTION_DATE_CHANGED); + BroadcastReceiver mBatInfoReceiver = new BroadcastReceiver() { + @Override + public void onReceive(final Context context, final Intent intent) { + if (Intent.ACTION_TIME_TICK.equals(intent.getAction()) + || Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) { + + Logger.e(TAG, "ACTION_TIME_TICK"); + //service存活做0点分隔 + dateChangeCleanStep(); + + } + } + }; + mContext.registerReceiver(mBatInfoReceiver, filter); + + } + + @Override + public void onSensorChanged(SensorEvent event) { + + if (event.sensor.getType() == Sensor.TYPE_STEP_COUNTER) { + + int counterStep = (int) event.values[0]; + + if (mCleanStep) { + //TODO:只有传感器回调才会记录当前传感器步数,然后对当天步数进行清零,所以步数会少,少的步数等于传感器启动需要的步数,假如传感器需要10步进行启动,那么就少10步 + cleanStep(counterStep); + } else { + //处理关机启动 + if (mShutdown || shutdownByCounterStep(counterStep)) { + Logger.e(TAG, "onSensorChanged shutdown"); + shutdown(counterStep); + } + } + sCurrStep = counterStep - sOffsetStep; + + if (sCurrStep < 0) { + //容错处理,无论任何原因步数不能小于0,如果小于0,直接清零 + Logger.e(TAG, "容错处理,无论任何原因步数不能小于0,如果小于0,直接清零"); + cleanStep(counterStep); + } + + PreferencesHelper.setCurrentStep(mContext, sCurrStep); + PreferencesHelper.setElapsedRealtime(mContext, SystemClock.elapsedRealtime()); + PreferencesHelper.setLastSensorStep(mContext, counterStep); + + Logger.e(TAG, "counterStep : " + counterStep + " --- " + "sOffsetStep : " + sOffsetStep + " --- " + "sCurrStep : " + sCurrStep); + + updateStepCounter(); + } + } + + private void cleanStep(int counterStep) { + //清除步数,步数归零,优先级最高 + sCurrStep = 0; + sOffsetStep = counterStep; + PreferencesHelper.setStepOffset(mContext, sOffsetStep); + + mCleanStep = false; + PreferencesHelper.setCleanStep(mContext, mCleanStep); + + Logger.e(TAG, "mCleanStep : " + "清除步数,步数归零"); + } + + private void shutdown(int counterStep) { + int tmpCurrStep = (int) PreferencesHelper.getCurrentStep(mContext); + //重新设置offset + sOffsetStep = counterStep - tmpCurrStep; + PreferencesHelper.setStepOffset(mContext, sOffsetStep); + + mShutdown = false; + PreferencesHelper.setShutdown(mContext, mShutdown); + } + + private boolean shutdownByCounterStep(int counterStep) { + if (mCounterStepReset) { + //只判断一次 + if (counterStep < PreferencesHelper.getLastSensorStep(mContext)) { + //当前传感器步数小于上次传感器步数肯定是重新启动了,只是用来增加精度不是绝对的 + Logger.e(TAG, "当前传感器步数小于上次传感器步数肯定是重新启动了,只是用来增加精度不是绝对的"); + return true; + } + mCounterStepReset = false; + } + return false; + } + + private boolean shutdownBySystemRunningTime() { + if (PreferencesHelper.getElapsedRealtime(mContext) > SystemClock.elapsedRealtime()) { + //上次运行的时间大于当前运行时间判断为重启,只是增加精度,极端情况下连续重启,会判断不出来 + Logger.e(TAG, "上次运行的时间大于当前运行时间判断为重启,只是增加精度,极端情况下连续重启,会判断不出来"); + return true; + } + return false; + } + + private synchronized void dateChangeCleanStep() { + //时间改变了清零,或者0点分隔回调 + if (!getTodayDate().equals(mTodayDate) || mSeparate) { + + WakeLockUtils.getLock(mContext); + + mCleanStep = true; + PreferencesHelper.setCleanStep(mContext, mCleanStep); + + mTodayDate = getTodayDate(); + PreferencesHelper.setStepToday(mContext, mTodayDate); + + mShutdown = false; + PreferencesHelper.setShutdown(mContext, mShutdown); + + mBoot = false; + + mSeparate = false; + + sCurrStep = 0; + PreferencesHelper.setCurrentStep(mContext, sCurrStep); + + if (null != mOnStepCounterListener) { + mOnStepCounterListener.onStepCounterClean(); + } + } + } + + private String getTodayDate() { + Date date = new Date(System.currentTimeMillis()); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + return sdf.format(date); + } + + private void updateStepCounter() { + + //每次回调都判断一下是否跨天 + dateChangeCleanStep(); + + if (null != mOnStepCounterListener) { + mOnStepCounterListener.onChangeStepCounter(sCurrStep); + } + } + + public int getCurrentStep() { + sCurrStep = (int) PreferencesHelper.getCurrentStep(mContext); + return sCurrStep; + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + + } + +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepDBHelper.java b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepDBHelper.java new file mode 100644 index 0000000..a7f582c --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepDBHelper.java @@ -0,0 +1,201 @@ +package com.today.step.lib; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 用来记录当天步数列表,传感器回调30次记录一条数据 + * Created by jiahongfei on 2017/10/9. + */ + +class TodayStepDBHelper extends SQLiteOpenHelper implements ITodayStepDBHelper{ + + private static final String TAG = "TodayStepDBHelper"; + + private static final String DATE_PATTERN_YYYY_MM_DD = "yyyy-MM-dd"; + + private static final int VERSION = 1; + private static final String DATABASE_NAME = "TodayStepDB.db"; + private static final String TABLE_NAME = "TodayStepData"; + private static final String PRIMARY_KEY = "_id"; + public static final String TODAY = "today"; + public static final String DATE = "date"; + public static final String STEP = "step"; + + private static final String SQL_CREATE_TABLE = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" + + PRIMARY_KEY + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + TODAY + " TEXT, " + + DATE + " long, " + + STEP + " long);"; + private static final String SQL_DELETE_TABLE = "DROP TABLE IF EXISTS " + TABLE_NAME; + private static final String SQL_QUERY_ALL = "SELECT * FROM " + TABLE_NAME; + private static final String SQL_QUERY_STEP = "SELECT * FROM " + TABLE_NAME + " WHERE " + TODAY + " = ? AND " + STEP + " = ?"; + private static final String SQL_QUERY_STEP_BY_DATE = "SELECT * FROM " + TABLE_NAME + " WHERE " + TODAY + " = ?"; + private static final String SQL_DELETE_TODAY = "DELETE FROM " + TABLE_NAME + " WHERE " + TODAY + " = ?"; + + //只保留mLimit天的数据 + private int mLimit = -1; + + public static ITodayStepDBHelper factory(Context context){ + return new TodayStepDBHelper(context); + } + + private TodayStepDBHelper(Context context) { + super(context, DATABASE_NAME, null, VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + + Logger.e(TAG, SQL_CREATE_TABLE); + db.execSQL(SQL_CREATE_TABLE); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + deleteTable(); + onCreate(db); + } + + @Override + public synchronized boolean isExist(TodayStepData todayStepData) { + Cursor cursor = getReadableDatabase().rawQuery(SQL_QUERY_STEP, new String[]{todayStepData.getToday(), todayStepData.getStep() + ""}); + boolean exist = cursor.getCount() > 0 ? true : false; + cursor.close(); + return exist; + } + + @Override + public synchronized void createTable() { + getWritableDatabase().execSQL(SQL_CREATE_TABLE); + } + + @Override + public synchronized void insert(TodayStepData todayStepData) { + + ContentValues contentValues = new ContentValues(); + contentValues.put(TODAY, todayStepData.getToday()); + contentValues.put(DATE, todayStepData.getDate()); + contentValues.put(STEP, todayStepData.getStep()); + getWritableDatabase().insert(TABLE_NAME, null, contentValues); + } + + @Override + public synchronized List getQueryAll() { + Cursor cursor = getReadableDatabase().rawQuery(SQL_QUERY_ALL, new String[]{}); + List todayStepDatas = getTodayStepDataList(cursor); + cursor.close(); + return todayStepDatas; + } + + /** + * 根据时间获取步数列表 + * + * @param dateString 格式yyyy-MM-dd + * @return + */ + @Override + public synchronized List getStepListByDate(String dateString) { + Cursor cursor = getReadableDatabase().rawQuery(SQL_QUERY_STEP_BY_DATE, new String[]{dateString}); + List todayStepDatas = getTodayStepDataList(cursor); + cursor.close(); + return todayStepDatas; + } + + /** + * 根据时间和天数获取步数列表 + * 例如: + * startDate = 2018-01-15 + * days = 3 + * 获取 2018-01-15、2018-01-16、2018-01-17三天的步数 + * + * @param startDate 格式yyyy-MM-dd + * @param days + * @return + */ + @Override + public synchronized List getStepListByStartDateAndDays(String startDate, int days) { + List todayStepDatas = new ArrayList<>(); + for (int i = 0; i < days; i++) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(DateUtils.getDateMillis(startDate, DATE_PATTERN_YYYY_MM_DD)); + calendar.add(Calendar.DAY_OF_YEAR, i); + Cursor cursor = getReadableDatabase().rawQuery(SQL_QUERY_STEP_BY_DATE, + new String[]{DateUtils.dateFormat(calendar.getTimeInMillis(), DATE_PATTERN_YYYY_MM_DD)}); + todayStepDatas.addAll(getTodayStepDataList(cursor)); + cursor.close(); + } + return todayStepDatas; + } + + private List getTodayStepDataList(Cursor cursor) { + + List todayStepDatas = new ArrayList<>(); + while (cursor.moveToNext()) { + String today = cursor.getString(cursor.getColumnIndex(TODAY)); + long date = cursor.getLong(cursor.getColumnIndex(DATE)); + long step = cursor.getLong(cursor.getColumnIndex(STEP)); + TodayStepData todayStepData = new TodayStepData(); + todayStepData.setToday(today); + todayStepData.setDate(date); + todayStepData.setStep(step); + todayStepDatas.add(todayStepData); + } + return todayStepDatas; + } + + /** + * 根据limit来清除数据库 + * 例如: + * curDate = 2018-01-10 limit=0;表示只保留2018-01-10 + * curDate = 2018-01-10 limit=1;表示保留2018-01-10、2018-01-09等 + * @param curDate + * @param limit -1失效 + */ + @Override + public synchronized void clearCapacity(String curDate, int limit) { + mLimit = limit; + if (mLimit <= 0) { + return; + } + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(DateUtils.getDateMillis(curDate, DATE_PATTERN_YYYY_MM_DD)); + calendar.add(Calendar.DAY_OF_YEAR, -(mLimit)); + String date = DateUtils.dateFormat(calendar.getTimeInMillis(), DATE_PATTERN_YYYY_MM_DD); + Log.e(TAG, date); + + List todayStepDataList = getQueryAll(); + Set delDateSet = new HashSet<>(); + for (TodayStepData tmpTodayStepData : todayStepDataList) { + long dbTodayDate = DateUtils.getDateMillis(tmpTodayStepData.getToday(), DATE_PATTERN_YYYY_MM_DD); + if (calendar.getTimeInMillis() >= dbTodayDate) { + delDateSet.add(tmpTodayStepData.getToday()); + } + } + + for (String delDate : delDateSet) { + getWritableDatabase().execSQL(SQL_DELETE_TODAY, new String[]{delDate}); + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public synchronized void deleteTable() { + getWritableDatabase().execSQL(SQL_DELETE_TABLE); + } + +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepData.java b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepData.java new file mode 100644 index 0000000..6f7b986 --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepData.java @@ -0,0 +1,84 @@ +package com.today.step.lib; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.Serializable; + + +public class TodayStepData implements Serializable, Parcelable { + + //当天时间,只显示到天 yyyy-MM-dd + private String today; + //步数时间,显示到毫秒 + private long date; + //对应date时间的步数 + private long step; + + public TodayStepData() { + + } + + protected TodayStepData(Parcel in) { + today = in.readString(); + date = in.readLong(); + step = in.readLong(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(today); + dest.writeLong(date); + dest.writeLong(step); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public TodayStepData createFromParcel(Parcel in) { + return new TodayStepData(in); + } + + @Override + public TodayStepData[] newArray(int size) { + return new TodayStepData[size]; + } + }; + + public long getDate() { + return date; + } + + public void setDate(long date) { + this.date = date; + } + + public long getStep() { + return step; + } + + public void setStep(long step) { + this.step = step; + } + + public String getToday() { + return today; + } + + public void setToday(String today) { + this.today = today; + } + + @Override + public String toString() { + return "TodayStepData{" + + ", today=" + today + + ", date=" + date + + ", step=" + step + + '}'; + } +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepDetector.java b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepDetector.java new file mode 100644 index 0000000..0f123ed --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepDetector.java @@ -0,0 +1,309 @@ +package com.today.step.lib; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.os.PowerManager; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +/** + * Sensor.TYPE_ACCELEROMETER + * 加速度传感器计算当天步数,需要保持后台Service + */ +class TodayStepDetector implements SensorEventListener{ + + private final String TAG = "TodayStepDetector"; + + //存放三轴数据 + float[] oriValues = new float[3]; + final int ValueNum = 4; + //用于存放计算阈值的波峰波谷差值 + float[] tempValue = new float[ValueNum]; + int tempCount = 0; + //是否上升的标志位 + boolean isDirectionUp = false; + //持续上升次数 + int continueUpCount = 0; + //上一点的持续上升的次数,为了记录波峰的上升次数 + int continueUpFormerCount = 0; + //上一点的状态,上升还是下降 + boolean lastStatus = false; + //波峰值 + float peakOfWave = 0; + //波谷值 + float valleyOfWave = 0; + //此次波峰的时间 + long timeOfThisPeak = 0; + //上次波峰的时间 + long timeOfLastPeak = 0; + //当前的时间 + long timeOfNow = 0; + //当前传感器的值 + float gravityNew = 0; + //上次传感器的值 + float gravityOld = 0; + //动态阈值需要动态的数据,这个值用于这些动态数据的阈值 + final float InitialValue = (float) 1.3; + //初始阈值 + float ThreadValue = (float) 2.0; + //波峰波谷时间差 + int TimeInterval = 400; + + private int count = 0; + private int mCount = 0; + private OnStepCounterListener mOnStepCounterListener; + private Context mContext; + private long timeOfLastPeak1 = 0; + private long timeOfThisPeak1 = 0; + private String mTodayDate; + + public TodayStepDetector(Context context, OnStepCounterListener onStepCounterListener){ + super(); + mContext = context; + this.mOnStepCounterListener = onStepCounterListener; + + WakeLockUtils.getLock(mContext); + + mCount = (int) PreferencesHelper.getCurrentStep(mContext); + mTodayDate = PreferencesHelper.getStepToday(mContext); + dateChangeCleanStep(); + initBroadcastReceiver(); + + updateStepCounter(); + + } + + private void initBroadcastReceiver() { + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_TIME_TICK); + filter.addAction(Intent.ACTION_DATE_CHANGED); + BroadcastReceiver mBatInfoReceiver = new BroadcastReceiver() { + @Override + public void onReceive(final Context context, final Intent intent) { + if (Intent.ACTION_TIME_TICK.equals(intent.getAction()) + || Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) { + Logger.e(TAG, "ACTION_TIME_TICK"); + //service存活做0点分隔 + dateChangeCleanStep(); + + } + } + }; + mContext.registerReceiver(mBatInfoReceiver, filter); + } + + private synchronized void dateChangeCleanStep() { + //时间改变了清零,或者0点分隔回调 + if (!getTodayDate().equals(mTodayDate)) { + + WakeLockUtils.getLock(mContext); + + mCount = 0; + PreferencesHelper.setCurrentStep(mContext, mCount); + + mTodayDate = getTodayDate(); + PreferencesHelper.setStepToday(mContext, mTodayDate); + + setSteps(0); + + if(null != mOnStepCounterListener){ + mOnStepCounterListener.onStepCounterClean(); + } + } + } + + private String getTodayDate() { + Date date = new Date(System.currentTimeMillis()); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + return sdf.format(date); + } + + private void updateStepCounter(){ + + //每次回调都判断一下是否跨天 + dateChangeCleanStep(); + + if (null != mOnStepCounterListener) { + mOnStepCounterListener.onChangeStepCounter(mCount); + } + } + + @Override + public void onSensorChanged(SensorEvent event) { + for (int i = 0; i < 3; i++) { + oriValues[i] = event.values[i]; + } + gravityNew = (float) Math.sqrt(oriValues[0] * oriValues[0] + + oriValues[1] * oriValues[1] + oriValues[2] * oriValues[2]); + detectorNewStep(gravityNew); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // + } + + /* + * 检测步子,并开始计步 + * 1.传入sersor中的数据 + * 2.如果检测到了波峰,并且符合时间差以及阈值的条件,则判定为1步 + * 3.符合时间差条件,波峰波谷差值大于initialValue,则将该差值纳入阈值的计算中 + * */ + private void detectorNewStep(float values) { + if (gravityOld == 0) { + gravityOld = values; + } else { + if (detectorPeak(values, gravityOld)) { + timeOfLastPeak = timeOfThisPeak; + timeOfNow = System.currentTimeMillis(); + if (timeOfNow - timeOfLastPeak >= TimeInterval + && (peakOfWave - valleyOfWave >= ThreadValue)) { + timeOfThisPeak = timeOfNow; + /* + * 更新界面的处理,不涉及到算法 + * 一般在通知更新界面之前,增加下面处理,为了处理无效运动: + * 1.连续记录10才开始计步 + * 2.例如记录的9步用户停住超过3秒,则前面的记录失效,下次从头开始 + * 3.连续记录了9步用户还在运动,之前的数据才有效 + * */ + countStep(); + } + if (timeOfNow - timeOfLastPeak >= TimeInterval + && (peakOfWave - valleyOfWave >= InitialValue)) { + timeOfThisPeak = timeOfNow; + ThreadValue = peakValleyThread(peakOfWave - valleyOfWave); + } + } + } + gravityOld = values; + } + + /* + * 检测波峰 + * 以下四个条件判断为波峰: + * 1.目前点为下降的趋势:isDirectionUp为false + * 2.之前的点为上升的趋势:lastStatus为true + * 3.到波峰为止,持续上升大于等于2次 + * 4.波峰值大于20 + * 记录波谷值 + * 1.观察波形图,可以发现在出现步子的地方,波谷的下一个就是波峰,有比较明显的特征以及差值 + * 2.所以要记录每次的波谷值,为了和下次的波峰做对比 + * */ + private boolean detectorPeak(float newValue, float oldValue) { + lastStatus = isDirectionUp; + if (newValue >= oldValue) { + isDirectionUp = true; + continueUpCount++; + } else { + continueUpFormerCount = continueUpCount; + continueUpCount = 0; + isDirectionUp = false; + } + + if (!isDirectionUp && lastStatus + && (continueUpFormerCount >= 2 || oldValue >= 20)) { + peakOfWave = oldValue; + return true; + } else if (!lastStatus && isDirectionUp) { + valleyOfWave = oldValue; + return false; + } else { + return false; + } + } + + /* + * 阈值的计算 + * 1.通过波峰波谷的差值计算阈值 + * 2.记录4个值,存入tempValue[]数组中 + * 3.在将数组传入函数averageValue中计算阈值 + * */ + private float peakValleyThread(float value) { + float tempThread = ThreadValue; + if (tempCount < ValueNum) { + tempValue[tempCount] = value; + tempCount++; + } else { + tempThread = averageValue(tempValue, ValueNum); + for (int i = 1; i < ValueNum; i++) { + tempValue[i - 1] = tempValue[i]; + } + tempValue[ValueNum - 1] = value; + } + return tempThread; + + } + + /* + * 梯度化阈值 + * 1.计算数组的均值 + * 2.通过均值将阈值梯度化在一个范围里 + * */ + private float averageValue(float value[], int n) { + float ave = 0; + for (int i = 0; i < n; i++) { + ave += value[i]; + } + ave = ave / ValueNum; + if (ave >= 8) + ave = (float) 4.3; + else if (ave >= 7 && ave < 8) + ave = (float) 3.3; + else if (ave >= 4 && ave < 7) + ave = (float) 2.3; + else if (ave >= 3 && ave < 4) + ave = (float) 2.0; + else { + ave = (float) 1.7; + } + return ave; + } + + + + + /* + * 连续走十步才会开始计步 + * 连续走了9步以下,停留超过3秒,则计数清空 + * */ + private void countStep() { + this.timeOfLastPeak1 = this.timeOfThisPeak1; + this.timeOfThisPeak1 = System.currentTimeMillis(); + if (this.timeOfThisPeak1 - this.timeOfLastPeak1 <= 3000L){ + if(this.count<9){ + this.count++; + }else if(this.count == 9){ + this.count++; + this.mCount += this.count; + PreferencesHelper.setCurrentStep(mContext, mCount); + updateStepCounter(); + }else{ + this.mCount++; + PreferencesHelper.setCurrentStep(mContext, mCount); + updateStepCounter(); + } + }else{//超时 + this.count = 1;//为1,不是0 + } + + } + + + private void setSteps(int initValue) { + this.mCount = initValue; + this.count = 0; + timeOfLastPeak1 = 0; + timeOfThisPeak1 = 0; + } + + public int getCurrentStep() { + return mCount; + } +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepManager.java b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepManager.java new file mode 100644 index 0000000..937d146 --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepManager.java @@ -0,0 +1,63 @@ +package com.today.step.lib; + +import android.app.Application; +import android.app.job.JobInfo; +import android.app.job.JobScheduler; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.support.annotation.RequiresApi; + +/** + * 计步SDK初始化方法 + * Created by jiahongfei on 2017/10/9. + */ + +public class TodayStepManager { + + private static final String TAG = "TodayStepManager"; + private static final int JOB_ID = 100; + + /** + * 在程序的最开始调用,最好在自定义的application oncreate中调用 + * + * @param application + */ + public static void init(Application application) { + + StepAlertManagerUtils.set0SeparateAlertManager(application); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { +// initJobScheduler(application); + } + + + startTodayStepService(application); + } + + public static void startTodayStepService(Application application) { + Intent intent = new Intent(application, TodayStepService.class); + application.startService(intent); + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + private static void initJobScheduler(Application application) { + Logger.e(TAG, "initJobScheduler"); + + JobScheduler jobScheduler = (JobScheduler) + application.getSystemService(Context.JOB_SCHEDULER_SERVICE); + JobInfo.Builder builder = new JobInfo.Builder( + JOB_ID, + new ComponentName(application.getPackageName(), JobSchedulerService.class.getName())); + builder.setMinimumLatency(5000)// 设置任务运行最少延迟时间 + .setOverrideDeadline(60000)// 设置deadline,若到期还没有达到规定的条件则会开始执行 + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)// 设置网络条件 + .setRequiresCharging(true)// 设置是否充电的条件 + .setRequiresDeviceIdle(false);// 设置手机是否空闲的条件 + int resultCode = jobScheduler.schedule(builder.build()); + if (JobScheduler.RESULT_FAILURE == resultCode) { + Logger.e(TAG, "jobScheduler 失败"); + } + } +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepService.java b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepService.java new file mode 100644 index 0000000..8ff48f0 --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepService.java @@ -0,0 +1,467 @@ +package com.today.step.lib; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.graphics.BitmapFactory; +import android.hardware.Sensor; +import android.hardware.SensorManager; +import android.os.Build; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.support.v7.app.NotificationCompat; +import android.text.TextUtils; +import android.util.Log; + +import org.json.JSONArray; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +import static com.today.step.lib.SportStepJsonUtils.getCalorieByStep; +import static com.today.step.lib.SportStepJsonUtils.getDistanceByStep; + +public class TodayStepService extends Service implements Handler.Callback { + + private static final String TAG = "TodayStepService"; + + /** + * 数据库中保存多少天的运动数据 + */ + private static final int DB_LIMIT = 2; + + //保存数据库频率 + private static final int DB_SAVE_COUNTER = 50; + + //传感器的采样周期,这里使用SensorManager.SENSOR_DELAY_FASTEST,如果使用SENSOR_DELAY_UI会导致部分手机后台清理内存之后传感器不记步 + private static final int SAMPLING_PERIOD_US = SensorManager.SENSOR_DELAY_FASTEST; + + private static final int HANDLER_WHAT_SAVE_STEP = 0; + //如果走路如果停止,10秒钟后保存数据库 + private static final int LAST_SAVE_STEP_DURATION = 10*1000; + + private static final int BROADCAST_REQUEST_CODE = 100; + + public static final String INTENT_NAME_0_SEPARATE = "intent_name_0_separate"; + public static final String INTENT_NAME_BOOT = "intent_name_boot"; + public static final String INTENT_JOB_SCHEDULER = "intent_job_scheduler"; + + public static int CURRENT_SETP = 0; + + private SensorManager sensorManager; + // private TodayStepDcretor stepDetector; + private TodayStepDetector mStepDetector; + private TodayStepCounter stepCounter; + + private NotificationManager nm; + Notification notification; + private NotificationCompat.Builder builder; + + private boolean mSeparate = false; + private boolean mBoot = false; + + private int mDbSaveCount = 0; + + private ITodayStepDBHelper mTodayStepDBHelper; + + private final Handler sHandler = new Handler(this); + + private Microlog4Android mMicrolog4Android = new Microlog4Android(); + + @Override + public boolean handleMessage(Message msg) { + switch (msg.what) { + case HANDLER_WHAT_SAVE_STEP: { + Logger.e(TAG, "HANDLER_WHAT_SAVE_STEP"); + + microlog4AndroidError("HANDLER_WHAT_SAVE_STEP"); + + mDbSaveCount = 0; + + saveDb(true, CURRENT_SETP); + break; + } + default: + break; + } + return false; + } + + @Override + public void onCreate() { + Logger.e(TAG, "onCreate:" + CURRENT_SETP); + super.onCreate(); + + mTodayStepDBHelper = TodayStepDBHelper.factory(getApplicationContext()); + + sensorManager = (SensorManager) this + .getSystemService(SENSOR_SERVICE); + + initNotification(CURRENT_SETP); + + if(null != mMicrolog4Android) { + mMicrolog4Android.configure(this); + } + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Logger.e(TAG, "onStartCommand:" + CURRENT_SETP); + + if (null != intent) { + mSeparate = intent.getBooleanExtra(INTENT_NAME_0_SEPARATE, false); + mBoot = intent.getBooleanExtra(INTENT_NAME_BOOT, false); + } + + mDbSaveCount = 0; + + updateNotification(CURRENT_SETP); + + //注册传感器 + startStepDetector(); + + //TODO:测试数据Start +// if(Logger.sIsDebug) { +// if (!isStepCounter()) { +// Toast.makeText(getApplicationContext(), "Lib 当前手机没有计步传感器", Toast.LENGTH_LONG).show(); +// } else { +// Toast.makeText(getApplicationContext(), "Lib 当前手机使用计步传感器", Toast.LENGTH_LONG).show(); +// +// } +// } + //TODO:测试数据End + + microlog4AndroidError("onStartCommand"); + + return START_STICKY; + } + + private void initNotification(int currentStep) { + + builder = new NotificationCompat.Builder(this); + builder.setPriority(Notification.PRIORITY_MIN); + + String receiverName = getReceiver(getApplicationContext()); + PendingIntent contentIntent = PendingIntent.getBroadcast(this, BROADCAST_REQUEST_CODE, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT); + if (!TextUtils.isEmpty(receiverName)) { + try { + contentIntent = PendingIntent.getBroadcast(this, BROADCAST_REQUEST_CODE, new Intent(this, Class.forName(receiverName)), PendingIntent.FLAG_UPDATE_CURRENT); + } catch (Exception e) { + e.printStackTrace(); + contentIntent = PendingIntent.getBroadcast(this, BROADCAST_REQUEST_CODE, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT); + } + } + builder.setContentIntent(contentIntent); + int smallIcon = getResources().getIdentifier("icon_step_small", "mipmap", getPackageName()); + if (0 != smallIcon) { + Logger.e(TAG, "smallIcon"); + builder.setSmallIcon(smallIcon); + } else { + builder.setSmallIcon(R.mipmap.ic_notification_default);// 设置通知小ICON + } + int largeIcon = getResources().getIdentifier("icon_step_large", "mipmap", getPackageName()); + if (0 != largeIcon) { + Logger.e(TAG, "largeIcon"); + builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), largeIcon)); + } else { + builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_notification_default)); + + } + builder.setTicker(getString(R.string.app_name)); + builder.setContentTitle(getString(R.string.title_notification_bar, String.valueOf(currentStep))); + String km = getDistanceByStep(currentStep); + String calorie = getCalorieByStep(currentStep); + builder.setContentText(calorie + " 千卡 " + km + " 公里"); + + //设置不可清除 + builder.setOngoing(true); + notification = builder.build(); + //将Service设置前台,这里的id和notify的id一定要相同否则会出现后台清理内存Service被杀死通知还存在的bug + startForeground(R.string.app_name, notification); + nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + nm.notify(R.string.app_name, notification); + + } + + @Override + public IBinder onBind(Intent intent) { + Logger.e(TAG, "onBind:" + CURRENT_SETP); + return mIBinder.asBinder(); + } + + private void startStepDetector() { + +// getLock(this); + + //android4.4以后如果有stepcounter可以使用计步传感器 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && isStepCounter()) { + addStepCounterListener(); + } else { + addBasePedoListener(); + } + } + + private void addStepCounterListener() { + Logger.e(TAG, "addStepCounterListener"); + if (null != stepCounter) { + Logger.e(TAG, "已经注册TYPE_STEP_COUNTER"); + WakeLockUtils.getLock(this); + CURRENT_SETP = stepCounter.getCurrentStep(); + updateNotification(CURRENT_SETP); + return; + } + Sensor countSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER); + if (null == countSensor) { + return; + } + stepCounter = new TodayStepCounter(getApplicationContext(), mOnStepCounterListener, mSeparate, mBoot); + Logger.e(TAG, "countSensor"); + sensorManager.registerListener(stepCounter, countSensor, SAMPLING_PERIOD_US); + } + + private void addBasePedoListener() { + Logger.e(TAG, "addBasePedoListener"); + if (null != mStepDetector) { + WakeLockUtils.getLock(this); + Logger.e(TAG, "已经注册TYPE_ACCELEROMETER"); + CURRENT_SETP = mStepDetector.getCurrentStep(); + updateNotification(CURRENT_SETP); + return; + } + //没有计步器的时候开启定时器保存数据 + Sensor sensor = sensorManager + .getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + if (null == sensor) { + return; + } + mStepDetector = new TodayStepDetector(this, mOnStepCounterListener); + Log.e(TAG, "TodayStepDcretor"); + // 获得传感器的类型,这里获得的类型是加速度传感器 + // 此方法用来注册,只有注册过才会生效,参数:SensorEventListener的实例,Sensor的实例,更新速率 + sensorManager.registerListener(mStepDetector, sensor, SAMPLING_PERIOD_US); + } + + @Override + public void onDestroy() { + Logger.e(TAG, "onDestroy:" + CURRENT_SETP); + + Intent intent = new Intent(this, TodayStepService.class); + startService(intent); + super.onDestroy(); + } + + @Override + public boolean onUnbind(Intent intent) { + Logger.e(TAG, "onUnbind:" + CURRENT_SETP); + return super.onUnbind(intent); + } + + /** + * 步数每次回调的方法 + * + * @param currentStep + */ + private void updateTodayStep(int currentStep) { + + microlog4AndroidError(" currentStep : " + currentStep); + + CURRENT_SETP = currentStep; + updateNotification(CURRENT_SETP); + saveStep(currentStep); + } + + private void saveStep(int currentStep) { + sHandler.removeMessages(HANDLER_WHAT_SAVE_STEP); + sHandler.sendEmptyMessageDelayed(HANDLER_WHAT_SAVE_STEP, LAST_SAVE_STEP_DURATION); + + microlog4AndroidError(" mDbSaveCount : " + mDbSaveCount); + + if (DB_SAVE_COUNTER > mDbSaveCount) { + mDbSaveCount++; + return; + } + mDbSaveCount = 0; + + saveDb(false, currentStep); + } + + /** + * @param handler true handler回调保存步数,否false + * @param currentStep + */ + private void saveDb(boolean handler, int currentStep) { + + TodayStepData todayStepData = new TodayStepData(); + todayStepData.setToday(getTodayDate()); + todayStepData.setDate(System.currentTimeMillis()); + todayStepData.setStep(currentStep); + if (null != mTodayStepDBHelper) { + Logger.e(TAG, "saveDb handler : " + handler); + if (!handler || !mTodayStepDBHelper.isExist(todayStepData)) { + Logger.e(TAG, "saveDb currentStep : " + currentStep); + + microlog4AndroidError("saveDb currentStep : " + currentStep); + + mTodayStepDBHelper.insert(todayStepData); + } + } + } + + private void cleanDb() { + + Logger.e(TAG, "cleanDb"); + + mDbSaveCount = 0; + + if (null != mTodayStepDBHelper) { + mTodayStepDBHelper.clearCapacity(DateUtils.dateFormat(System.currentTimeMillis(), "yyyy-MM-dd"), DB_LIMIT); + } + +// if (null != mTodayStepDBHelper) { + //保存多天的步数 +// mTodayStepDBHelper.deleteTable(); +// mTodayStepDBHelper.createTable(); +// } + } + + private String getTodayDate() { + Date date = new Date(System.currentTimeMillis()); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + return sdf.format(date); + } + + /** + * 更新通知 + */ + private void updateNotification(int stepCount) { + if (null == builder || null == nm) { + return; + } + builder.setContentTitle(getString(R.string.title_notification_bar, String.valueOf(stepCount))); + String km = getDistanceByStep(stepCount); + String calorie = getCalorieByStep(stepCount); + builder.setContentText(calorie + " 千卡 " + km + " 公里"); + notification = builder.build(); + nm.notify(R.string.app_name, notification); + } + + private boolean isStepCounter() { + return getPackageManager().hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_COUNTER); + } + + private boolean isStepDetector() { + return getPackageManager().hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_DETECTOR); + } + + private OnStepCounterListener mOnStepCounterListener = new OnStepCounterListener() { + @Override + public void onChangeStepCounter(int step) { + updateTodayStep(step); + } + + @Override + public void onStepCounterClean() { + + CURRENT_SETP = 0; + updateNotification(CURRENT_SETP); + + cleanDb(); + } + + }; + + private final ISportStepInterface.Stub mIBinder = new ISportStepInterface.Stub() { + + @Override + public int getCurrentTimeSportStep() throws RemoteException { + return CURRENT_SETP; + } + + private JSONArray getSportStepJsonArray(List todayStepDataArrayList) { + return SportStepJsonUtils.getSportStepJsonArray(todayStepDataArrayList); + } + + @Override + public String getTodaySportStepArray() throws RemoteException { + if (null != mTodayStepDBHelper) { + List todayStepDataArrayList = mTodayStepDBHelper.getQueryAll(); + JSONArray jsonArray = getSportStepJsonArray(todayStepDataArrayList); + Logger.e(TAG, jsonArray.toString()); + return jsonArray.toString(); + } + return null; + } + + @Override + public String getTodaySportStepArrayByDate(String date) throws RemoteException { + if (null != mTodayStepDBHelper) { + List todayStepDataArrayList = mTodayStepDBHelper.getStepListByDate(date); + JSONArray jsonArray = getSportStepJsonArray(todayStepDataArrayList); + Logger.e(TAG, jsonArray.toString()); + return jsonArray.toString(); + } + return null; + } + + @Override + public String getTodaySportStepArrayByStartDateAndDays(String date, int days) throws RemoteException { + if (null != mTodayStepDBHelper) { + List todayStepDataArrayList = mTodayStepDBHelper.getStepListByStartDateAndDays(date, days); + JSONArray jsonArray = getSportStepJsonArray(todayStepDataArrayList); + Logger.e(TAG, jsonArray.toString()); + return jsonArray.toString(); + } + return null; + } + }; + + public static String getReceiver(Context context) { + try { + PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_RECEIVERS); + ActivityInfo[] activityInfos = packageInfo.receivers; + if (null != activityInfos && activityInfos.length > 0) { + for (int i = 0; i < activityInfos.length; i++) { + String receiverName = activityInfos[i].name; + Class superClazz = Class.forName(receiverName).getSuperclass(); + int count = 1; + while (null != superClazz) { + if (superClazz.getName().equals("java.lang.Object")) { + break; + } + if (superClazz.getName().equals(BaseClickBroadcast.class.getName())) { + Log.e(TAG, "receiverName : " + receiverName); + return receiverName; + } + if (count > 20) { + //用来做容错,如果20个基类还不到Object直接跳出防止while死循环 + break; + } + count++; + superClazz = superClazz.getSuperclass(); + Log.e(TAG, "superClazz : " + superClazz); + + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + private void microlog4AndroidError(String msg){ + if (null != mMicrolog4Android) { + mMicrolog4Android.error(DateUtils.getCurrentDate("yyyy-MM-dd HH:mm:ss") + " " + msg); + } + } + +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepShutdownReceiver.java b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepShutdownReceiver.java new file mode 100644 index 0000000..c47df83 --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/TodayStepShutdownReceiver.java @@ -0,0 +1,23 @@ +package com.today.step.lib; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +/** + * Created by jiahongfei on 2017/9/27. + */ + +public class TodayStepShutdownReceiver extends BroadcastReceiver { + + private static final String TAG = "TodayStepShutdownReceiver"; + + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_SHUTDOWN)) { + Logger.e(TAG,"TodayStepShutdownReceiver"); + PreferencesHelper.setShutdown(context,true); + } + } + +} diff --git a/todaystepcounterlib/src/main/java/com/today/step/lib/WakeLockUtils.java b/todaystepcounterlib/src/main/java/com/today/step/lib/WakeLockUtils.java new file mode 100644 index 0000000..2db3788 --- /dev/null +++ b/todaystepcounterlib/src/main/java/com/today/step/lib/WakeLockUtils.java @@ -0,0 +1,39 @@ +package com.today.step.lib; + +import android.content.Context; +import android.os.PowerManager; + +import java.util.Calendar; + +/** + * @author : jiahongfei + * @email : jiahongfeinew@163.com + * @date : 2018/2/12 + * @desc : + */ + +class WakeLockUtils { + + private static PowerManager.WakeLock mWakeLock; + + synchronized static PowerManager.WakeLock getLock(Context context) { + if (mWakeLock != null) { + if (mWakeLock.isHeld()) + mWakeLock.release(); + mWakeLock = null; + } + + if (mWakeLock == null) { + PowerManager mgr = (PowerManager) context + .getSystemService(Context.POWER_SERVICE); + mWakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + TodayStepService.class.getName()); + mWakeLock.setReferenceCounted(true); + Calendar c = Calendar.getInstance(); + c.setTimeInMillis(System.currentTimeMillis()); + mWakeLock.acquire(); + } + return (mWakeLock); + } + +} diff --git a/todaystepcounterlib/src/main/res/mipmap-xxxhdpi/ic_notification_default.png b/todaystepcounterlib/src/main/res/mipmap-xxxhdpi/ic_notification_default.png new file mode 100644 index 0000000..aee44e1 Binary files /dev/null and b/todaystepcounterlib/src/main/res/mipmap-xxxhdpi/ic_notification_default.png differ diff --git a/todaystepcounterlib/src/main/res/values/strings.xml b/todaystepcounterlib/src/main/res/values/strings.xml new file mode 100644 index 0000000..c7f2281 --- /dev/null +++ b/todaystepcounterlib/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + TodayStepCounterLib + 当前步数:%1$s + diff --git a/todaystepcounterlib/src/test/java/com.today.step.lib/MyRobolectricTestRunner.java b/todaystepcounterlib/src/test/java/com.today.step.lib/MyRobolectricTestRunner.java new file mode 100644 index 0000000..4ca5cd1 --- /dev/null +++ b/todaystepcounterlib/src/test/java/com.today.step.lib/MyRobolectricTestRunner.java @@ -0,0 +1,53 @@ +package com.today.step.lib; + +import org.junit.runners.model.InitializationError; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.manifest.AndroidManifest; +import org.robolectric.res.Fs; + +/** + * @author : jiahongfei + * @email : jiahongfeinew@163.com + * @date : 2018/1/22 + * @desc : + */ + +public class MyRobolectricTestRunner extends RobolectricTestRunner { + /** + * Creates a runner to run {@code testClass}. Looks in your working directory for your AndroidManifest.xml file + * and res directory by default. Use the {@link Config} annotation to configure. + * + * @param testClass the test class to be run + * @throws InitializationError if junit says so + */ + public MyRobolectricTestRunner(Class testClass) throws InitializationError { + super(testClass); + } + + @Override + protected AndroidManifest getAppManifest(Config config) { + String projectName = "TodayStepCounter"; + int nameLength = projectName.length(); + String rootPath = System.getProperty("user.dir", "./"); + int index = rootPath.indexOf(projectName); + if (index == -1) { + throw new RuntimeException("project name not found in user.dir"); + } + //获取项目的根目录 + rootPath = rootPath.substring(0, index + nameLength); + String manifestProperty = rootPath + "/todaystepcounterlib/src/main/AndroidManifest.xml"; + String resProperty = rootPath + "/todaystepcounterlib/src/main/res"; + String assetsProperty = rootPath + "/todaystepcounterlib/src/main/assets"; + return new AndroidManifest( + Fs.fileFromPath(manifestProperty), + Fs.fileFromPath(resProperty), + Fs.fileFromPath(assetsProperty)) { + @Override + public int getTargetSdkVersion() { + return 21; + } + }; + } + +} diff --git a/todaystepcounterlib/src/test/java/com.today.step.lib/TodayStepDBHelperTest.java b/todaystepcounterlib/src/test/java/com.today.step.lib/TodayStepDBHelperTest.java new file mode 100644 index 0000000..447b0da --- /dev/null +++ b/todaystepcounterlib/src/test/java/com.today.step.lib/TodayStepDBHelperTest.java @@ -0,0 +1,110 @@ +package com.today.step.lib; + +import android.app.Application; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import java.util.Calendar; +import java.util.List; + +/** + * @author : jiahongfei + * @email : jiahongfeinew@163.com + * @date : 2018/1/22 + * @desc : + */ +@RunWith(MyRobolectricTestRunner.class) +@Config(constants = BuildConfig.class, sdk = 21) +public class TodayStepDBHelperTest { + + private static final String SPORT_DATE = "sportDate"; + private static final String STEP_NUM = "stepNum"; + + private ITodayStepDBHelper mTodayStepDBHelper; + + @Before + public void before(){ + + Application application = RuntimeEnvironment.application; + + System.out.println(application); + + mTodayStepDBHelper = TodayStepDBHelper.factory(application); + + } + + @Test + public void insert(){ + + //10天 + for (int i = 0; i<10; i++){ + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(DateUtils.getDateMillis("2018-01-01","yyyy-MM-dd")); + if(4 == i || 5 == i || 7 == i || 8 == i){ + continue; + } + calendar.add(Calendar.DAY_OF_YEAR,i); + //每天存储5次步数 + for (int j = 0; j<5; j++){ + TodayStepData todayStepData = new TodayStepData(); + todayStepData.setToday(DateUtils.dateFormat(calendar.getTimeInMillis(),"yyyy-MM-dd")); + todayStepData.setStep(100*j); + Calendar hourCalendar = Calendar.getInstance(); + hourCalendar.setTimeInMillis(DateUtils.getDateMillis("2018-01-01 08:00","yyyy-MM-dd HH:mm")); + hourCalendar.add(Calendar.HOUR_OF_DAY,j); + todayStepData.setDate(hourCalendar.getTimeInMillis()); + mTodayStepDBHelper.insert(todayStepData); + } + } + + List todayStepDataList = mTodayStepDBHelper.getQueryAll(); + JSONArray jsonArray = getSportStepJsonArray(todayStepDataList); + System.out.println(jsonArray.toString()); + + todayStepDataList = mTodayStepDBHelper.getStepListByDate("2018-01-01"); + jsonArray = getSportStepJsonArray(todayStepDataList); + System.out.println(jsonArray.toString()); + + todayStepDataList = mTodayStepDBHelper.getStepListByStartDateAndDays("2018-01-03",3); + jsonArray = getSportStepJsonArray(todayStepDataList); + System.out.println(jsonArray.toString()); + + + +// mTodayStepDBHelper.clearCapacity("2018-01-10",7); +// +// todayStepDataList = mTodayStepDBHelper.getQueryAll(); +// jsonArray = getSportStepJsonArray(todayStepDataList); +// Systemhttp://pa.mokous.com/share/renewal/esb_plus_award_notice.html.out.println(jsonArray.toString()); + + + } + + + private JSONArray getSportStepJsonArray(List todayStepDataArrayList){ + JSONArray jsonArray = new JSONArray(); + if (null == todayStepDataArrayList || 0 == todayStepDataArrayList.size()) { + return jsonArray; + } + for (int i = 0; i < todayStepDataArrayList.size(); i++) { + TodayStepData todayStepData = todayStepDataArrayList.get(i); + try { + JSONObject subObject = new JSONObject(); + subObject.put(TodayStepDBHelper.TODAY, todayStepData.getToday()); + subObject.put(SPORT_DATE, DateUtils.dateFormat(todayStepData.getDate(),"yyyy-MM-dd HH:mm")); + subObject.put(STEP_NUM, todayStepData.getStep()); + jsonArray.put(subObject); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return jsonArray; + } +}