diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser new file mode 100644 index 0000000..67fe5f6 Binary files /dev/null and b/.idea/caches/build_file_checksums.ser differ diff --git a/.idea/misc.xml b/.idea/misc.xml index 6f881ac..1a7dcf0 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -43,26 +43,10 @@ - + - - - - - 1.8 - - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 5dc05c1..3b0c3e6 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,12 @@ * I want to be stimulated by my Habit Rabbit. * Habit Rabbit will guilt me into keeping to my habits by accusing me of "murdering" my virtual rabbits if I do not do what I promised I would. +## Landing Page +https://habit-rabbit.000webhostapp.com/ + +## Beta Release +https://youtu.be/Xox4mv7I9XQ + ## Project Description: @@ -20,7 +26,8 @@ Users will be able to * create/login * input, remove and edit habits * view habits in an agenda layout -* view Rabbit Hutch, which is filled with rabbits as you complete a habit, but kills the rabbits when you don’t (and notifies the user that they murdered their rabbits) +* view Rabbit Hutch, which is contain a cute rabit as you complete the habit, but kills the rabbit when you don’t (and notify user if theri rabit dies) +* show the habit history in a graph view form (so user can clearly see how they did in past) ### Add-on features * sync with other apps to be kept accountable (such as Fitbit, MyFitnessPal, etc) @@ -28,6 +35,17 @@ Users will be able to * add “friends” who will be able to shame them when they falsely enter that they did something that they didn’t * bet money,using paypal, that they will complete all their habits for a given period of time. If they fail, the money is donated to a charity of their choice. +### How to use our app + +when you first open up our app, you will see the login page pops up. you have 2 opetions here: first login with your existing account. second, creating an account. To create a account you simplly click on the register button and this will lead you to the registration page. there is 2 limits on creating account: for user name it must contain "@" and for passwords it has to have at least 4 characters. +After you done with register you can back to login page and login there. When you login, you will see our main page which contain only one rabit house. you can go to another page using the "hunberger" button ont he top left and top right. + +In habit page, you can add and create new habit (just click on the button on the buttom right) and in the Agenda page you can see all your habit in a list view. Those data is update whenever you open you app (we update them through our database). + +you can check the habit when you finish them and we will record how you did in a period and display them in a graph view (just click on the habit in habit page for detail). this give user a clearly view how they do in past to build up their habits. + +Lastly, our notification works when you open the app and only when you open the app (we are thinking to make it run in background, but not sure if that is too "expensive" in resource for our app. So still thinking). + ### How to Run Test e-mail: test@example.com @@ -35,7 +53,7 @@ Test password: test You need these to log in once you have run the app. -#### If you have an android device with android version 7.0 (API 24) or above: +#### If you have an android device with android with API level 25 or above (to work properly): 1. Download the apk with your android device using the link provided below. 2. https://drive.google.com/file/d/0B5mPzcwLTPnPQ1VfbldDUHU5eXhTazFzVC1NWUJSQmUtaUpn/view?usp=sharing @@ -64,3 +82,4 @@ https://youtu.be/g607Lzud3Ss ## Alpha Release https://youtu.be/jdsvigK7bPs + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a22506c..c754a28 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -72,7 +72,12 @@ android:name=".DeadRabbitActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:label="@string/title_activity_fullscreen" - android:theme="@style/FullscreenTheme"> + android:theme="@style/FullscreenTheme" /> + + \ No newline at end of file diff --git a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/AddHabitActivity.java b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/AddHabitActivity.java index 68265b8..abc6ddc 100644 --- a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/AddHabitActivity.java +++ b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/AddHabitActivity.java @@ -59,6 +59,7 @@ public void onClick(View view) { public void onClick(View v) { //start new activity startActivity(new Intent(AddHabitActivity.this, HabitListActivity.class)); + finish(); } }); } @@ -85,12 +86,11 @@ private void addHabitRequest() { final String reminder = mRemider.isChecked()?"1":"0"; // send add new habit request - RequestQueue queue = Volley.newRequestQueue(this); + RequestQueue queue = VolleySingleton.getInstance(this).getRequestQueue(this); final String add_habit_url = "https://habit-rabbit.000webhostapp.com/add_habit.php"; - // request server to add this habit to database - StringRequest loginReq = new StringRequest(Request.Method.POST, add_habit_url, + StringRequest addHabitReq = new StringRequest(Request.Method.POST, add_habit_url, new Response.Listener(){ @Override public void onResponse(String response) { @@ -110,8 +110,9 @@ public void onResponse(String response) { //TODO: notify adapter // jump to habit list page - startActivity(new Intent(AddHabitActivity.this, HabitListActivity.class)); +// startActivity(new Intent(AddHabitActivity.this, HabitListActivity.class)); finish(); + HabitListActivity.adapter.notifyItemInserted(HabitList.HABITS_list.size() - 1); } else { // show message when fails AlertDialog.Builder builder = new AlertDialog.Builder(AddHabitActivity.this); @@ -163,6 +164,6 @@ protected Map getParams() { } }; - queue.add(loginReq); + queue.add(addHabitReq); } } diff --git a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/AgendaAdapeter.java b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/AgendaAdapeter.java index 6f8ff93..768aee5 100644 --- a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/AgendaAdapeter.java +++ b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/AgendaAdapeter.java @@ -5,9 +5,11 @@ import android.content.Intent; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; +import android.widget.PopupMenu; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; @@ -128,9 +130,26 @@ void bind(Habit event){ // listener itemView.setOnLongClickListener(v -> { Context context = v.getContext(); - Intent intent = new Intent(context, HabitDetailActivity.class); - intent.putExtra(HabitDetailFragment.ARG_ITEM_ID, event.getHabitID()+""); - context.startActivity(intent); + PopupMenu popupMenu = new PopupMenu(context, itemView); + popupMenu.inflate(R.menu.agenda_popup_menu); + popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.detail: + Intent intent = new Intent(context, HabitDetailActivity.class); + intent.putExtra(HabitDetailFragment.ARG_ITEM_ID, event.getHabitID()+""); + context.startActivity(intent); + break; + case R.id.check: + addRecordRequest(event); + break; + } + return false; + } + }); + popupMenu.show(); + return true; }); } @@ -149,7 +168,7 @@ private void addRecordRequest(Habit habit) { final String date = dateFormat.format(currentDate); Context context = itemView.getContext(); // send delete habit request - RequestQueue queue = Volley.newRequestQueue(context); + RequestQueue queue = VolleySingleton.getInstance(context).getRequestQueue(context); final String add_record_url = "https://habit-rabbit.000webhostapp.com/add_record.php"; // request server to add this habit to database diff --git a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/CalendarActivity.java b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/CalendarActivity.java index a44a448..5edce09 100644 --- a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/CalendarActivity.java +++ b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/CalendarActivity.java @@ -34,9 +34,6 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case Menu.FIRST: - startActivity(new Intent(CalendarActivity.this, LoginActivity.class)); - break; case R.id.nav_Profile: startActivity(new Intent(CalendarActivity.this, ProfileActivity.class)); break; @@ -53,7 +50,9 @@ public boolean onOptionsItemSelected(MenuItem item) { File file= new File(this.getFilesDir().getParent()+"/shared_prefs/"+fileName+".xml"); file.delete(); - startActivity(new Intent(CalendarActivity.this, LoginActivity.class)); + Intent intent = new Intent(this, LoginActivity.class); + intent.setFlags(intent.FLAG_ACTIVITY_NEW_TASK | intent.FLAG_ACTIVITY_CLEAR_TOP | intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent); }catch(Exception e){} break; case R.id.nav_habits: diff --git a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/HabitCheckReciever.java b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/HabitCheckReciever.java new file mode 100644 index 0000000..360e0e1 --- /dev/null +++ b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/HabitCheckReciever.java @@ -0,0 +1,127 @@ +package comjianzhaojohnhabit_rabbit.httpsgithub.habit_rabbit; + +import android.app.AlarmManager; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.util.Log; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Hashtable; + + +public class HabitCheckReciever extends BroadcastReceiver { + private static boolean timerOn = false; + private static SharedPreferences sharedPref; + private static SharedPreferences.Editor editor; + private static Context shared_context; + private static PendingIntent pendingIntent; + private static NotificationHelper notificationHelper; + + @Override + public void onReceive(Context context, Intent intent) { + Log.d("alarm", "reciever"); + checkHabits(); + } + + public static boolean isTimerOn() { return timerOn; } + public void setTimerOn(){timerOn = true; } + + public static void setShared_context(Context context){ + shared_context = context; + sharedPref = shared_context.getSharedPreferences("UserInfo", Context.MODE_PRIVATE); + editor = sharedPref.edit(); + notificationHelper = new NotificationHelper(shared_context); + } + + + public static boolean rabbitIsAlive() { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + int complete = 0; + Calendar today = Calendar.getInstance(); + if (HabitList.HABITS_list != null) { + for (Habit x : HabitList.HABITS_list) { + Hashtable rc = x.getRecords(); + String period = x.getPeriod(); + Calendar startDate = Calendar.getInstance(); + startDate.clear(); + startDate.setTime(x.getStartDate()); + if (period.equals("week") && today.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY) { + Calendar yesterday = Calendar.getInstance(); + if (startDate.get(Calendar.YEAR) == yesterday.get(Calendar.YEAR) && + startDate.get(Calendar.WEEK_OF_YEAR) == yesterday.get(Calendar.WEEK_OF_YEAR)) + break; + yesterday.add(Calendar.DATE, -8); + yesterday.set(Calendar.DAY_OF_WEEK, 1); + for (int i = 0; i < 7; i++) { + String dateString = dateFormat.format(yesterday.getTime()); + if (rc.contains(dateString)) { + complete += rc.get(dateString); + } + yesterday.add(Calendar.DATE, 1); + } + } else if (period.equals("month") && today.get(Calendar.DAY_OF_MONTH) == 1) { + Calendar yesterday = Calendar.getInstance(); + if (startDate.get(Calendar.YEAR) == yesterday.get(Calendar.YEAR) && + startDate.get(Calendar.MONTH) == yesterday.get(Calendar.MONTH)) + break; + yesterday.add(Calendar.MONTH, -1); + yesterday.set(Calendar.DAY_OF_MONTH, 1); + Calendar calendar = Calendar.getInstance(); + calendar.clear(); + calendar.setTime(yesterday.getTime()); + for (calendar.setTime(yesterday.getTime()); calendar.get(Calendar.MONTH) == yesterday.get(Calendar.MONTH); calendar.add(Calendar.DATE, 1)) { + String dateString = dateFormat.format(calendar.getTime()); + if (rc.contains(dateString)) { + complete += rc.get(dateString); + } + yesterday.add(Calendar.DATE, 1); + } + } else { + Calendar yesterday = Calendar.getInstance(); + if (startDate.get(Calendar.DATE) == yesterday.get(Calendar.DATE)) + break; + yesterday.add(Calendar.DATE, -1); + String dateString = dateFormat.format(yesterday.getTime()); + if (rc.contains(dateString)) { + complete = rc.get(dateString); + } + } + if (complete < x.getTimesPerPeriod()) { + return false; + } + } + + return true; + } + return true; + } + + + + public static void checkHabits(){ + editor.putInt("DayOfYear",Calendar.getInstance().get(Calendar.DAY_OF_YEAR)); + editor.apply(); + + boolean rabbitAlive = rabbitIsAlive(); + Log.d("alarm","checking rabbit state"); + String notificationMessage; + if (!rabbitAlive){ + notificationMessage = "MURDERER. Because you failed to complete your habits for today, your rabbit is going to die. His blood is on your hands!"; + } + else { + notificationMessage = "You done good"; + } + notificationHelper.sendNotification(notificationMessage); + + editor.putBoolean("RabbitAlive",rabbitAlive); + editor.apply(); + //TODO send to notification helper + + } +} diff --git a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/HabitDetailActivity.java b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/HabitDetailActivity.java index c42d649..61dc883 100644 --- a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/HabitDetailActivity.java +++ b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/HabitDetailActivity.java @@ -131,7 +131,7 @@ private void editHabitRequest() { final String reminder = mReminder.isChecked()?"1":"0"; // send edit habit request - RequestQueue queue = Volley.newRequestQueue(this); + RequestQueue queue = VolleySingleton.getInstance(this).getRequestQueue(this); final String add_habit_url = "https://habit-rabbit.000webhostapp.com/edit_habit.php"; // request server to add this habit to database diff --git a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/HabitListActivity.java b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/HabitListActivity.java index 07d226e..94facd8 100644 --- a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/HabitListActivity.java +++ b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/HabitListActivity.java @@ -7,6 +7,7 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.Snackbar; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.RecyclerView; @@ -52,6 +53,7 @@ public class HabitListActivity extends AppCompatActivity { * device. */ private boolean mTwoPane; + protected static RecyclerView.Adapter adapter; /** * Called when the activity is starting. @@ -133,9 +135,6 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case Menu.FIRST: - startActivity(new Intent(HabitListActivity.this, LoginActivity.class)); - break; case R.id.nav_Profile: startActivity(new Intent(HabitListActivity.this, ProfileActivity.class)); break; @@ -153,11 +152,13 @@ public boolean onOptionsItemSelected(MenuItem item) { File file= new File(this.getFilesDir().getParent()+"/shared_prefs/"+fileName+".xml"); file.delete(); - startActivity(new Intent(HabitListActivity.this, LoginActivity.class)); + Intent intent = new Intent(this, LoginActivity.class); + intent.setFlags(intent.FLAG_ACTIVITY_NEW_TASK | intent.FLAG_ACTIVITY_CLEAR_TOP | intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent); }catch(Exception e){} break; case R.id.nav_habits: - startActivity(new Intent(HabitListActivity.this, HabitListActivity.class)); +// startActivity(new Intent(HabitListActivity.this, HabitListActivity.class)); break; default: } @@ -167,7 +168,9 @@ public boolean onOptionsItemSelected(MenuItem item) { //set up the recycleView private void setupRecyclerView(@NonNull RecyclerView recyclerView) { // recyclerView.setAdapter(new SimpleItemRecyclerViewAdapter(this, DummyContent.ITEMS, mTwoPane)); - recyclerView.setAdapter(new SimpleItemRecyclerViewAdapter(this, HabitList.HABITS_list, mTwoPane)); +// recyclerView.setAdapter(new SimpleItemRecyclerViewAdapter(this, HabitList.HABITS_list, mTwoPane)); + adapter = new SimpleItemRecyclerViewAdapter(this, HabitList.HABITS_list, mTwoPane); + recyclerView.setAdapter(adapter); } @@ -299,7 +302,8 @@ private void deleteHabitRequest(final Context context, final Habit habit) { final String username = SharedPref.getUser(context); final String habit_id = habit.getHabitID()+""; // send delete habit request - RequestQueue queue = Volley.newRequestQueue(context); + RequestQueue queue = VolleySingleton.getInstance(context) + .getRequestQueue(context); final String add_habit_url = "https://habit-rabbit.000webhostapp.com/delete_habit.php"; // request server to add this habit to database @@ -315,8 +319,6 @@ public void onResponse(String response) { if (success) { deleteHabit(context, habit); - // jump to habit list page - context.startActivity(new Intent(context, HabitListActivity.class)); } else { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle("Delete Habit") diff --git a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/LoginActivity.java b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/LoginActivity.java index 25e895b..cb2f026 100644 --- a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/LoginActivity.java +++ b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/LoginActivity.java @@ -3,7 +3,9 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.TargetApi; +import android.app.AlarmManager; import android.app.AlertDialog; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -47,6 +49,7 @@ import java.io.File; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Calendar; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; @@ -94,6 +97,35 @@ protected void onCreate(Bundle savedInstanceState) { } + if (!HabitCheckReciever.isTimerOn()){ + Context context = getApplicationContext(); + HabitCheckReciever.setShared_context(context); + + //Intent intent = new Intent(context, HabitCheckReciever.class); + Intent intent = new Intent(context, HabitCheckReciever.class); + + PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + + Calendar midnight = Calendar.getInstance(); + midnight.add(Calendar.DATE,1); + midnight.set(Calendar.HOUR, 0); + midnight.set(Calendar.MINUTE, 1); + + Calendar now = Calendar.getInstance(); + Log.d("alarm","set timer"); + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, midnight.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent); + } + SharedPreferences sharedPref = getSharedPreferences("UserInfo", Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + int lastCheckedDay = sharedPref.getInt("DayOfYear", -1); + if (lastCheckedDay != Calendar.getInstance().get(Calendar.DAY_OF_YEAR)) { + HabitCheckReciever.checkHabits(); + } + + + + mEmailView = (AutoCompleteTextView) findViewById(R.id.email); populateAutoComplete(); @@ -224,7 +256,9 @@ private void requestLogin(String email, String password) { final String mPassword = password; // send login request - RequestQueue queue = Volley.newRequestQueue(this); +// RequestQueue queue = Volley.newRequestQueue(this); + RequestQueue queue = VolleySingleton.getInstance(this) + .getRequestQueue(this); // String url_login = "https://habit-rabbit.000webhostapp.com/Login.php"; String url_login = "https://habit-rabbit.000webhostapp.com/Login_encrypt.php"; diff --git a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/MainActivity.java b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/MainActivity.java index 6973282..839df0d 100644 --- a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/MainActivity.java +++ b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/MainActivity.java @@ -47,7 +47,7 @@ protected void onCreate(Bundle savedInstanceState) { } - public boolean isRabbitAlive() { + public static boolean isRabbitAlive() { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); int complete = 0; for (Habit x : HabitList.HABITS_list) { @@ -137,9 +137,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } // following switch statement indicate what happened when you click on the Item in the menu switch (item.getItemId()) { - case Menu.FIRST: - startActivity(new Intent(MainActivity.this, LoginActivity.class)); - break; + case R.id.nav_Profile: startActivity(new Intent(MainActivity.this, ProfileActivity.class)); break; @@ -155,8 +153,10 @@ public boolean onOptionsItemSelected(MenuItem item) { String fileName = SharedPref.FILE_NAME; File file= new File(this.getFilesDir().getParent()+"/shared_prefs/"+fileName+".xml"); file.delete(); - //opent he login page - startActivity(new Intent(MainActivity.this, LoginActivity.class)); + //open the login page + Intent intent = new Intent(this, LoginActivity.class); + intent.setFlags(intent.FLAG_ACTIVITY_NEW_TASK | intent.FLAG_ACTIVITY_CLEAR_TOP | intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent); }catch(Exception e){} break; case R.id.nav_habits: diff --git a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/NotificationHelper.java b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/NotificationHelper.java new file mode 100644 index 0000000..25b66a1 --- /dev/null +++ b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/NotificationHelper.java @@ -0,0 +1,59 @@ +package comjianzhaojohnhabit_rabbit.httpsgithub.habit_rabbit; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.content.ContextWrapper; +import android.graphics.Color; +import android.os.Build; +import android.util.Log; + +public class NotificationHelper extends ContextWrapper { + public static String CHANNEL_ID = "comjianzhaojohnhabit_rabbit.httpsgithub.habit_rabbit.ANDROID"; + public static String CHANNEL_NAME = "Habit Completion"; + + private NotificationManager manager; + private Context context; + public NotificationHelper(Context base) { + super(base); + context = base; + + createChannels(); + } + + private void createChannels() { + if (Build.VERSION.SDK_INT > 25 ){ + NotificationChannel androidChannel = new NotificationChannel(CHANNEL_ID,CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW); + androidChannel.enableLights(true); + androidChannel.setLightColor(Color.RED); + androidChannel.enableVibration(true); + androidChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400}); + + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.createNotificationChannel(androidChannel); + } + getManager(); + } + public NotificationManager getManager(){ + if (manager==null){ + manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + } + return manager; + } + public void sendNotification(String message){ + Notification.Builder notification = new Notification.Builder(context) + .setAutoCancel(false) + .setWhen(System.currentTimeMillis()+ 1000) + .setContentTitle("How did you do today?") + .setContentText(message) + .setSmallIcon(R.drawable.icon); + if (Build.VERSION.SDK_INT > 25 ) { + notification.setChannelId(CHANNEL_ID); + } + + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.notify(100,notification.build()); + + } +} diff --git a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/ProfileActivity.java b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/ProfileActivity.java index ffa65fb..347978c 100644 --- a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/ProfileActivity.java +++ b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/ProfileActivity.java @@ -48,9 +48,6 @@ public boolean onCreateOptionsMenu(Menu menu) { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case Menu.FIRST: - startActivity(new Intent(ProfileActivity.this, LoginActivity.class)); - break; case R.id.nav_Profile: startActivity(new Intent(ProfileActivity.this, ProfileActivity.class)); break; @@ -68,7 +65,9 @@ public boolean onOptionsItemSelected(MenuItem item) { File file= new File(this.getFilesDir().getParent()+"/shared_prefs/"+fileName+".xml"); file.delete(); - startActivity(new Intent(ProfileActivity.this, LoginActivity.class)); + Intent intent = new Intent(this, LoginActivity.class); + intent.setFlags(intent.FLAG_ACTIVITY_NEW_TASK | intent.FLAG_ACTIVITY_CLEAR_TOP | intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent); }catch(Exception e){} break; case R.id.nav_habits: diff --git a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/PubKeyManager.java b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/PubKeyManager.java new file mode 100644 index 0000000..91d0c08 --- /dev/null +++ b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/PubKeyManager.java @@ -0,0 +1,69 @@ +package comjianzhaojohnhabit_rabbit.httpsgithub.habit_rabbit; + +import java.math.BigInteger; +import java.security.KeyStore; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPublicKey; + +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +public final class PubKeyManager implements X509TrustManager { + + private String publicKey; + + public PubKeyManager(String publicKey) { + this.publicKey = publicKey; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + if (chain == null) { + throw new IllegalArgumentException( + "checkServerTrusted: X509Certificate array is null"); + } + if (!(chain.length > 0)) { + throw new IllegalArgumentException( + "checkServerTrusted: X509Certificate is empty"); + } + + // Perform customary SSL/TLS checks + TrustManagerFactory tmf; + try { + tmf = TrustManagerFactory.getInstance("X509"); + tmf.init((KeyStore) null); + + for (TrustManager trustManager : tmf.getTrustManagers()) { + ((X509TrustManager) trustManager).checkServerTrusted( + chain, authType); + } + + } catch (Exception e) { + throw new CertificateException(e.toString()); + } + + // Hack ahead: BigInteger and toString(). We know a DER encoded Public Key starts with 0x30 + // (ASN.1 SEQUENCE and CONSTRUCTED), so there is no leading 0x00 to drop. + RSAPublicKey pubKey = (RSAPublicKey) chain[0].getPublicKey(); + String encoded = new BigInteger(1 /* positive */, pubKey.getEncoded()) + .toString(16); + + // Pin it! + final boolean expected = publicKey.equalsIgnoreCase(encoded); + // fail if expected public key is different from our public key + if (!expected) { + throw new CertificateException("Not trusted"); + } + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } +} diff --git a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/RegisterActivity.java b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/RegisterActivity.java index 318382d..05c105f 100644 --- a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/RegisterActivity.java +++ b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/RegisterActivity.java @@ -131,7 +131,8 @@ private void requestRegister(String email, String password) { final String mPassword = password; // send login request - RequestQueue queue = Volley.newRequestQueue(this); + RequestQueue queue = VolleySingleton.getInstance(this) + .getRequestQueue(this); final String url_reg = "https://habit-rabbit.000webhostapp.com/Register_encrypt.php"; StringRequest loginReq = new StringRequest(Request.Method.POST, url_reg, diff --git a/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/VolleySingleton.java b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/VolleySingleton.java new file mode 100644 index 0000000..f05206f --- /dev/null +++ b/app/src/main/java/comjianzhaojohnhabit_rabbit/httpsgithub/habit_rabbit/VolleySingleton.java @@ -0,0 +1,50 @@ +package comjianzhaojohnhabit_rabbit.httpsgithub.habit_rabbit; + +import android.content.Context; + +import com.android.volley.RequestQueue; +import com.android.volley.toolbox.HurlStack; +import com.android.volley.toolbox.Volley; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; + +public class VolleySingleton { + + private static final String PUBLIC_KEY = "30820122300d06092a864886f70d01010105000382010f003082010a0282010100d2d5578512e9eaf0a9a6a993a67c4c0c310f17c81a27e9d637269921ac53ef075f29705045303b10a266f4fb64b0ed850b45d394c8e760fade9fc8b605dba70f3c3d86cf9a5b1cbe78cf8a47fe5dce296cf765eade76fd54ac19b3a26715ef1c2e5f416be083395f7eff9dc5c05e444e4d45f62fb3ac935226f18af3ba0925a09a7a7d44913770868bbb53d1e89e2e3d7ff18a5fde56de0bb9fe78d958b4b905df181e6f0dafca62c20ead1aa6c507dff01f79d3e4d87ad04236a1ab20ca99591326b7e2406ba9c2c3f20e08a85a4a655b34d26167cfbddd1b9d40fa38cbf4016b4dcae3df588a6172fe1ed7f7fbc9a7b61d1b77c86a216ed1914252d19a0baf0203010001"; + private static VolleySingleton mInstance; + private RequestQueue mRequestQueue; + + private VolleySingleton(Context context) { + mRequestQueue = getRequestQueue(context); + } + + public static synchronized VolleySingleton getInstance(Context context) { + if (mInstance == null) { + mInstance = new VolleySingleton(context); + } + return mInstance; + } + + public RequestQueue getRequestQueue(Context context) { + if (mRequestQueue == null) { + TrustManager tm[] = {new PubKeyManager(PUBLIC_KEY)}; + SSLSocketFactory pinnedSSLSocketFactory = null; + try { + SSLContext TLSContext = SSLContext.getInstance("TLS"); + TLSContext.init(null, tm, null); + pinnedSSLSocketFactory = TLSContext.getSocketFactory(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (KeyManagementException e) { + e.printStackTrace(); + } + mRequestQueue = Volley.newRequestQueue(context.getApplicationContext(), new HurlStack(null, pinnedSSLSocketFactory)); + } + return mRequestQueue; + } +} diff --git a/app/src/main/res/drawable/ic_agenda_black_24dp.xml b/app/src/main/res/drawable/ic_agenda_black_24dp.xml new file mode 100644 index 0000000..77fd98c --- /dev/null +++ b/app/src/main/res/drawable/ic_agenda_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_details_black_24dp.xml b/app/src/main/res/drawable/ic_details_black_24dp.xml new file mode 100644 index 0000000..a30104a --- /dev/null +++ b/app/src/main/res/drawable/ic_details_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_exit_to_app_black_24dp.xml b/app/src/main/res/drawable/ic_exit_to_app_black_24dp.xml new file mode 100644 index 0000000..6f40d77 --- /dev/null +++ b/app/src/main/res/drawable/ic_exit_to_app_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_list_black_24dp.xml b/app/src/main/res/drawable/ic_list_black_24dp.xml new file mode 100644 index 0000000..4c2fb88 --- /dev/null +++ b/app/src/main/res/drawable/ic_list_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_habit_detail.xml b/app/src/main/res/layout/activity_habit_detail.xml index b9c7250..ba48aa4 100644 --- a/app/src/main/res/layout/activity_habit_detail.xml +++ b/app/src/main/res/layout/activity_habit_detail.xml @@ -44,13 +44,13 @@ android:layout_margin="@dimen/fab_margin" app:layout_anchor="@+id/habit_detail_container" app:layout_anchorGravity="top|end" - app:srcCompat="@android:drawable/ic_menu_edit"> + app:srcCompat="@android:drawable/ic_menu_save" /> + app:layout_behavior="@string/appbar_scrolling_view_behavior" /> /> diff --git a/app/src/main/res/menu/agenda_popup_menu.xml b/app/src/main/res/menu/agenda_popup_menu.xml new file mode 100644 index 0000000..c685d8d --- /dev/null +++ b/app/src/main/res/menu/agenda_popup_menu.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/navigation_menu.xml b/app/src/main/res/menu/navigation_menu.xml index 7dfc527..746383d 100644 --- a/app/src/main/res/menu/navigation_menu.xml +++ b/app/src/main/res/menu/navigation_menu.xml @@ -9,14 +9,19 @@ android:title="Friends" /> --> - - + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b2778f8..58c1c24 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -23,7 +23,7 @@ My New Habit Description: Description here. - Frequency: + Repeat: choose a frequency: Reminder: Open