diff --git a/.gitignore b/.gitignore
index 56cc642..307ee93 100644
--- a/.gitignore
+++ b/.gitignore
@@ -83,3 +83,4 @@ lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
+.idea/.name
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index a5f05cd..2370474 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -21,5 +21,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index d338dfe..5d75aeb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,5 +1,6 @@
plugins {
id 'com.android.application'
+ id 'kotlin-android'
}
android {
@@ -37,7 +38,12 @@ dependencies {
implementation 'androidx.navigation:navigation-ui:2.3.2'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ implementation 'org.naishadhparmar.zcustomcalendar:zcustomcalendar:1.0.1'
+ implementation 'com.github.Armen101:AudioRecordView:1.0.5'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 33ef7d1..2abbaff 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -9,15 +9,43 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Sentimo">
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png
new file mode 100644
index 0000000..5aa99e9
Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ
diff --git a/app/src/main/java/ca/uwaterloo/sentimo/AudioPlayerActivity.java b/app/src/main/java/ca/uwaterloo/sentimo/AudioPlayerActivity.java
new file mode 100644
index 0000000..e06a11c
--- /dev/null
+++ b/app/src/main/java/ca/uwaterloo/sentimo/AudioPlayerActivity.java
@@ -0,0 +1,215 @@
+package ca.uwaterloo.sentimo;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.media.MediaPlayer;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import java.io.File;
+import java.io.IOException;
+
+public class AudioPlayerActivity extends AppCompatActivity {
+
+ private TextView txtDescription, txtFilename, txtDate, txtSeekPos, txtSeekDur;
+ private ImageButton btnPlay, btnFor, btnRev;
+ private SeekBar seekBar;
+
+ private MediaPlayer mediaPlayer;
+ private Runnable updateSeekBar;
+ private Handler seekBarHandler = new Handler();
+
+ private static boolean isPlaying = false;
+ private File fileToPlay;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_audio_player);
+ fileToPlay = (File) getIntent().getSerializableExtra("FILE_TO_PLAY");
+
+ txtDate = findViewById(R.id.player_moddate_txt);
+ txtDescription = findViewById(R.id.player_description_txt);
+ txtFilename = findViewById(R.id.player_filename_txt);
+ txtSeekDur = findViewById(R.id.player_duration_txt);
+ txtSeekPos = findViewById(R.id.player_position_txt);
+ btnPlay = findViewById(R.id.player_play_btn);
+ btnFor = findViewById(R.id.player_forward_btn);
+ btnRev = findViewById(R.id.player_reverse_btn);
+ seekBar = findViewById(R.id.player_seekbar);
+
+ playAudio();
+
+ btnPlay.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (isPlaying)
+ pauseAudio();
+ else
+ resumeAudio();
+ }
+ });
+
+ btnFor.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!isPlaying)
+ resumeAudio();
+ int currentPos = mediaPlayer.getCurrentPosition();
+ int duration = mediaPlayer.getDuration();
+ if (currentPos + 5000 >= duration)
+ {
+ // go to end of audio file and pause.
+ txtSeekPos.setText(Utils.formatMilliSeccond(currentPos));
+ seekBar.setProgress(seekBar.getMax());
+ mediaPlayer.seekTo(duration);
+ pauseAudio();
+ } else {
+ // fast forward 5 secs.
+ currentPos = currentPos + 5000;
+ txtSeekPos.setText(Utils.formatMilliSeccond(currentPos));
+ mediaPlayer.seekTo(currentPos);
+ }
+ }
+ });
+
+ btnRev.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!isPlaying)
+ resumeAudio();
+ int currentPos = mediaPlayer.getCurrentPosition();
+ int duration = mediaPlayer.getDuration();
+ if (currentPos - 5000 < 0)
+ {
+ // go to beginning of audio file
+ txtSeekPos.setText("0:00");
+ mediaPlayer.seekTo(0);
+ } else {
+ // rewind 5 secs.
+ currentPos = currentPos - 5000;
+ txtSeekPos.setText(Utils.formatMilliSeccond(currentPos));
+ mediaPlayer.seekTo(currentPos);
+ }
+ }
+ });
+
+ seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (fromUser) {
+ mediaPlayer.seekTo(progress);
+ }
+ txtSeekPos.setText(Utils.formatMilliSeccond(mediaPlayer.getCurrentPosition()));
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ pauseAudio();
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ int progress = seekBar.getProgress();
+ mediaPlayer.seekTo(progress);
+ resumeAudio();
+ }
+ });
+
+ mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ pauseAudio();
+ }
+ });
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (isPlaying)
+ stopAudio();
+ }
+
+ private void pauseAudio() {
+ mediaPlayer.pause();
+ btnPlay.setImageDrawable(getDrawable(R.drawable.ic_play_arrow_36dp));
+ isPlaying = false;
+ seekBarHandler.removeCallbacks(updateSeekBar);
+ }
+
+ private void resumeAudio() {
+ mediaPlayer.start();
+ btnPlay.setImageDrawable(getDrawable(R.drawable.ic_baseline_pause_36dp));
+ isPlaying = true;
+ updateRunnable();
+ seekBarHandler.postDelayed(updateSeekBar, 0);
+ }
+
+ private void stopAudio() {
+ //Stops The Audio Completely.
+ isPlaying = false;
+ mediaPlayer.pause();
+ mediaPlayer.stop();
+ mediaPlayer.release();
+ seekBarHandler.removeCallbacks(updateSeekBar);
+ }
+
+ private void playAudio() {
+ mediaPlayer = new MediaPlayer();
+ try {
+ mediaPlayer.setDataSource(fileToPlay.getAbsolutePath());
+ mediaPlayer.prepare();
+ mediaPlayer.start();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ btnPlay.setImageDrawable(getDrawable(R.drawable.ic_baseline_pause_36dp));
+ txtFilename.setText(fileToPlay.getName());
+ txtDate.setText(Utils.formatDateModified(fileToPlay.lastModified()));
+ //Play the audio
+ isPlaying = true;
+ mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ pauseAudio();
+ }
+ });
+ // set seekbar max value
+ seekBar.setMax(mediaPlayer.getDuration());
+ // set audio duration
+ txtSeekDur.setText(Utils.formatMilliSeccond(mediaPlayer.getDuration()));
+
+ seekBarHandler = new Handler();
+ updateRunnable();
+ seekBarHandler.postDelayed(updateSeekBar, 0);
+ }
+
+ private void updateRunnable() {
+ updateSeekBar = new Runnable() {
+ @Override
+ public void run() {
+ seekBar.setProgress(mediaPlayer.getCurrentPosition());
+ seekBarHandler.postDelayed(this, 500);
+ }
+ };
+ }
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/ca/uwaterloo/sentimo/MainActivity.java b/app/src/main/java/ca/uwaterloo/sentimo/MainActivity.java
index da8f085..e8c429e 100644
--- a/app/src/main/java/ca/uwaterloo/sentimo/MainActivity.java
+++ b/app/src/main/java/ca/uwaterloo/sentimo/MainActivity.java
@@ -1,15 +1,19 @@
package ca.uwaterloo.sentimo;
+import android.content.Intent;
import android.os.Bundle;
-
-import com.google.android.material.bottomnavigation.BottomNavigationView;
+import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
+import com.google.android.material.bottomnavigation.BottomNavigationView;
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+
public class MainActivity extends AppCompatActivity {
@Override
@@ -20,11 +24,21 @@ protected void onCreate(Bundle savedInstanceState) {
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
- R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications)
+ R.id.navigation_recordingList, R.id.navigation_dashboard, R.id.navigation_calendar)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
- NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_activity_main);
+ setSupportActionBar(toolbar);
NavigationUI.setupWithNavController(navView, navController);
+ // Not using toolbar to display titles/headings anymore
+ // NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
+ FloatingActionButton fab = findViewById(R.id.fab_record);
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(new Intent(MainActivity.this, RecordActivity.class));
+ }
+ });
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ca/uwaterloo/sentimo/RecordActivity.java b/app/src/main/java/ca/uwaterloo/sentimo/RecordActivity.java
new file mode 100644
index 0000000..7763b0f
--- /dev/null
+++ b/app/src/main/java/ca/uwaterloo/sentimo/RecordActivity.java
@@ -0,0 +1,307 @@
+package ca.uwaterloo.sentimo;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.Application;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.media.MediaRecorder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.Chronometer;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+import com.visualizer.amplitude.AudioRecordView;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class RecordActivity extends AppCompatActivity implements View.OnClickListener {
+
+ private final static int bitDepth = 16;
+ private final static int sampleRate = 44100;
+ private final static int bitRate = sampleRate * bitDepth;
+
+ // Requesting permission to RECORD_AUDIO
+ private boolean permissionToRecordAccepted = false;
+ private static final String LOG_TAG = "AudioRecordTest";
+ private static final int REQUEST_RECORD_AUDIO_PERMISSION = 200;
+ private String [] permissions = {Manifest.permission.RECORD_AUDIO};
+ private static String recordPath = null;
+ private static String oldFileName = null;
+ private static long startTime = 0L, time_MS = 0L;
+
+
+ private ImageButton btnRecord;
+ private TextView txtRecord;
+ private Chronometer txtTimer_hm;
+ private TextView txtTimer_ms;
+ private AudioRecordView arvVisualizer;
+ private MediaRecorder mRecorder = null;
+ private Handler customHandler = new Handler();
+ public static final int DELAY_BTN_AMIN_MILLIS = 500;
+
+ // animation drawables
+ private static Drawable anim1;
+ private static Drawable anim2;
+ private static Drawable anim3;
+
+ private boolean isRecording = false;
+
+ private Runnable updateTimerThread = new Runnable() {
+ @Override
+ public void run() {
+ time_MS = startTime + SystemClock.uptimeMillis();
+ int milliseconds = (int)(time_MS % 1000);
+ txtTimer_ms.setText(String.format("%03d", milliseconds));
+ customHandler.postDelayed(updateTimerThread, 10);
+ }
+ };
+
+ private Runnable updateVisualizerThread = new Runnable() {
+ @Override
+ public void run() {
+ int currentMaxAmplitude = mRecorder.getMaxAmplitude();
+ arvVisualizer.update(currentMaxAmplitude);
+ customHandler.postDelayed(updateVisualizerThread, 25);
+ }
+ };
+
+ private Runnable updateAnimationThread = new Runnable() {
+ @Override
+ public void run() {
+ if (btnRecord.getDrawable() == anim1) {
+ btnRecord.setImageDrawable(anim2);
+ }
+ else if (btnRecord.getDrawable() == anim2) {
+ btnRecord.setImageDrawable(anim3);
+ } else {
+ btnRecord.setImageDrawable(anim1);
+ }
+ customHandler.postDelayed(updateAnimationThread, DELAY_BTN_AMIN_MILLIS);
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_record);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_activity_record);
+ setSupportActionBar(toolbar);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setDisplayShowHomeEnabled(true);
+
+ btnRecord = findViewById(R.id.btn_record);
+ btnRecord.setImageDrawable(getDrawable(R.drawable.record_icon_off));
+ btnRecord.setOnClickListener(this);
+
+ txtRecord = findViewById(R.id.txt_record);
+
+ txtTimer_hm = findViewById(R.id.txt_timer_hm);
+ txtTimer_ms = findViewById(R.id.txt_timer_ms);
+
+ arvVisualizer = findViewById(R.id.audioRecordView);
+
+ anim1 = getDrawable(R.drawable.record_icon_on_1);
+ anim2 = getDrawable(R.drawable.record_icon_on_2);
+ anim3 = getDrawable(R.drawable.record_icon_on_3);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) {
+ return super.onCreateView(name, context, attrs);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if(isRecording)
+ {
+ isRecording = false;
+ stopRecording(true);
+ btnRecord.setImageDrawable(getDrawable(R.drawable.record_icon_off));
+ txtRecord.setText(R.string.stop_record);
+ }
+ else
+ {
+ if (checkPermissions())
+ {
+ isRecording = true;
+ startRecording();
+ btnRecord.setImageDrawable(getDrawable(R.drawable.record_icon_on_1));
+ txtRecord.setText(R.string.start_record);
+ }
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ startActivity(new Intent(RecordActivity.this, MainActivity.class));
+ return true;
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (mRecorder != null) {
+ stopRecording(false);
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ super.onBackPressed();
+ if (mRecorder != null) {
+ stopRecording(false);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ switch (requestCode) {
+ case REQUEST_RECORD_AUDIO_PERMISSION:
+ permissionToRecordAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
+ break;
+ }
+ if (!permissionToRecordAccepted) finish();
+ }
+
+ private boolean checkPermissions() {
+ if ((ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED))
+ return true;
+ ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION);
+ return (ContextCompat.checkSelfPermission(getApplicationContext(),
+ Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED);
+ }
+
+ private void startRecording() {
+ recordPath = getExternalFilesDir("/").getAbsolutePath();
+ SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_hh_mm_ss");
+ oldFileName = "Recording_" + formatter.format(new Date()) + ".mp3";
+ recordPath += "/" + oldFileName;
+
+ mRecorder = new MediaRecorder();
+ mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_RECOGNITION);
+ mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
+ mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+ mRecorder.setAudioEncodingBitRate(bitRate);
+ mRecorder.setAudioSamplingRate(sampleRate);
+ mRecorder.setOutputFile(recordPath);
+
+ try {
+ mRecorder.prepare();
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "prepare() failed");
+ }
+
+ mRecorder.start();
+ txtTimer_hm.setBase(SystemClock.elapsedRealtime());
+ txtTimer_hm.start();
+ startTime = System.currentTimeMillis();
+ arvVisualizer.clearAnimation();
+ customHandler.postDelayed(updateTimerThread, 0);
+ customHandler.postDelayed(updateVisualizerThread, 0);
+ customHandler.postDelayed(updateAnimationThread, DELAY_BTN_AMIN_MILLIS);
+ }
+
+ private void stopRecording(boolean save) {
+ customHandler.removeCallbacks(updateTimerThread);
+ customHandler.removeCallbacks(updateVisualizerThread);
+ customHandler.removeCallbacks(updateAnimationThread);
+ mRecorder.stop();
+ txtTimer_hm.stop();
+ mRecorder.release();
+ if (save)
+ saveRecording();
+ else
+ {
+ //Save file as default if user leaves app before saving.
+ Toast.makeText(RecordActivity.this, "Recording saved as " + oldFileName, Toast.LENGTH_LONG).show();
+ }
+ mRecorder = null;
+ }
+
+ private void saveRecording() {
+ LayoutInflater li = LayoutInflater.from(this);
+ View promptsView = li.inflate(R.layout.dialogue_save, null);
+
+ AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
+
+ // set prompts.xml to alertdialog builder
+ alertDialogBuilder.setView(promptsView);
+
+ final EditText userInput = (EditText) promptsView.findViewById(R.id.editTextDialogUserInput);
+ userInput.setText(oldFileName);
+ userInput.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ userInput.selectAll();
+ }
+ });
+ Button cancel = promptsView.findViewById(R.id.save_cancel);
+ Button ok = promptsView.findViewById(R.id.save_ok);
+ Activity mActivity = this;
+
+ // set dialog message
+ alertDialogBuilder.setCancelable(false);
+
+ // create alert dialog
+ final AlertDialog alertDialog = alertDialogBuilder.create();
+ cancel.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ File oldFile = new File(getExternalFilesDir("/").getAbsolutePath(), oldFileName);
+ oldFile.delete();
+ Toast.makeText(RecordActivity.this, "Recording discarded", Toast.LENGTH_LONG).show();
+ alertDialog.dismiss();
+ }
+ });
+ ok.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String newFileName = userInput.getText().toString();
+ if (!newFileName.contains(".mp3"))
+ newFileName += ".mp3";
+ if (newFileName != null && newFileName.trim().length() > 0) {
+ File newFile = new File(getExternalFilesDir("/").getAbsolutePath(), newFileName);
+ File oldFile = new File(getExternalFilesDir("/").getAbsolutePath(), oldFileName);
+ oldFile.renameTo(newFile);
+ Toast.makeText(RecordActivity.this, "Recording Saved", Toast.LENGTH_LONG).show();
+ alertDialog.dismiss();
+
+ // open audio player activity after save
+ Intent intent = new Intent(mActivity, AudioPlayerActivity.class);
+ intent.putExtra("FILE_TO_PLAY", newFile);
+ startActivity(intent);
+ }
+ }
+ });
+ // show it
+ alertDialog.show();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ca/uwaterloo/sentimo/SplashScreenActivity.java b/app/src/main/java/ca/uwaterloo/sentimo/SplashScreenActivity.java
new file mode 100644
index 0000000..914fc17
--- /dev/null
+++ b/app/src/main/java/ca/uwaterloo/sentimo/SplashScreenActivity.java
@@ -0,0 +1,16 @@
+package ca.uwaterloo.sentimo;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+public class SplashScreenActivity extends AppCompatActivity {
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ startActivity(new Intent(SplashScreenActivity.this, MainActivity.class));
+ finish();
+ }
+}
diff --git a/app/src/main/java/ca/uwaterloo/sentimo/Utils.java b/app/src/main/java/ca/uwaterloo/sentimo/Utils.java
new file mode 100644
index 0000000..f030bf2
--- /dev/null
+++ b/app/src/main/java/ca/uwaterloo/sentimo/Utils.java
@@ -0,0 +1,87 @@
+package ca.uwaterloo.sentimo;
+
+import android.media.MediaMetadataRetriever;
+
+import java.io.File;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+public class Utils {
+
+ /**
+ * Function to convert milliseconds time to
+ * Timer Format
+ * Hours:Minutes:Seconds
+ */
+ public static String formatMilliSeccond(long milliseconds) {
+ String finalTimerString = "";
+ String secondsString = "";
+
+ // Convert total duration into time
+ int hours = (int) (milliseconds / (1000 * 60 * 60));
+ int minutes = (int) (milliseconds % (1000 * 60 * 60)) / (1000 * 60);
+ int seconds = (int) ((milliseconds % (1000 * 60 * 60)) % (1000 * 60) / 1000);
+
+ // Add hours if there
+ if (hours > 0) {
+ finalTimerString = hours + ":";
+ }
+
+ // Prepending 0 to seconds if it is one digit
+ if (seconds < 10) {
+ secondsString = "0" + seconds;
+ } else {
+ secondsString = "" + seconds;
+ }
+
+ finalTimerString = finalTimerString + minutes + ":" + secondsString;
+
+ // return String.format("%02d Min, %02d Sec",
+ // TimeUnit.MILLISECONDS.toMinutes(milliseconds),
+ // TimeUnit.MILLISECONDS.toSeconds(milliseconds) -
+ // TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliseconds)));
+
+ // return timer string
+ return finalTimerString;
+ }
+
+ public static String formatDateModified(long lastModified) {
+ Date lastModDate = new Date(lastModified);
+ String[] date = lastModDate.toString().split(" ");
+ return date[2] + " " + date[1] + " " + date[5];
+ }
+
+ public static String getTimeAgo(long lastModified) {
+ Date now = new Date();
+
+ long seconds = TimeUnit.MILLISECONDS.toSeconds(now.getTime() - lastModified);
+ long minutes = TimeUnit.MILLISECONDS.toMinutes(now.getTime() - lastModified);
+ long hours = TimeUnit.MILLISECONDS.toHours(now.getTime() - lastModified);
+ long days = TimeUnit.MILLISECONDS.toDays(now.getTime() - lastModified);
+
+ if(seconds < 60){
+ return "just now";
+ } else if (minutes == 1) {
+ return "a minute ago";
+ } else if (minutes > 1 && minutes < 60) {
+ return minutes + " minutes ago";
+ } else if (hours == 1) {
+ return "an hour ago";
+ } else if (hours > 1 && hours < 24) {
+ return hours + " hours ago";
+ } else if (days == 1) {
+ return "a day ago";
+ } else if (days <= 5) {
+ return days + " days ago";
+ } else {
+ return formatDateModified(lastModified);
+ }
+ }
+
+ public static String getDuration(File file) {
+ MediaMetadataRetriever mmr = new MediaMetadataRetriever();
+ mmr.setDataSource(file.getAbsolutePath());
+ String durationStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
+ return Utils.formatMilliSeccond(Long.parseLong(durationStr));
+ }
+}
diff --git a/app/src/main/java/ca/uwaterloo/sentimo/onboarding/LoadingFragment.kt b/app/src/main/java/ca/uwaterloo/sentimo/onboarding/LoadingFragment.kt
new file mode 100644
index 0000000..374574f
--- /dev/null
+++ b/app/src/main/java/ca/uwaterloo/sentimo/onboarding/LoadingFragment.kt
@@ -0,0 +1,21 @@
+package ca.uwaterloo.sentimo.onboarding
+
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import ca.uwaterloo.sentimo.R
+
+class LoadingFragment : Fragment() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.fragment_welcome, container, false)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ca/uwaterloo/sentimo/onboarding/Onboarding1Fragment.kt b/app/src/main/java/ca/uwaterloo/sentimo/onboarding/Onboarding1Fragment.kt
new file mode 100644
index 0000000..7c2e69f
--- /dev/null
+++ b/app/src/main/java/ca/uwaterloo/sentimo/onboarding/Onboarding1Fragment.kt
@@ -0,0 +1,21 @@
+package ca.uwaterloo.sentimo.onboarding
+
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import ca.uwaterloo.sentimo.R
+
+class Onboarding1Fragment : Fragment() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.fragment_onboarding1, container, false)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ca/uwaterloo/sentimo/onboarding/Onboarding2Fragment.kt b/app/src/main/java/ca/uwaterloo/sentimo/onboarding/Onboarding2Fragment.kt
new file mode 100644
index 0000000..5bcd9c1
--- /dev/null
+++ b/app/src/main/java/ca/uwaterloo/sentimo/onboarding/Onboarding2Fragment.kt
@@ -0,0 +1,21 @@
+package ca.uwaterloo.sentimo.onboarding
+
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import ca.uwaterloo.sentimo.R
+
+class Onboarding2Fragment : Fragment() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.fragment_onboarding2, container, false)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ca/uwaterloo/sentimo/onboarding/Onboarding3Fragment.kt b/app/src/main/java/ca/uwaterloo/sentimo/onboarding/Onboarding3Fragment.kt
new file mode 100644
index 0000000..f7bd5f9
--- /dev/null
+++ b/app/src/main/java/ca/uwaterloo/sentimo/onboarding/Onboarding3Fragment.kt
@@ -0,0 +1,21 @@
+package ca.uwaterloo.sentimo.onboarding
+
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import ca.uwaterloo.sentimo.R
+
+class Onboarding3Fragment : Fragment() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.fragment_onboarding3, container, false)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ca/uwaterloo/sentimo/onboarding/WelcomeFragment.kt b/app/src/main/java/ca/uwaterloo/sentimo/onboarding/WelcomeFragment.kt
new file mode 100644
index 0000000..09054a5
--- /dev/null
+++ b/app/src/main/java/ca/uwaterloo/sentimo/onboarding/WelcomeFragment.kt
@@ -0,0 +1,21 @@
+package ca.uwaterloo.sentimo.onboarding
+
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import ca.uwaterloo.sentimo.R
+
+class WelcomeFragment : Fragment() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.fragment_welcome, container, false)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ca/uwaterloo/sentimo/ui/calendar/CalendarFragment.java b/app/src/main/java/ca/uwaterloo/sentimo/ui/calendar/CalendarFragment.java
new file mode 100644
index 0000000..888595e
--- /dev/null
+++ b/app/src/main/java/ca/uwaterloo/sentimo/ui/calendar/CalendarFragment.java
@@ -0,0 +1,130 @@
+package ca.uwaterloo.sentimo.ui.calendar;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import org.naishadhparmar.zcustomcalendar.CustomCalendar;
+import org.naishadhparmar.zcustomcalendar.OnDateSelectedListener;
+import org.naishadhparmar.zcustomcalendar.OnNavigationButtonClickedListener;
+import org.naishadhparmar.zcustomcalendar.Property;
+
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
+
+import ca.uwaterloo.sentimo.R;
+
+public class CalendarFragment extends Fragment implements OnNavigationButtonClickedListener {
+
+ CustomCalendar customCalendar;
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ //Assign variable
+ customCalendar = view.findViewById(R.id.custom_calendar);
+
+ //Initialize description hash map
+ HashMap