├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
├── release
│ ├── app-release.apk
│ └── output.json
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── habittest
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── habittest
│ │ │ ├── AboutUs.java
│ │ │ ├── AddHabit.java
│ │ │ ├── CalendarReminderUtils.java
│ │ │ ├── DBManager.java
│ │ │ ├── Fragment1.java
│ │ │ ├── Fragment2.java
│ │ │ ├── Fragment3.java
│ │ │ ├── Habit.java
│ │ │ ├── HabitListItem.java
│ │ │ ├── HabitListItemAdapter.java
│ │ │ ├── HabitLog.java
│ │ │ ├── HelpLayout.java
│ │ │ ├── MainActivity.java
│ │ │ ├── MySqliteHelper.java
│ │ │ ├── OverHabit.java
│ │ │ ├── TodayCard.java
│ │ │ └── Utils.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── card.jpg
│ │ ├── card_1.jpg
│ │ ├── card_2.jpg
│ │ ├── card_3.jpg
│ │ ├── card_4.jpg
│ │ ├── card_5.jpg
│ │ ├── card_6.jpg
│ │ ├── const_edge.xml
│ │ ├── habit_1.png
│ │ ├── habit_1_gray.png
│ │ ├── habit_2.png
│ │ ├── habit_2_gray.png
│ │ ├── habit_3.png
│ │ ├── habit_3_gray.png
│ │ ├── habit_4.png
│ │ ├── habit_4_gray.png
│ │ ├── habit_5.png
│ │ ├── habit_5_gray.png
│ │ ├── habit_6.png
│ │ ├── ic_about_us.png
│ │ ├── ic_all_normal.png
│ │ ├── ic_all_select.png
│ │ ├── ic_card.png
│ │ ├── ic_dashboard_black_24dp.xml
│ │ ├── ic_help.png
│ │ ├── ic_home_1_normal.png
│ │ ├── ic_home_1_select.png
│ │ ├── ic_home_black_24dp.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_menu_share.png
│ │ ├── ic_more_normal.png
│ │ ├── ic_more_select.png
│ │ ├── ic_notifications_black_24dp.xml
│ │ ├── ic_over.png
│ │ ├── ic_share.png
│ │ ├── ic_sina.png
│ │ ├── logo.png
│ │ ├── plus.png
│ │ ├── pure.xml
│ │ ├── select.xml
│ │ ├── shadow_date.xml
│ │ ├── shape.xml
│ │ ├── shape_1.xml
│ │ ├── shape_1_selected.xml
│ │ ├── shape_2.xml
│ │ ├── shape_2_selected.xml
│ │ ├── shape_finish.xml
│ │ ├── shape_mine.xml
│ │ ├── shape_selected.xml
│ │ ├── shape_time.xml
│ │ ├── sign_point.xml
│ │ └── social_qq.png
│ │ ├── layout
│ │ ├── activity_about_us.xml
│ │ ├── activity_add_habit.xml
│ │ ├── activity_habit_log.xml
│ │ ├── activity_main.xml
│ │ ├── activity_over_habit.xml
│ │ ├── activity_today_card.xml
│ │ ├── all_habits.xml
│ │ ├── date_item.xml
│ │ ├── grid_item.xml
│ │ ├── habit_grid.xml
│ │ ├── habit_list_item.xml
│ │ ├── help.xml
│ │ └── mine.xml
│ │ ├── menu
│ │ └── navigation.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_check_white.png
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ └── md_nav_back.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ └── icon_back_arrow.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ │ └── xml
│ │ └── file_paths.xml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── habittest
│ └── ExampleUnitTest.java
├── build.gradle
├── example
├── 1.jpg
├── 2.jpg
├── 3.jpg
├── 4.jpg
├── 5.jpg
└── 6.jpg
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches/build_file_checksums.ser
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | .DS_Store
9 | /build
10 | /captures
11 | .externalNativeBuild
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HabitDeveloping
2 | 一款习惯养成app,包含设定习惯目标、打卡、习惯提醒、今日卡片等功能。
3 |
4 | ## 说明
5 | - 系统:Android 8.1以上
6 | - 分辨率:2340 * 1080
7 |
8 | 你可以[点击此处](https://github.com/a925722655/HabitDeveloping/raw/master/app/release/app-release.apk)下载apk安装
9 |
10 | ## 界面
11 | 今日习惯
12 |
13 |
14 |
15 | 添加习惯
16 |
17 |
18 |
19 | 全部习惯
20 |
21 |
22 |
23 | 习惯详情
24 |
25 |
26 |
27 | 关于
28 |
29 |
30 |
31 | 今日卡片
32 |
33 |
34 |
35 |
36 | 第一次写Android,代码不够优雅,有很多硬编码,还有一些小bug。
37 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 27
5 | aaptOptions.cruncherEnabled = false
6 | aaptOptions.useNewCruncher = false
7 | defaultConfig {
8 | applicationId "com.example.habittest"
9 | minSdkVersion 26
10 | targetSdkVersion 27
11 | versionCode 1
12 | versionName "1.0"
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 |
22 | }
23 |
24 | dependencies {
25 | implementation fileTree(include: ['*.jar'], dir: 'libs')
26 | implementation 'com.android.support:appcompat-v7:27.1.1'
27 | implementation 'com.android.support:design:27.1.1'
28 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
29 | testImplementation 'junit:junit:4.12'
30 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
31 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
32 |
33 | // jsoup HTML parser library @ https://jsoup.org/
34 | implementation 'org.jsoup:jsoup:1.12.1'
35 |
36 | implementation 'com.contrarywind:Android-PickerView:4.1.8'
37 | }
38 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/release/app-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a9257/HabitDeveloping/2d913f1673dba1aa627ef2d4b1a6e19b35a6ca3d/app/release/app-release.apk
--------------------------------------------------------------------------------
/app/release/output.json:
--------------------------------------------------------------------------------
1 | [{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/example/habittest/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.example.habittest;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.example.habittest", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
20 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
40 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/habittest/AboutUs.java:
--------------------------------------------------------------------------------
1 | package com.example.habittest;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.content.pm.ApplicationInfo;
6 | import android.content.pm.PackageManager;
7 | import android.net.Uri;
8 | import android.support.v7.app.AppCompatActivity;
9 | import android.os.Bundle;
10 | import android.view.View;
11 | import android.widget.Toast;
12 |
13 | public class AboutUs extends AppCompatActivity {
14 |
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 | setContentView(R.layout.activity_about_us);
19 |
20 | android.support.v7.widget.Toolbar toolbar = (android.support.v7.widget.Toolbar) findViewById(R.id.toolbar5);
21 | toolbar.setNavigationOnClickListener(new View.OnClickListener() {
22 | @Override
23 | public void onClick(View view) {
24 | finish();
25 | }
26 | });
27 |
28 | }
29 |
30 | // 跳转至微博个人页
31 | public void jumpToWeiboProfileInfo(Context context, String uid) {
32 | Intent intent = new Intent(Intent.ACTION_VIEW);
33 | intent.addCategory(Intent.CATEGORY_DEFAULT);
34 | intent.addCategory(Intent.CATEGORY_BROWSABLE);
35 | boolean weiboInstalled = Utils.isSinaWeiboInstalled(context);
36 | if (weiboInstalled) {
37 | intent.setData(Uri.parse("sinaweibo://userinfo?uid=" + uid));
38 | } else {
39 | intent.setData(Uri.parse("http://weibo.cn/qr/userinfo?uid=" + uid));
40 | }
41 | context.startActivity(intent);
42 | }
43 |
44 | public void weibo(View v) {
45 | jumpToWeiboProfileInfo(this, "2102377183");
46 | }
47 |
48 | public void jumptoQQ(View view) {
49 | String qqnum = "982728182";
50 | if (checkApkExist(this, "com.tencent.mobileqq")) {
51 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("mqqwpa://im/chat?chat_type=wpa&uin=" + qqnum + "&version=1")));
52 | } else {
53 | Toast.makeText(this, "本机未安装QQ应用", Toast.LENGTH_SHORT).show();
54 | }
55 | }
56 |
57 | public boolean checkApkExist(Context context, String packageName) {
58 | if (packageName == null || "".equals(packageName))
59 | return false;
60 | try {
61 | ApplicationInfo info = context.getPackageManager().getApplicationInfo(packageName,
62 | PackageManager.GET_UNINSTALLED_PACKAGES);
63 | return true;
64 | } catch (PackageManager.NameNotFoundException e) {
65 | return false;
66 | }
67 | }
68 |
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/habittest/AddHabit.java:
--------------------------------------------------------------------------------
1 | package com.example.habittest;
2 |
3 | import android.database.sqlite.SQLiteDatabase;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.os.Bundle;
6 | import android.view.View;
7 | import android.widget.Button;
8 | import android.widget.EditText;
9 | import android.widget.ImageView;
10 | import android.widget.RadioGroup;
11 | import android.widget.TextView;
12 | import android.support.v7.widget.Toolbar;
13 | import android.widget.Toast;
14 |
15 |
16 | import com.bigkoo.pickerview.builder.TimePickerBuilder;
17 | import com.bigkoo.pickerview.listener.OnTimeSelectListener;
18 | import com.bigkoo.pickerview.view.TimePickerView;
19 |
20 | import java.util.Calendar;
21 | import java.util.Date;
22 |
23 | public class AddHabit extends AppCompatActivity {
24 |
25 | private ImageView imageView;
26 | private Button[] bt = new Button[4];
27 | private Button[] bt2 = new Button[3];
28 | private String[] t = {"任意时间", "晨间习惯", "午间习惯", "晚间习惯"};
29 | private String[] f = {"每日", "每周", "每月"};
30 |
31 | private String img;
32 | private String time;
33 | private String frequency;
34 | private String strHour = "";
35 | private String strMin = "";
36 |
37 | //数据库相关变量
38 | private MySqliteHelper helper;
39 | private SQLiteDatabase db;
40 | private DBManager mgr;
41 |
42 | @Override
43 | protected void onCreate(Bundle savedInstanceState) {
44 | super.onCreate(savedInstanceState);
45 | setContentView(R.layout.activity_add_habit);
46 | final String[] imgName = {"habit_1", "habit_2", "habit_3", "habit_4", "habit_5"};
47 |
48 | //数据库变量初始化
49 | helper = DBManager.getIntance(this);
50 | db = helper.getWritableDatabase();//创建或打开数据库
51 | mgr = new DBManager(db);
52 |
53 | //返回事件
54 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar2);
55 | toolbar.setNavigationOnClickListener(new View.OnClickListener() {
56 | @Override
57 | public void onClick(View view) {
58 | finish();
59 | }
60 | });
61 |
62 | bt[0] = (Button) findViewById(R.id.button);
63 | bt[1] = (Button) findViewById(R.id.morning);
64 | bt[2] = (Button) findViewById(R.id.noon);
65 | bt[3] = (Button) findViewById(R.id.button4);
66 |
67 | bt2[0] = (Button) findViewById(R.id.button5);
68 | bt2[1] = (Button) findViewById(R.id.button6);
69 | bt2[2] = (Button) findViewById(R.id.button7);
70 |
71 |
72 | //设置初始选择任意时间
73 | selectTime(0);
74 | //设置初始选择每天
75 | selectFre(0);
76 |
77 | //选择图标事件
78 | RadioGroup rg = (RadioGroup) findViewById(R.id.radioGroup);
79 | imageView = (ImageView) findViewById(R.id.habit_img);
80 |
81 | //设置初始图标
82 | img = imgName[0];
83 | rg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
84 | @Override
85 | public void onCheckedChanged(RadioGroup group, int checkedId) {
86 | switch (checkedId) {
87 | case R.id.img1:
88 | imageView.setImageResource(getResources().getIdentifier(imgName[0], "drawable", getPackageName()));
89 | img = imgName[0];
90 | break;
91 | case R.id.img2:
92 | imageView.setImageResource(getResources().getIdentifier(imgName[1], "drawable", getPackageName()));
93 | img = imgName[1];
94 | break;
95 | case R.id.img3:
96 | imageView.setImageResource(getResources().getIdentifier(imgName[2], "drawable", getPackageName()));
97 | img = imgName[2];
98 | break;
99 | case R.id.img4:
100 | imageView.setImageResource(getResources().getIdentifier(imgName[3], "drawable", getPackageName()));
101 | img = imgName[3];
102 | break;
103 | case R.id.img5:
104 | imageView.setImageResource(getResources().getIdentifier(imgName[4], "drawable", getPackageName()));
105 | img = imgName[4];
106 | break;
107 | }
108 | }
109 | });
110 |
111 | //时间选择器
112 | final TimePickerView pvTime = new TimePickerBuilder(AddHabit.this, new OnTimeSelectListener() {
113 | @Override
114 | public void onTimeSelect(Date date, View v) {
115 | Calendar cal = Calendar.getInstance();
116 | cal.setTime(date);
117 | // Toast.makeText(AddHabit.this, cal.get(Calendar.HOUR_OF_DAY) + "" + cal.get(Calendar.MINUTE), Toast.LENGTH_SHORT).show();
118 | EditText et = (EditText) findViewById(R.id.editText3);
119 | strHour = String.format("%02d", cal.get(Calendar.HOUR_OF_DAY));
120 | strMin = String.format("%02d", cal.get(Calendar.MINUTE));
121 | et.setText(strHour + " : " + strMin);
122 | }
123 | }).setType(new boolean[]{false, false, false, true, true, false})
124 | .setTitleText("设置提醒时间")
125 | .isCyclic(true)
126 | .build();
127 |
128 | findViewById(R.id.editText3).setOnClickListener(new View.OnClickListener() {
129 | @Override
130 | public void onClick(View v) {
131 | pvTime.show();
132 | }
133 | });
134 |
135 |
136 | }
137 |
138 | public void selectTime(int k) {
139 | for (int i = 0; i < 4; i++) {
140 | if (i == k) {
141 | bt[i].setTextColor(getResources().getColor(R.color.white));
142 | bt[i].setBackgroundResource(R.drawable.shape_selected);
143 | } else {
144 | bt[i].setTextColor(getResources().getColor(R.color.black));
145 | bt[i].setBackgroundResource(R.drawable.shape);
146 | }
147 | }
148 | time = t[k];
149 | }
150 |
151 | public void time1(View view) {
152 | selectTime(0);
153 | }
154 |
155 | public void time2(View view) {
156 | selectTime(1);
157 | }
158 |
159 | public void time3(View view) {
160 | selectTime(2);
161 | }
162 |
163 | public void time4(View view) {
164 | selectTime(3);
165 | }
166 |
167 | public void selectFre(int k) {
168 | for (int i = 0; i < 3; i++) {
169 | if (i == k) {
170 | bt2[i].setTextColor(getResources().getColor(R.color.white));
171 | bt2[i].setBackgroundResource(R.drawable.shape_2_selected);
172 | } else {
173 | bt2[i].setTextColor(getResources().getColor(R.color.bg_color));
174 | bt2[i].setBackgroundResource(R.drawable.shape_2);
175 | }
176 | }
177 | frequency = f[k];
178 | TextView textView = (TextView) findViewById(R.id.textView5);
179 | textView.setText(f[k]);
180 | }
181 |
182 | public void fre1(View view) {
183 | selectFre(0);
184 | }
185 |
186 | public void fre2(View view) {
187 | selectFre(1);
188 | }
189 |
190 | public void fre3(View view) {
191 | selectFre(2);
192 | }
193 |
194 | //获取时间的函数
195 | //获取当天零点的时间
196 | public long get_zero_time() {
197 | Calendar calendar = Calendar.getInstance();
198 | calendar.set(Calendar.HOUR_OF_DAY, 0);
199 | calendar.set(Calendar.MINUTE, 0);
200 | calendar.set(Calendar.SECOND, 0);
201 | calendar.set(Calendar.MILLISECOND, 0);
202 | //当天0点
203 | long zero = calendar.getTimeInMillis();
204 | return zero;
205 | }
206 |
207 | //根据小时、分钟、习惯名称设置提醒
208 | public void set_date_notice(String habit_name, String htext, int hour_time, int minute_time) {
209 | long zero = get_zero_time();
210 | long extra_msec = 1000 * (hour_time * 60 + minute_time) * 60 + zero;
211 | CalendarReminderUtils.addCalendarEvent(this, habit_name, htext, extra_msec, 0);
212 | }
213 |
214 | //创建习惯事件
215 | public void addaHabit(View view) {
216 | //获取输入框
217 | EditText etName = (EditText) findViewById(R.id.editText);
218 | EditText etNum = (EditText) findViewById(R.id.editText2);
219 | // EditText etHour = (EditText) findViewById(R.id.editText3);
220 | // EditText etMin = (EditText) findViewById(R.id.editText4);
221 | EditText etText = (EditText) findViewById(R.id.editText5);
222 | //获取输入值
223 | String name = etName.getText().toString();
224 | String strNum = etNum.getText().toString();
225 | // String strHour = etHour.getText().toString();
226 | // String strMin = etMin.getText().toString();
227 | String htext = etText.getText().toString();
228 |
229 | int num, hour, min;
230 |
231 | if (name.equals("")) {
232 | Toast.makeText(this, "习惯名不能为空", Toast.LENGTH_SHORT).show();
233 | return;
234 | }
235 | if (strNum.equals("")) {
236 | Toast.makeText(this, "打卡次数不能为空", Toast.LENGTH_SHORT).show();
237 | return;
238 | }
239 | num = Integer.parseInt(strNum);
240 | if (htext.equals("")) {
241 | htext = "只有千锤百炼,才能成为好钢。";
242 | }
243 | if (strHour.equals("") && strMin.equals("")) {
244 | Date date = new Date();
245 | Habit habit = new Habit(name, img, num, 0, time, frequency, htext, 0, 0, 0, Utils.date2String(date), 1);
246 | if (mgr.insertHabitDB(habit)) {
247 | finish();
248 | return;
249 | } else {
250 | Toast.makeText(this, "习惯名不能重复", Toast.LENGTH_SHORT).show();
251 | return;
252 | }
253 | }
254 | if ((!strHour.equals("")) && (!strMin.equals(""))) {
255 | hour = Integer.parseInt(strHour);
256 | min = Integer.parseInt(strMin);
257 | if (hour < 24 && hour >= 0 && min >= 0 && min < 60) {
258 | Date date = new Date();
259 | Habit habit = new Habit(name, img, num, 0, time, frequency, htext, 0, 0, 0, Utils.date2String(date), 1);
260 | if (mgr.insertHabitDB(habit)) {
261 | set_date_notice(name, htext, hour, min);
262 | finish();
263 | return;
264 | } else {
265 | Toast.makeText(this, "习惯名不能重复", Toast.LENGTH_SHORT).show();
266 | return;
267 | }
268 | } else {
269 | Toast.makeText(this, "确保提醒时间设置正确", Toast.LENGTH_SHORT).show();
270 | return;
271 | }
272 | }
273 | Toast.makeText(this, "确保提醒时间设置正确", Toast.LENGTH_SHORT).show();
274 | }
275 |
276 |
277 | }
278 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/habittest/CalendarReminderUtils.java:
--------------------------------------------------------------------------------
1 | package com.example.habittest;
2 |
3 | import android.content.ContentUris;
4 | import android.content.ContentValues;
5 | import android.content.Context;
6 | import android.database.Cursor;
7 | import android.graphics.Color;
8 | import android.net.Uri;
9 | import android.os.Build;
10 | import android.provider.CalendarContract;
11 | import android.support.annotation.RequiresApi;
12 | import android.text.TextUtils;
13 |
14 | import java.util.Calendar;
15 | import java.util.TimeZone;
16 |
17 |
18 |
19 | public class CalendarReminderUtils {
20 | private static String CALENDER_URL = "content://com.android.calendar/calendars";
21 | private static String CALENDER_EVENT_URL = "content://com.android.calendar/events";
22 | private static String CALENDER_REMINDER_URL = "content://com.android.calendar/reminders";
23 |
24 | private static String CALENDARS_NAME = "boohee";
25 | private static String CALENDARS_ACCOUNT_NAME = "BOOHEE@boohee.com";
26 | private static String CALENDARS_ACCOUNT_TYPE = "com.android.boohee";
27 | private static String CALENDARS_DISPLAY_NAME = "BOOHEE账户";
28 |
29 | /**
30 | * 检查是否已经添加了日历账户,如果没有添加先添加一个日历账户再查询
31 | * 获取账户成功返回账户id,否则返回-1
32 | */
33 | @RequiresApi(api = Build.VERSION_CODES.N)
34 | private static int checkAndAddCalendarAccount(Context context) {
35 | int oldId = checkCalendarAccount(context);
36 | if( oldId >= 0 ){
37 | return oldId;
38 | }else{
39 | long addId = addCalendarAccount(context);
40 | if (addId >= 0) {
41 | return checkCalendarAccount(context);
42 | } else {
43 | return -1;
44 | }
45 | }
46 | }
47 |
48 | /**
49 | * 检查是否存在现有账户,存在则返回账户id,否则返回-1
50 | */
51 | private static int checkCalendarAccount(Context context) {
52 | Cursor userCursor = context.getContentResolver().query(Uri.parse(CALENDER_URL), null, null, null, null);
53 | try {
54 | if (userCursor == null) { //查询返回空值
55 | return -1;
56 | }
57 | int count = userCursor.getCount();
58 | if (count > 0) { //存在现有账户,取第一个账户的id返回
59 | userCursor.moveToFirst();
60 | return userCursor.getInt(userCursor.getColumnIndex(CalendarContract.Calendars._ID));
61 | } else {
62 | return -1;
63 | }
64 | } finally {
65 | if (userCursor != null) {
66 | userCursor.close();
67 | }
68 | }
69 | }
70 |
71 | /**
72 | * 添加日历账户,账户创建成功则返回账户id,否则返回-1
73 | */
74 |
75 | private static long addCalendarAccount(Context context) {
76 | TimeZone timeZone = TimeZone.getDefault();
77 | ContentValues value = new ContentValues();
78 | value.put(CalendarContract.Calendars.NAME, CALENDARS_NAME);
79 | value.put(CalendarContract.Calendars.ACCOUNT_NAME, CALENDARS_ACCOUNT_NAME);
80 | value.put(CalendarContract.Calendars.ACCOUNT_TYPE, CALENDARS_ACCOUNT_TYPE);
81 | value.put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, CALENDARS_DISPLAY_NAME);
82 | value.put(CalendarContract.Calendars.VISIBLE, 1);
83 | value.put(CalendarContract.Calendars.CALENDAR_COLOR, Color.BLUE);
84 | value.put(CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL, CalendarContract.Calendars.CAL_ACCESS_OWNER);
85 | value.put(CalendarContract.Calendars.SYNC_EVENTS, 1);
86 | value.put(CalendarContract.Calendars.CALENDAR_TIME_ZONE, timeZone.getID());
87 | value.put(CalendarContract.Calendars.OWNER_ACCOUNT, CALENDARS_ACCOUNT_NAME);
88 | value.put(CalendarContract.Calendars.CAN_ORGANIZER_RESPOND, 0);
89 |
90 | Uri calendarUri = Uri.parse(CALENDER_URL);
91 | calendarUri = calendarUri.buildUpon()
92 | .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
93 | .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_NAME, CALENDARS_ACCOUNT_NAME)
94 | .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_TYPE, CALENDARS_ACCOUNT_TYPE)
95 | .build();
96 |
97 | Uri result = context.getContentResolver().insert(calendarUri, value);
98 | long id = result == null ? -1 : ContentUris.parseId(result);
99 | return id;
100 | }
101 |
102 | /**
103 | * 添加日历事件
104 | */
105 | @RequiresApi(api = Build.VERSION_CODES.N)
106 | public static void addCalendarEvent(Context context, String title, String description, long reminderTime, int previousDate)
107 | {
108 | if (context == null)
109 | {
110 | return;
111 | }
112 | int calId = checkAndAddCalendarAccount(context); //获取日历账户的id
113 | if (calId < 0)
114 | { //获取账户id失败直接返回,添加日历事件失败
115 | return;
116 | }
117 |
118 | //添加日历事件
119 | Calendar mCalendar = Calendar.getInstance();
120 | mCalendar.setTimeInMillis(reminderTime);//设置开始时间
121 | long start = mCalendar.getTime().getTime();
122 | mCalendar.setTimeInMillis(start + 10 * 60 * 1000);//设置终止时间,开始时间加10分钟
123 | long end = mCalendar.getTime().getTime();
124 | ContentValues event = new ContentValues();
125 | event.put("title", title);
126 | event.put("description", description);
127 | event.put("calendar_id", calId); //插入账户的id
128 | event.put(CalendarContract.Events.DTSTART, start);
129 | event.put(CalendarContract.Events.DTEND, end);
130 | event.put(CalendarContract.Events.HAS_ALARM, 1);//设置有闹钟提醒
131 | event.put(CalendarContract.Events.EVENT_TIMEZONE, "Asia/Shanghai");//这个是时区,必须有
132 | //设置每日提醒
133 | event.put(CalendarContract.Events.RRULE,"FREQ=DAILY");
134 | Uri newEvent = context.getContentResolver().insert(Uri.parse(CALENDER_EVENT_URL), event); //添加事件
135 | if (newEvent == null)
136 | { //添加日历事件失败直接返回
137 | return;
138 | }
139 |
140 | //事件提醒的设定
141 | ContentValues values = new ContentValues();
142 | values.put(CalendarContract.Reminders.EVENT_ID, ContentUris.parseId(newEvent));
143 | values.put(CalendarContract.Reminders.MINUTES, previousDate * 24 * 60);// 提前previousDate天有提醒
144 | values.put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT);
145 | Uri uri = context.getContentResolver().insert(Uri.parse(CALENDER_REMINDER_URL), values);
146 | if(uri == null)
147 | { //添加事件提醒失败直接返回
148 | return;
149 | }
150 | }
151 |
152 | /**
153 | * 删除日历事件
154 | */
155 | public static void deleteCalendarEvent(Context context,String title) {
156 | if (context == null) {
157 | return;
158 | }
159 | Cursor eventCursor = context.getContentResolver().query(Uri.parse(CALENDER_EVENT_URL), null, null, null, null);
160 | try {
161 | if (eventCursor == null) { //查询返回空值
162 | return;
163 | }
164 | if (eventCursor.getCount() > 0) {
165 | //遍历所有事件,找到title跟需要查询的title一样的项
166 | for (eventCursor.moveToFirst(); !eventCursor.isAfterLast(); eventCursor.moveToNext()) {
167 | String eventTitle = eventCursor.getString(eventCursor.getColumnIndex("title"));
168 | if (!TextUtils.isEmpty(title) && title.equals(eventTitle)) {
169 | int id = eventCursor.getInt(eventCursor.getColumnIndex(CalendarContract.Calendars._ID));//取得id
170 | Uri deleteUri = ContentUris.withAppendedId(Uri.parse(CALENDER_EVENT_URL), id);
171 | int rows = context.getContentResolver().delete(deleteUri, null, null);
172 | if (rows == -1) { //事件删除失败
173 | return;
174 | }
175 | }
176 | }
177 | }
178 | } finally {
179 | if (eventCursor != null) {
180 | eventCursor.close();
181 | }
182 | }
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/habittest/DBManager.java:
--------------------------------------------------------------------------------
1 | package com.example.habittest;
2 |
3 | import android.content.Context;
4 | import android.database.Cursor;
5 | import android.database.sqlite.SQLiteDatabase;
6 |
7 | import java.util.Date;
8 |
9 | public class DBManager {
10 | private static MySqliteHelper helper;
11 | private SQLiteDatabase db;
12 |
13 | public static MySqliteHelper getIntance(Context context) {
14 | if (helper == null) {
15 | helper = new MySqliteHelper(context);
16 | }
17 | return helper;
18 | }
19 |
20 | public DBManager(SQLiteDatabase db) {
21 | this.db = db;
22 | }
23 |
24 |
25 | //数据库创建函数
26 | public void createTableOrNot() {
27 | boolean notable = true;
28 | int count = -1;
29 | //先判断表是否存在
30 | String sql = "select count(*) as c from sqlite_master where type ='table' and name ='habits' ";
31 | Cursor cursor = db.rawQuery(sql, null);
32 | if (cursor.moveToNext()) {
33 | count = cursor.getInt(0);
34 | if (count > 0) {
35 | notable = false;
36 | }
37 | }
38 | if (notable) {//不存在则创建
39 | String sql1 = "create table habits (hname text primary key,pic text, total_num integer,finished_num integer,time text, fre text, htext text,days integer, curdays integer, highdays integer, credate text , swit integer)";
40 | String sql2 = "create table daka (hname text,dakadate date)";
41 | db.execSQL(sql1);
42 | db.execSQL(sql2);
43 | this.insertTestRecord();
44 | }
45 | //调试用Log.i("tag",Integer.toString(count));
46 | //调试用Log.i("tag",Boolean.toString(notable));
47 | }
48 |
49 | public void insertTestRecord() {
50 | String sql1 = "insert into habits values ('测试习惯1','habit_1',2,0,'晨间习惯','每天','只有千锤百炼,才能成为好钢。',5,0,3,'20190526',1)";
51 | String sql2 = "insert into habits values ('测试习惯2','habit_2',3,0,'午间习惯','每天','只有千锤百炼,才能成为好钢。',3,0,2,'20190515',1)";
52 | String sql3 = "insert into habits values ('测试习惯3','habit_3',1,0,'晚间习惯','每天','只有千锤百炼,才能成为好钢。',4,0,2,'20190522',1)";
53 | String sql4 = "insert into habits values ('测试习惯4','habit_4',1,0,'任意时间','每天','只有千锤百炼,才能成为好钢。',6,0,3,'20190531',1)";
54 |
55 | String sql5 = "insert into daka values ('测试习惯1','20190601'),('测试习惯1','20190602'),('测试习惯1','20190603'),('测试习惯1','20190610'),('测试习惯1','20190611')";
56 | String sql6 = "insert into daka values ('测试习惯2','20190603'),('测试习惯2','20190610'),('测试习惯2','20190611')";
57 | String sql7 = "insert into daka values ('测试习惯3','20190603'),('测试习惯3','20190604'),('测试习惯3','20190610'),('测试习惯3','20190611')";
58 | String sql8 = "insert into daka values ('测试习惯4','20190601'),('测试习惯4','20190602'),('测试习惯4','20190603'),('测试习惯4','20190609'),('测试习惯4','20190610'),('测试习惯4','20190611')";
59 |
60 | db.execSQL(sql1);
61 | db.execSQL(sql2);
62 | db.execSQL(sql3);
63 | db.execSQL(sql4);
64 | db.execSQL(sql5);
65 | db.execSQL(sql6);
66 | db.execSQL(sql7);
67 | db.execSQL(sql8);
68 | }
69 |
70 | public void clockinUpdateDB(String h) {
71 | //点击签到键更新数据库
72 |
73 | //非必做操作 筛选更新
74 | String sql3 = "update habits set days = days+1 where hname='" + h + "' and finished_num=0";
75 | String sql4 = "update habits set curdays = curdays+1 where hname='" + h + "' and finished_num=0 and curdays=0";
76 | String sql5 = "update habits set highdays = highdays+1 where hname='" + h + "' and finished_num=0 and highdays=0";
77 | db.execSQL(sql3);
78 | db.execSQL(sql4);
79 | db.execSQL(sql5);
80 | //必做操作:finished_num++ 、插入打卡记录
81 | Date date = new Date(); ///获取当前日期
82 | String date_s = Utils.date2String(date);
83 | String sql1 = "update habits set finished_num = finished_num+1 where hname ='" + h + "'";
84 | String sql2 = "insert into daka values ('" + h + "','" + date_s + "')";
85 | db.execSQL(sql1);
86 | db.execSQL(sql2);
87 | }
88 |
89 | //返回给定时间段下的习惯
90 | //isNotFinished为1时返回未结束的,为0返回已结束的
91 | public Habit[] getHabit(String time, int isNotFinished) {
92 | String[] Time = new String[]{time};
93 | if (time == "任意时间") {//选中的是任意时间habits,返回全部habits
94 | String sql = "select count(*) from habits where swit=" + isNotFinished;
95 | Cursor cursor = db.rawQuery(sql, null);
96 | cursor.moveToNext();
97 | int count = cursor.getInt(0);//获取习惯总数
98 | Habit[] h = new Habit[count];//创建habit数组
99 | String sq2 = "select * from habits where swit=" + isNotFinished;
100 | Cursor c = db.rawQuery(sq2, null);
101 | c.moveToFirst();
102 | int i = 0;
103 | while (!c.isAfterLast()) {
104 | Habit h1 = new Habit(c.getString(0), c.getString(1), c.getInt(2), c.getInt(3), c.getString(4), c.getString(5), c.getString(6), c.getInt(7), c.getInt(8), c.getInt(9), c.getString(10), c.getInt(11));
105 | h[i++] = h1;
106 | c.moveToNext();
107 | }
108 | ///应当有count == h.length;
109 | return h;
110 | } else {//其它情况相似
111 | String sql1 = "select count(*) from habits where time=? and swit =" + isNotFinished;
112 | Cursor cursor = db.rawQuery(sql1, Time);
113 | cursor.moveToNext();
114 | int count = cursor.getInt(0);//获取习惯总数
115 | Habit[] h = new Habit[count];//创建habit数组
116 | String sql2 = "select * from habits where time=? and swit =" + isNotFinished;
117 | Cursor c = db.rawQuery(sql2, Time);
118 | c.moveToFirst();
119 | int i = 0;
120 | while (!c.isAfterLast()) {
121 | Habit h1 = new Habit(c.getString(0), c.getString(1), c.getInt(2), c.getInt(3), c.getString(4), c.getString(5), c.getString(6), c.getInt(7), c.getInt(8), c.getInt(9), c.getString(10), c.getInt(11));
122 | h[i++] = h1;
123 | c.moveToNext();
124 | }
125 | ///应当有count == h.length ==i;实际h数组的下标应该为0至i-1
126 | //调试用Log.i("tag##",Integer.toString(i));
127 | return h;
128 | }
129 | }
130 |
131 | //用于在添加新的习惯时更新数据库,成功返回ture,失败返回false(表示该习惯已经存在)。
132 | public boolean insertHabitDB(Habit habit) {
133 |
134 | //第一步先看要添加的habit名称是否已经存在
135 |
136 | String sql1 = "select count(*) from habits where hname = '" + habit.getHname() + "'"; //sql语句查询是否存在该名字
137 | Cursor cursor = db.rawQuery(sql1, null);
138 | cursor.moveToFirst();
139 | int count = cursor.getInt(0);
140 | if (count == 1) //若已经存在这个名字的习惯则直接返回false
141 | return false;
142 | //否则创建这个习惯。
143 | String sql2 = "insert into habits values('" + habit.getHname() + "','" + habit.getPic() + "'," + habit.getTotal_num() + "," + habit.getFinished_num() + ",'" + habit.getTime() + "','" + habit.getFre() + "','" + habit.getHtext() + "'," + habit.getDays() + "," + habit.getCurdays() + "," + habit.getHighdays() + ",'" + habit.getCredate() + "'," + habit.getSwit() + ")";
144 | db.execSQL(sql2);
145 | return true; ///添加则返回true
146 | }
147 |
148 | public void switchHabit(String hname,int swit) {
149 | String sql = "update habits set swit = "+swit+" where hname='" + hname + "'";
150 | db.execSQL(sql);
151 | }
152 |
153 | //获取查看的习惯已经打卡的日期,返回存放已打卡日期的int数组
154 | public int[] getDates(String hname) {
155 | int[] dates;//存放结果的int数组
156 | int count;//数组长度 通过查询记录获得
157 | String sql1 = "select count(*) from daka where hname = '" + hname + "'";
158 | Cursor cursor = db.rawQuery(sql1, null);
159 | cursor.moveToFirst();
160 | count = cursor.getInt(0);
161 | dates = new int[count]; ///获取该习惯打卡总数后,实例化int数组,准备存数据
162 |
163 | String sql2 = "select dakadate from daka where hname = '" + hname + "'";
164 | Cursor c = db.rawQuery(sql2, null);
165 | c.moveToFirst();
166 | int i = 0;
167 | while (!c.isAfterLast()) {//获取日期
168 | String s1 = c.getString(0);
169 | dates[i++] = Integer.parseInt(s1.substring(6, 8));
170 | c.moveToNext();
171 | }
172 | //while过后,应当有count==i==h.length,且s[]下标范围0至i-1
173 |
174 | return dates;
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/habittest/Fragment1.java:
--------------------------------------------------------------------------------
1 | package com.example.habittest;
2 |
3 | import android.database.Cursor;
4 | import android.database.sqlite.SQLiteDatabase;
5 | import android.os.Bundle;
6 | import android.support.v4.app.Fragment;
7 | import android.util.Log;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.AdapterView;
12 | import android.widget.Button;
13 | import android.widget.GridView;
14 | import android.widget.TextView;
15 | import android.widget.SimpleAdapter;
16 | import android.widget.Toast;
17 | import android.widget.Toolbar;
18 |
19 | import java.util.ArrayList;
20 | import java.util.Date;
21 | import java.util.HashMap;
22 | import java.util.List;
23 | import java.util.Map;
24 |
25 | public class Fragment1 extends Fragment {
26 | Habit[] habit;
27 | private View v;
28 | private GridView gview;
29 | private List