diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..61a9130 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..6513874 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..a5f05cd --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..d5d35ec --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..336d2a5 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,58 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdkVersion 30 + buildToolsVersion "29.0.3" + + defaultConfig { + applicationId "com.example.attendancemgr" + minSdkVersion 26 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + + implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'com.google.android.material:material:1.4.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.0' + implementation 'androidx.room:room-runtime:2.3.0' + implementation 'androidx.annotation:annotation:1.1.0' + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' + implementation 'androidx.navigation:navigation-fragment:2.3.5' + implementation 'androidx.navigation:navigation-ui:2.3.5' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'androidx.recyclerview:recyclerview:1.1.0' + implementation 'androidx.work:work-rxjava3:2.5.0-rc01' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + annotationProcessor 'androidx.room:room-compiler:2.3.0' + + // Room components + implementation "androidx.room:room-runtime:2.3.0" + annotationProcessor "androidx.room:room-compiler:2.3.0" + + // Lifecycle components + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.3.1" + +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/attendancemgr/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/example/attendancemgr/ExampleInstrumentedTest.java new file mode 100644 index 0000000..b411db0 --- /dev/null +++ b/app/src/androidTest/java/com/example/attendancemgr/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.example.attendancemgr; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.example.attendancemgr", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b7796f9 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/MainActivity.java b/app/src/main/java/com/example/attendancemgr/MainActivity.java new file mode 100644 index 0000000..4705e9a --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/MainActivity.java @@ -0,0 +1,722 @@ +package com.example.attendancemgr; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Color; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.util.Base64; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.ProgressBar; +import android.widget.Spinner; +import android.widget.Toast; + +import androidx.activity.result.ActivityResult; +import androidx.activity.result.ActivityResultCallback; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.RequiresApi; +import androidx.appcompat.app.AppCompatActivity; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.ViewModelProvider; + +import com.example.attendancemgr.ui.CourseListAdapter; +import com.example.attendancemgr.database.AgentCourse; +import com.example.attendancemgr.database.AttendanceCourse; +import com.example.attendancemgr.database.CourseViewModel; +import com.example.attendancemgr.database.EnrolCourse; +import com.google.android.material.snackbar.Snackbar; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; + +public class MainActivity extends AppCompatActivity { + private final String[] Courses = {"Select a course","PHY101", "PHY102"}; + private String chosenCourse, jsonAttendance, jsonEnrol; + private final int DOWNLOADING = 1; + private final int OPEN_ATTENDANCE_CODE = 2; + private final int NOT_DOWNLOADING = 0; + private LinkedList lecturerFP; + private final String ATTENDANCE_RECORD = "Attendance list"; + private final String COURSE_CODE = "Course code"; + private final String ATT_COURSE = "Course"; + private final String ATT_TUTOR_FP = "Authenticate tutor"; + private final String ATD_TUTOR_FP = "Tutor FPs"; + private static final String STUDENT_MATCH = "Authenticate student"; + private final String ATT_STDs_FPs = "Students FPs"; + public final String ATT_STDs_NOs = "Students NOs"; + public final String ATT_STDs_IDs = "Attendance IDs"; + public final String TUTOR_VALID = "is tutor valid?"; + private LinkedList AdmNos; + private boolean FPStatus, pb_is_Visible, isSubmitted, isSubmitting; + private int downloadStatus = NOT_DOWNLOADING; + private ProgressBar pb; + private LinkedList studentsFP; + private LinkedList IDs; + Button scanButton, regButton, submitRecordButton; + private CourseListAdapter listAdapter; + private CourseViewModel mCourseViewModel; + private List mAgentCourses; + private List attendanceCourses; + private List enrolCourses; + private List courseString; + private final int TUTOR_OK = 234; + private final int ATT_OK = 432; + private int idOPen = -1, tempID; + private AgentCourse openedCourse; + public static final String AddressFile = "bt_address_file"; + + private ActivityResultCallback attCallback = result -> { + + //Pattern pat; + //Matcher mat; + if(result.getResultCode() == ATT_OK){ if(result.getData().hasExtra(ATT_STDs_NOs)) { + for (String data : result.getData().getExtras().getStringArrayList(ATT_STDs_NOs)) { + if (data.length() > 20) { + // ga wanda ya fara daukar attendance na farko kafin enrollment + /* mCourseViewModel.insert(new AttendanceCourse(chosenCourse, + null, + Base64.decode(data, Base64.DEFAULT), + AttendanceCourse.Sub_Status.unsubmitted, + AttendanceCourse.Cap_Status.captured));*/ + } else { + /* pat = Pattern.compile(data); + mat = pat.matcher("&"); + if (mat.find()) {*/ + String[] admNo_id = data.split("&"); + // for the enrolled + /* mCourseViewModel.update(new AttendanceCourse(Integer.parseInt(admNo_id[0]), + chosenCourse, + admNo_id[1], + null, + AttendanceCourse.Sub_Status.unsubmitted, + AttendanceCourse.Cap_Status.captured)); +*/ + /*}else { + mCourseViewModel.insert(new AttendanceCourse(chosenCourse, data, null, AttendanceCourse.Sub_Status.unsubmitted, AttendanceCourse.Cap_Status.captured)); + }*/ + } + } + if (openedCourse != null ){ + openedCourse.setAtt_status(AgentCourse.Att_Status.closed); + mCourseViewModel.update(openedCourse); + } + + if (result.getData().hasExtra("BTAddress")) saveAddress((String) result.getData().getExtras().get("BTAddress")); + }} + }; + + ActivityResultCallback tutorScanCallback = result -> { + if (result.getResultCode() == TUTOR_OK){ + for (AgentCourse course : mAgentCourses) { + if (course.getCourse().equals(chosenCourse)) { + course.setAtt_status(AgentCourse.Att_Status.open); + openedCourse = course; + } + break; + } + getAtt_EnrolData(null); + } + if (result.getData() != null && result.getData().hasExtra("BTAddress")) saveAddress((String) result.getData().getExtras().get("BTAddress")); + }; + public Spinner courseSpinner; + ActivityResultLauncher tutorScanLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), tutorScanCallback); + ActivityResultLauncher attLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), attCallback); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + //mCourseViewModel = new CourseViewModel(getApplication()); + mCourseViewModel = new ViewModelProvider(this).get(CourseViewModel.class); + courseSpinner = findViewById(R.id.selectCourseSpinner); + LiveData> agentLiveData = mCourseViewModel.getAllAgentCourses(); + LiveData> attLiveData = mCourseViewModel.getAllAttendanceCourses(); + LiveData> enrolLiveData = mCourseViewModel.getAllEnrolCourses(); + /*ActionBar bar = this.getActionBar(); + Toast.makeText(this, bar.getTitle(), Toast.LENGTH_SHORT).show();*/ + courseString = new LinkedList<>(); + + if (openedCourse != null) mCourseViewModel.update(openedCourse); + + ArrayAdapter mAdapter = new ArrayAdapter<>(MainActivity.this, R.layout.spinner_text); + mAdapter.setNotifyOnChange(true); + + agentLiveData.observe(MainActivity.this, agentCourses -> { + if (agentCourses != null){ + mAgentCourses = agentCourses; + courseString.clear(); + for (AgentCourse course: agentCourses) { + courseString.add(course.getCourse()); + } + mAdapter.clear(); + mAdapter.insert("Please select a course", 0); + mAdapter.addAll(courseString); + getAllRecords(); + } + }); + + Thread t = new Thread(() -> { + if(mCourseViewModel.getAnyCourse().length<1) initAgentCourses(); + }); + t.start(); + + courseSpinner.setAdapter(mAdapter); + + courseSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + chosenCourse = mAdapter.getItem(position); + if (!chosenCourse.equals(mAdapter.getItem(0))) { + if(idOPen != position && downloadStatus == NOT_DOWNLOADING){ + if (chosenCourse!=null && isWifiCxd()) { + tempID = position; + getFP(chosenCourse); + } else { + Toast.makeText(MainActivity.this, "No Internet connection.", Toast.LENGTH_SHORT).show(); + } + } else { + getAtt_EnrolData(position); + } + + } else { + Toast.makeText(MainActivity.this, "Please select a course.", Toast.LENGTH_LONG).show(); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + chosenCourse = Courses[0]; + } + }); + + enrolLiveData.observe(this, enrolCourses1 ->{ + enrolCourses = enrolCourses1; + updateSubmitButton(); + }); + + attLiveData.observe(this, attendanceCourses1 -> { + if (attendanceCourses1!=null){ + attendanceCourses = attendanceCourses1; + updateSubmitButton(); + } + }); + + pb = findViewById(R.id.progressBarSelectCourse); + pb.setVisibility(View.INVISIBLE); + FPStatus = false; + scanButton = findViewById(R.id.scanFPButton); + regButton = findViewById(R.id.registerTutorButton); + scanButton.setVisibility(View.INVISIBLE); + submitRecordButton = findViewById(R.id.submitRecordButton); + regButton.setBackgroundColor(Color.GREEN); + // + lecturerFP = new LinkedList<>(); + AdmNos = new LinkedList<>(); + studentsFP = new LinkedList<>(); + IDs = new LinkedList<>(); + + scanButton.setOnClickListener(view -> { + if (!pb_is_Visible && getLecturerFP() != null){ + scanTutorFP(); + } + }); + + regButton.setOnClickListener(view ->{ + if (!pb_is_Visible) { + Intent intent = new Intent(MainActivity.this, RegisterTutor.class); + startActivity(intent); + } + }); + + // Update the cached copy of the words in the adapter. + //adapter.notifyDataSetChanged(); + + submitRecordButton.setOnClickListener(View -> { + if (!isSubmitted && !pb_is_Visible && !isSubmitting){ + submitRecord(); + } + }); + + } + + private String[] convertToArray(List agentCourses) { + ArrayList courses = new ArrayList<>(); + for (AgentCourse course : agentCourses) { + courses.add(course.getCourse()); + } + return (String[]) courses.toArray(); + } + + private void scanTutorFP() { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setPackage("com.fgtit.reader"); + if (!getAddress().equals("No_Address")) + intent.putExtra("bt_Address", getAddress()); + intent.putExtra(ATT_COURSE, chosenCourse); + intent.putExtra(ATT_TUTOR_FP, getLecturerFP().toArray()); + tutorScanLauncher.launch(intent); + + } + + public LinkedList getLecturerFP() { + SharedPreferences preferences = this.getSharedPreferences("Tutors", MODE_PRIVATE); + String defaultData = "No_FP"; + String fpStrings = preferences.getString("tutorsFP", defaultData); + if (!fpStrings.equals(defaultData)) { + String[] fpStringsArray = fpStrings.split(" "); + for (String fp : fpStringsArray) { + lecturerFP.add(Base64.decode(fp, Base64.DEFAULT)); + } + } + return lecturerFP; + } + private void initAgentCourses(){ + final String [] courses = {"PHY101", "PHY401", "PHY102", + "PHY302"}; + for (int i = 0 ; i <= courses.length - 1 ; i++) { + AgentCourse course = new AgentCourse("Sciences","Physics", courses[i], AgentCourse.Att_Status.closed, AgentCourse.Sub_Status.unsubmitted); + mCourseViewModel.insert(course); + + } + } + void getAllRecords(){ + attendanceCourses = mCourseViewModel.getAllAttendanceCourses().getValue(); + enrolCourses = mCourseViewModel.getAllEnrolCourses().getValue(); + } + void updateSubmitButton(){ + if (isRecordSubmitted()){ + submitRecordButton.setBackgroundColor(Color.GREEN); + isSubmitted = true; + } else { + submitRecordButton.setBackgroundColor(Color.RED); + isSubmitted = false; + } + } + private boolean isRecordSubmitted() { + int count = 0; + if (attendanceCourses != null) { + for (AttendanceCourse course : attendanceCourses) { + if (course.getSub_Status() == AttendanceCourse.Sub_Status.unsubmitted && + course.getCap_status().equals(AttendanceCourse.Cap_Status.captured)) count++; + } + } + if (enrolCourses != null) { + for (EnrolCourse course : enrolCourses) { + if (course.getSub_status() == EnrolCourse.Sub_Status.unsubmitted) count++; + } + } + + return count<1; + } + + private void getFP(String chosenCourse) { + class DownloadFP extends AsyncTask{ + @Override + protected void onPreExecute() { + super.onPreExecute(); + pb.setVisibility(View.VISIBLE); + downloadStatus = DOWNLOADING; + pb_is_Visible = true; + } + + @Override + protected void onPostExecute(String data) { + super.onPostExecute(data); + if (data.equals("timeout")){ + Toast.makeText(MainActivity.this, "Connection timeout!", Toast.LENGTH_SHORT).show(); + } else if (!data.isEmpty()){ + try { + storeAttendanceData(data); + idOPen = tempID; + } catch (JSONException e) { + e.printStackTrace(); + } + scanButton.setVisibility(View.VISIBLE); + } + else { + Toast.makeText(MainActivity.this, "Lecturer or course details incomplete!", Toast.LENGTH_SHORT).show(); + + } + pb.setVisibility(View.INVISIBLE); + pb_is_Visible = false; + downloadStatus = NOT_DOWNLOADING; + + } + + @Override + protected String doInBackground(Void... voids) { + + String login_url = "http://www.gstcbunza2012.org.ng/fas/fetch_data.php"; + try { + java.net.URL url = new URL(login_url); + try { + HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); + httpURLConnection.setRequestMethod("POST"); + httpURLConnection.setReadTimeout(15000); + httpURLConnection.setConnectTimeout(15000); + httpURLConnection.setDoInput(true); + httpURLConnection.setDoOutput(true); + + OutputStream outputStream = httpURLConnection.getOutputStream(); + BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8")); + String post_data = URLEncoder.encode("course", "UTF-8") + "=" + URLEncoder.encode(chosenCourse, "UTF-8"); + bufferedWriter.write(post_data); + bufferedWriter.flush(); + bufferedWriter.close(); + + InputStream inputStream = httpURLConnection.getInputStream(); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "iso-8859-1")); + StringBuilder result = new StringBuilder(); + String line; + new Timer().schedule(new TimerTask() { + @RequiresApi(api = Build.VERSION_CODES.O) + @Override + public void run() { + if (inputStream == null){ + onPostExecute("timeout"); + httpURLConnection.disconnect(); + + } + } + + }, 15000); + while ((line = bufferedReader.readLine()) != null) { + result.append(line); + } + String rslt = result.toString().trim(); + if (!rslt.isEmpty() & rslt.length()>20) { + return rslt; + + } + bufferedReader.close(); + inputStream.close(); + httpURLConnection.disconnect(); + } catch (Exception e) { + e.printStackTrace(); + } + + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + } + DownloadFP downloadFP = new DownloadFP(); + if (isWifiCxd()) { + downloadFP.execute(); + } else { + Snackbar.make(MainActivity.this, submitRecordButton, "No network connection", Snackbar.LENGTH_LONG); + } + } + + private void storeAttendanceData(String data) throws JSONException { + JSONArray jsonArray = new JSONArray(data); + JSONObject tutorFP; + + StringBuilder builder = new StringBuilder(); + String tutorFPStrings; + + // JSONObject object = jsonArray.getJSONObject(i); + JSONArray tutorsArray = jsonArray.getJSONArray(0); + + for (int j=0; j0) return temp; + return new String[0][0]; + } + private boolean prepareAttData(){ + + int count2 = 0; + for (AttendanceCourse course : attendanceCourses) { + if (course.getCap_status().equals(AttendanceCourse.Cap_Status.uncaptured)) { + studentsFP.add(course.getFP()); + AdmNos.add(course.getAdmNo()); + IDs.add(course.getId()); + + } + } + SharedPreferences preferences = this.getSharedPreferences("Tutors", MODE_PRIVATE); + String defaultFP = ""; + String tutorsFP = preferences.getString("tutorsFP", defaultFP); + String[] tutorsFPArray = tutorsFP.split(" "); + for (String fp : tutorsFPArray) { + lecturerFP.add(Base64.decode(fp, Base64.DEFAULT)); + count2++; + } + + return count2>0; + } + private void getAtt_EnrolData(Integer position) { + final AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); + builder.setMessage("What would you like to do?"); + //builder.setTitle("Register Entry or Add Fingerprint"); + + builder.setPositiveButton("Enrol Student", (dialog, which) -> { + if(isSubmitting){ + Toast.makeText(this, "Please wait, system is busy.", Toast.LENGTH_SHORT).show(); + } else { + String[][] un_enrolled = getAllUn_enrolledAttendees(); + Intent intent = new Intent(MainActivity.this, enrolActivity.class); + if (un_enrolled.length!=0) intent.putExtra("Un_enrolled", un_enrolled); + intent.putExtra("chosenCourse", chosenCourse); + startActivity(intent); + } + }); + + builder.setNegativeButton("Take attendance", (dialog, which) -> { + if (position==null || mAgentCourses.get(position).getAtt_status().equals(AgentCourse.Att_Status.open)) { + if (prepareAttData()) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setPackage("com.fgtit.reader"); + if (!getAddress().equals("No_Address")) + intent.putExtra("bt_Address", getAddress()); + + intent.putExtra(ATT_COURSE, chosenCourse); + intent.putExtra(ATD_TUTOR_FP, lecturerFP.toArray()); + if (studentsFP != null) intent.putExtra(ATT_STDs_FPs, studentsFP.toArray()); + if (AdmNos != null) intent.putExtra(ATT_STDs_NOs, AdmNos.toArray()); + if (IDs != null) intent.putExtra(ATT_STDs_IDs, IDs.toArray()); + intent.putExtra(STUDENT_MATCH, true); + + attLauncher.launch(intent); + dialog.cancel(); + } else { + Toast.makeText(this, "An error has occured!", Toast.LENGTH_LONG).show(); + } + } else { + Toast.makeText(this, "Attendance is closed for the course.", Toast.LENGTH_SHORT).show(); + } + }); + AlertDialog dialog = builder.create(); + dialog.show(); + } + private String getAddress(){ + SharedPreferences preferences = this.getSharedPreferences(AddressFile, MODE_PRIVATE); + String defaultAddress = "No_Address"; + return preferences.getString("Address", defaultAddress); + } + + @RequiresApi(api = Build.VERSION_CODES.R) + void updateAgentCourseSub() throws JSONException { + Set att_crs = new HashSet<>(attendanceCourses); + int attCount = 0; + for (AgentCourse agCourse: mAgentCourses) { + for (AttendanceCourse course:att_crs) { + if (course.getCourse().equals(agCourse.getCourse()) && course.getCap_status().equals(AttendanceCourse.Cap_Status.captured)) { + agCourse.setSub_status(AgentCourse.Sub_Status.submitted); + mCourseViewModel.update(agCourse); + att_crs.remove(course); + attCount++; + break; + } + } + } + Set enrol_crs = new HashSet<>(); + for (EnrolCourse crs : enrolCourses) { + JSONArray crs_arr = new JSONArray(crs.getCourse()); + for (int i=0; i0) updateSubmitButton(); + } + private JSONArray prepareRecordJsonArray() throws JSONException { + JSONArray attArray = new JSONArray(); + for (AttendanceCourse course:attendanceCourses) { + if (attendanceCourses.size() == 0){ + break; + }else if(course.getCap_status().equals(AttendanceCourse.Cap_Status.captured)) { + attArray.put(new JSONObject().put("att_course", course.getCourse()).put("AdmNo", course.getAdmNo())); + } + } + for (EnrolCourse course:enrolCourses) { + if (enrolCourses.size() == 0){ + break; + }else{ + attArray.put(new JSONObject().put("enrol_course", new JSONArray(course.getCourse())) + .put("AdmNo", course.getAdmNo()) + .put("fp", Base64.encodeToString(course.getFP(), Base64.DEFAULT)) + .put("IDImg", Base64.encodeToString(course.getIDImg(), Base64.DEFAULT)) + ); + } + + } + + return attArray; + } + void submitRecord(){ + class SubmitRecordAsynTask extends AsyncTask{ + @Override + protected void onPreExecute() { + super.onPreExecute(); + pb.setVisibility(View.VISIBLE); + isSubmitting = true; + } + + @RequiresApi(api = Build.VERSION_CODES.R) + @Override + protected void onPostExecute(Boolean o) { + super.onPostExecute(o); + if (o) { + try { + updateAgentCourseSub(); + } catch (JSONException e) { + e.printStackTrace(); + } + for (AttendanceCourse course : attendanceCourses) { + if (course.getCap_status().equals(AttendanceCourse.Cap_Status.captured)) { + course.setSub_Status(AttendanceCourse.Sub_Status.submitted); + mCourseViewModel.update(course); + } + } + for (EnrolCourse course : enrolCourses) { + course.setSub_status(EnrolCourse.Sub_Status.submitted); + mCourseViewModel.update(course); + } + isSubmitting = false; + pb.setVisibility(View.GONE); + } + } + + @Override + protected Boolean doInBackground(Void... params) { + String urlString = "http://www.gstcbunza2012.org.ng/fas/c_register.php"; + + try { + URL url = new URL(urlString); + try { + HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); + httpURLConnection.setRequestMethod("POST"); + httpURLConnection.setRequestProperty("Content-type", "application/json"); + httpURLConnection.setRequestProperty("Connection", "Keep-Alive"); + httpURLConnection.setReadTimeout(15000); + httpURLConnection.setConnectTimeout(15000); + httpURLConnection.setDoInput(true); + httpURLConnection.setDoOutput(true); + httpURLConnection.connect(); + + + OutputStream outputStream = httpURLConnection.getOutputStream(); + outputStream.write(prepareRecordJsonArray().toString().getBytes(StandardCharsets.UTF_8)); + + InputStream inputStream = httpURLConnection.getInputStream(); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "iso-8859-1")); + StringBuilder result = new StringBuilder(); + String line; + + while ((line = bufferedReader.readLine()) != null) { + result.append(line); + } + String rslt = result.toString().trim(); + if (rslt.equals("Data successfully submitted")) { + return true; + } + bufferedReader.close(); + inputStream.close(); + httpURLConnection.disconnect(); + } catch (Exception e) { + e.printStackTrace(); + } + + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + } + + SubmitRecordAsynTask submit = new SubmitRecordAsynTask(); + submit.execute(); + + } + public boolean isWifiCxd() { + ConnectivityManager connMgr = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); + + return networkInfo!=null && networkInfo.isConnected(); + + } + public void saveAddress(String mAddress) { + SharedPreferences sharedPref = getSharedPreferences(AddressFile, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString("Address", mAddress); + + editor.apply(); + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/MainActivity2.java b/app/src/main/java/com/example/attendancemgr/MainActivity2.java new file mode 100644 index 0000000..99eb992 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/MainActivity2.java @@ -0,0 +1,408 @@ +package com.example.attendancemgr; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Color; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.AsyncTask; +import android.os.Bundle; +import android.util.Base64; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.ImageView; +import android.widget.Toast; + +import androidx.activity.OnBackPressedCallback; +import androidx.activity.OnBackPressedDispatcher; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.drawerlayout.widget.DrawerLayout; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.ViewModelProvider; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; +import androidx.navigation.fragment.NavHostFragment; +import androidx.navigation.ui.NavigationUI; + +import com.example.attendancemgr.database.AgentCourse; +import com.example.attendancemgr.database.AttendanceCourse; +import com.example.attendancemgr.database.CourseViewModel; +import com.example.attendancemgr.database.EnrolCourse; +import com.google.android.material.navigation.NavigationView; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static com.example.attendancemgr.ui.login.LoginActivity.ID_FILE; +import static com.example.attendancemgr.ui.login.LoginActivity.USERNAME; + +public class MainActivity2 extends AppCompatActivity { + public static final String ATT_COURSE = "Course"; + public static final String ATT_TUTOR_FP = "Authenticate tutor"; + public static final String ATD_TUTOR_FP = "Tutor FPs"; + public static final String STUDENT_MATCH = "Authenticate student"; + public static final String ATT_STDs_FPs = "Students FPs"; + public static final String ATT_STDs_NOs = "Students NOs"; + public static final String ATT_STDs_IDs = "Attendance IDs"; + + private DrawerLayout drawer; + private Set orgData = new HashSet<>(); + private List allCourses; + private NavigationView navigationView; + private CourseViewModel mCourseViewModel; + private boolean isSubmitting, Submitted; + private ImageView headerImageView; + Toolbar toolbar; + public static final String AVAILABLE_JOBS = "Available"; + public static final String COMPLETED_JOBS = "Completed"; + public static final String JOBS_FILE = "Jobs file"; + public static final String NO_DATA = "No_Data"; + private List attendanceCourses; + private List enrolCourses; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main2); + toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + headerImageView = findViewById(R.id.imageView); + drawer = findViewById(R.id.drawer_layout); + navigationView = findViewById(R.id.nav_view); + mCourseViewModel = new ViewModelProvider(this).get(CourseViewModel.class); + LiveData> allCoursesLiveData = mCourseViewModel.getAllAgentCourses(); + LiveData> attLiveData = mCourseViewModel.getAllAttendanceCourses(); + LiveData> enrolLiveData = mCourseViewModel.getAllEnrolCourses(); + NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment); + NavController navController = navHostFragment.getNavController(); + + /*Bundle b = new Bundle(); + b.putString("name", "isa"); + navController.setGraph(R.navigation.mobile_navigation, b);*/ + NavigationUI.setupActionBarWithNavController(this, navController, drawer); + NavigationUI.setupWithNavController(navigationView, navController); + + + Intent loginIntent = getIntent(); + if (loginIntent.hasExtra("data")){ + if (loginIntent.getExtras().get("data") != null){ + Object[] availsObject = (Object[]) loginIntent.getExtras().get("data"); + for (Object obj:availsObject) { + orgData.add((String[]) obj); + } + + /*if (availJobs.size()>0){ + saveData(availJobs, AVAILABLE_JOBS); + }*/ + } + } + + enrolLiveData.observe(this, enrolCourses1 ->{ + enrolCourses = enrolCourses1; + updateBarImage(); + }); + + attLiveData.observe(this, attendanceCourses1 -> { + if (attendanceCourses1!=null){ + attendanceCourses = attendanceCourses1; + updateBarImage(); + } + }); + + allCoursesLiveData.observe(this, agentCourses -> allCourses = agentCourses); + + OnBackPressedDispatcher dispatcher = this.getOnBackPressedDispatcher(); + dispatcher.addCallback(new OnBackPressedCallback(true) { + @Override + public void handleOnBackPressed() { + if (!Submitted){ + final AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity2.this); + builder.setMessage("Submit record?"); + builder.setPositiveButton("Yes", (dialog, which) -> { + if (isWifiCxd()){ + submitRecord(); + dialog.cancel(); + } + } + ); + + builder.setNegativeButton("No", (dialog, which) -> dialog.cancel()); + + AlertDialog dialog = builder.create(); + + dialog.show(); + } else { + finish(); + } + } + }); + + } + + private void updateBarImage() { + if (isRecordSubmitted()) { + navigationView.getHeaderView(0).setBackgroundColor(getResources().getColor(R.color.myGreen, null)); + Submitted = true; + } else { + navigationView.getHeaderView(0).setBackgroundColor(Color.RED); + Submitted = false; + } + + } + private boolean isRecordSubmitted() { + int count = 0; + if (attendanceCourses != null) { + for (AttendanceCourse course : attendanceCourses) { + if (course.getSub_Status() == AttendanceCourse.Sub_Status.unsubmitted && course.getCap_status().equals(AttendanceCourse.Cap_Status.captured)) count++; + } + } + if (enrolCourses != null) { + for (EnrolCourse course : enrolCourses) { + if (course.getSub_status() == EnrolCourse.Sub_Status.unsubmitted) count++; + } + } + + return count<1; + } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.main_activity2, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + if (item.getTitle() != null){ + switch (item.getTitle().toString()){ + case "Submit Record": + if (!isWifiCxd()){ + Toast.makeText(this, R.string.no_network, Toast.LENGTH_SHORT).show(); + } else if (!isSubmitting){ + submitRecord(); + } + break; + case "Register Tutor": + if (!isSubmitting) { + Intent i = new Intent(this, RegisterTutor.class); + startActivity(i); + } + break; + case "Logout": + onBackPressed(); + break; + } + return true; + } else { + return false; + } + } + + @Override + public boolean onSupportNavigateUp() { + NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); + return NavigationUI.navigateUp(navController, drawer) + || super.onSupportNavigateUp(); + } + + public void saveData(Set data, String Name) { + SharedPreferences sharedPref = getSharedPreferences(JOBS_FILE, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putStringSet(Name, data); + editor.apply(); + } + + private String getData(String Name){ + SharedPreferences preferences = this.getSharedPreferences(JOBS_FILE, MODE_PRIVATE); + return preferences.getString(Name, NO_DATA); + } + public boolean isWifiCxd() { + ConnectivityManager connMgr = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); + + return networkInfo!=null && networkInfo.isConnected(); + + } + void updateAgentCourseSub() throws JSONException { + Set att_crs = new HashSet<>(attendanceCourses); + int attCount = 0; + for (AgentCourse agCourse: allCourses) { + for (AttendanceCourse course:att_crs) { + if (course.getCourse().equals(agCourse.getCourse()) && course.getCap_status().equals(AttendanceCourse.Cap_Status.captured)) { + agCourse.setSub_status(AgentCourse.Sub_Status.submitted); + mCourseViewModel.update(agCourse); + att_crs.remove(course); + attCount++; + break; + } + } + } + Set enrol_crs = new HashSet<>(); + for (EnrolCourse crs : enrolCourses) { + JSONArray crs_arr = new JSONArray(crs.getCourse()); + for (int i=0; i0) updateBarImage(); + } + private JSONArray prepareRecordJsonArray() throws JSONException { + JSONArray attArray = new JSONArray(); + for (AttendanceCourse course:attendanceCourses) { + if (attendanceCourses.size() == 0){ + break; + }else if(course.getCap_status().equals(AttendanceCourse.Cap_Status.captured)) { + + attArray.put(new JSONObject().put("att_course", course.getCourse()) + .put("AdmNo", course.getAdmNo()) + .put("date", course.getDate()).put("period", course.getPeriod())); + } + } + for (EnrolCourse course:enrolCourses) { + if (enrolCourses.size() == 0){ + break; + }else{ + if (course.getFP() != null){ + // for new courses + attArray.put(new JSONObject().put("enrol_course", new JSONArray(course.getCourse())) + .put("AdmNo", course.getAdmNo()) + .put("fp", Base64.encodeToString(course.getFP(), Base64.DEFAULT)) + .put("IDImg", Base64.encodeToString(course.getIDImg(), Base64.DEFAULT)) + ); + } else { + //for added courses + attArray.put(new JSONObject().put("enrol_course", new JSONArray(course.getCourse())) + .put("AdmNo", course.getAdmNo())); + } + + } + + } + attArray.put(new JSONObject().put("uname", getUsername())); + attArray.put(new JSONObject().put("code", getCode())); + return attArray; + } + + private String getCode() { + SharedPreferences preferences = this.getSharedPreferences("code file", MODE_PRIVATE); + return preferences.getString("code", NO_DATA); + } + + private String getUsername(){ + SharedPreferences preferences = this.getSharedPreferences(ID_FILE, MODE_PRIVATE); + return preferences.getString(USERNAME, NO_DATA); + } + void submitRecord(){ + class SubmitRecordAsynTask extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + toolbar.setSubtitle("Loading..."); + isSubmitting = true; + } + + @Override + protected void onPostExecute(Boolean o) { + super.onPostExecute(o); + if (o) { + try { + updateAgentCourseSub(); + } catch (JSONException e) { + e.printStackTrace(); + } + for (AttendanceCourse course : attendanceCourses) { + if (course.getCap_status().equals(AttendanceCourse.Cap_Status.captured)) { + course.setSub_Status(AttendanceCourse.Sub_Status.submitted); + mCourseViewModel.update(course); + } + } + for (EnrolCourse course : enrolCourses) { + course.setSub_status(EnrolCourse.Sub_Status.submitted); + mCourseViewModel.update(course); + } + + } else { + Toast.makeText(MainActivity2.this, "Error occurred, try again later.", Toast.LENGTH_SHORT).show(); + } + isSubmitting = false; + toolbar.setSubtitle(""); + } + + @Override + protected Boolean doInBackground(Void... params) { + String urlString = "http://www.gstcbunza2012.org.ng/fas/c_register.php"; + + try { + URL url = new URL(urlString); + try { + HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); + httpURLConnection.setRequestMethod("POST"); + httpURLConnection.setRequestProperty("Content-type", "application/json"); + httpURLConnection.setRequestProperty("Connection", "Keep-Alive"); + httpURLConnection.setReadTimeout(15000); + httpURLConnection.setConnectTimeout(15000); + httpURLConnection.setDoInput(true); + httpURLConnection.setDoOutput(true); + httpURLConnection.connect(); + + + OutputStream outputStream = httpURLConnection.getOutputStream(); + outputStream.write(prepareRecordJsonArray().toString().getBytes(StandardCharsets.UTF_8)); + + InputStream inputStream = httpURLConnection.getInputStream(); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "iso-8859-1")); + StringBuilder result = new StringBuilder(); + String line; + + while ((line = bufferedReader.readLine()) != null) { + result.append(line); + } + String rslt = result.toString().trim(); + if (rslt.equals("Data successfully submitted")) { + return true; + } + bufferedReader.close(); + inputStream.close(); + httpURLConnection.disconnect(); + } catch (Exception e) { + e.printStackTrace(); + } + + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + } + + SubmitRecordAsynTask submit = new SubmitRecordAsynTask(); + submit.execute(); + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/RegisterTutor.java b/app/src/main/java/com/example/attendancemgr/RegisterTutor.java new file mode 100644 index 0000000..ce61250 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/RegisterTutor.java @@ -0,0 +1,487 @@ +package com.example.attendancemgr; + +import androidx.activity.result.ActivityResult; +import androidx.activity.result.ActivityResultCallback; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.appcompat.app.AppCompatActivity; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.ViewModelProvider; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Color; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.AsyncTask; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Base64; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import com.example.attendancemgr.database.AgentCourse; +import com.example.attendancemgr.database.CourseViewModel; + +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class RegisterTutor extends AppCompatActivity { +androidx.appcompat.widget.AppCompatSpinner coursesSpinner, facultiesSpinner, deptsSpinner; +private List tutorCourses; +private EditText OTPText; +private final ActivityResultCallback tutorScanCallback = result -> { + if (result.getResultCode() == RESULT_OK) { + storeCourseTutor(result.getData().getByteArrayExtra("tutorBytes"), tutorCourses); + OTPText.setText(""); + } + + assert result.getData() != null; + if (result.getData().hasExtra("BTAddress")) saveAddress((String) result.getData().getExtras().get("BTAddress")); + +}; +private boolean isVisible; +private String chosenCourse = "", trueOTP; +private final String PHONE = "phone number"; +private final String OTP = "otp"; +private List allCourses; +private String authStage, phoneNumber; +private Map>> facsMap; +private Map> selectedFacultyMap; +private ArrayList selectedDeptCourses; +TextView coursesView, statusView; +private ArrayAdapter facSpinnerAdapter; +private ArrayAdapter deptSpinnerAdapter; +ProgressBar pb; +Button scanButton; +private ActivityResultLauncher enrolTutorLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), tutorScanCallback); + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_register_tutor); + coursesSpinner = findViewById(R.id.availableCoursesSpinner); + facultiesSpinner = findViewById(R.id.facultySpinner); + deptsSpinner = findViewById(R.id.deptSpinner); + coursesView = findViewById(R.id.selectedCourses); + scanButton = findViewById(R.id.scanFingerButton); + OTPText = findViewById(R.id.OTPEditText); + pb = findViewById(R.id.progressBar); + statusView = findViewById(R.id.statusTextView); + CourseViewModel mCourseViewModel = new ViewModelProvider(this).get(CourseViewModel.class); + + LiveData> allCoursesLiveData = mCourseViewModel.getAllAgentCourses(); + scanButton.setText("next"); + tutorCourses = new LinkedList<>(); + pb.setVisibility(View.INVISIBLE); + + facsMap = new HashMap<>(); + + allCoursesLiveData.observe(this, agentCourses -> { + allCourses = agentCourses; + initMaps(); + }); + + + facSpinnerAdapter = new ArrayAdapter<>(RegisterTutor.this, R.layout.faculty_spinner_text); + deptSpinnerAdapter = new ArrayAdapter<>(RegisterTutor.this, R.layout.dept_spinner_text); + ArrayAdapter adapter = new ArrayAdapter<>(RegisterTutor.this, R.layout.spinner_text); + + facSpinnerAdapter.insert("Select faculty", 0); + facultiesSpinner.setAdapter(facSpinnerAdapter); + deptSpinnerAdapter.add("Select department"); + deptsSpinner.setAdapter(deptSpinnerAdapter); + deptSpinnerAdapter.setNotifyOnChange(true); + + adapter.add("Select course"); + coursesSpinner.setAdapter(adapter); + adapter.setNotifyOnChange(true); + + facultiesSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + if (i>0){ + if (facsMap.containsKey((String) facultiesSpinner.getAdapter().getItem(i))){ + selectedFacultyMap = facsMap.get((String) facultiesSpinner.getAdapter().getItem(i)); + + Set deptsSet = selectedFacultyMap.keySet(); + deptSpinnerAdapter.clear(); + deptSpinnerAdapter.add("Select Department"); + deptSpinnerAdapter.addAll(deptsSet); + deptSpinnerAdapter.notifyDataSetChanged(); + + } + + } + } + + @Override + public void onNothingSelected(AdapterView adapterView) { + + } + }); + deptsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + if(i>0){ + if (selectedFacultyMap.containsKey(deptSpinnerAdapter.getItem(i))) { + selectedDeptCourses = selectedFacultyMap.get(deptSpinnerAdapter.getItem(i)); + adapter.clear(); + adapter.add("Select Course"); + adapter.addAll(selectedDeptCourses); + adapter.notifyDataSetChanged(); + } + } + } + + @Override + public void onNothingSelected(AdapterView adapterView) { + + } + }); + coursesSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (position>0) { + chosenCourse = selectedDeptCourses.get(position-1); + String space = " " + chosenCourse; + String viewText = coursesView.getText().toString(); + if (!viewText.isEmpty() && !viewText.contains(chosenCourse)) { + String concat = viewText + " - " + chosenCourse; + coursesView.setText(concat); + tutorCourses.add(chosenCourse); + } else if (viewText.isEmpty()) { + coursesView.setText(space); + tutorCourses.add(chosenCourse); + } else if (viewText.contains(chosenCourse)) { + Toast.makeText(RegisterTutor.this, "Course has already been selected", Toast.LENGTH_LONG).show(); + } + scanButton.setText("next"); + } + } + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + + allCoursesLiveData.observe(this, agentCourses -> { + allCourses = agentCourses; + initMaps(); + ArrayList facs = new ArrayList<>(); + for (Object obj : facsMap.keySet()) { + facs.add( obj.toString()); + } + facSpinnerAdapter.addAll(facs); + facSpinnerAdapter.notifyDataSetChanged(); + }); + coursesView.setOnLongClickListener(view -> { + coursesView.setText(""); + return true; + }); + OTPText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { + if (authStage.equals(PHONE)) { + + + if (charSequence.length() == 11) { + authStage = + phoneNumber = charSequence.toString(); + OTPText.setText(""); + authStage = OTP; + scanButton.setText("get PIN"); + getOTP(chosenCourse, phoneNumber); + + } + } else { + if(charSequence.length() == 6){ + if (trueOTP.contentEquals(charSequence.toString())){ + scanButton.setText("Scan"); + scanTutor(scanButton); + } + } + } + } + + @Override + public void afterTextChanged(Editable editable) { + } + }); + } + + private void initMaps() { + Set facSet = new HashSet<>(); + Set depSet = new HashSet<>(); + for (AgentCourse course : allCourses) { + facSet.add(course.getFaculty()); + depSet.add(course.getDept()); + } + Map>> facMap = new HashMap<>(); + + for (String fac : facSet) { + Map> deptMap = new HashMap<>(); + for (String dep : depSet) { + ArrayList deptCourses = new ArrayList<>(); + for (AgentCourse course : allCourses) { + if (dep.equals(course.getDept()) && fac.equals(course.getFaculty())){ + deptCourses.add(course.getCourse()); + } + } + + if (deptCourses.size()>0) deptMap.put(dep, deptCourses); + } + facMap.put(fac, deptMap); + } + + facsMap = facMap; + } + + public void scanTutor(View v) { + if (isWifiCxd()) { + if (coursesView.getText().toString().isEmpty()) { + Toast.makeText(this, "Please select a course", Toast.LENGTH_SHORT).show(); + } else { + if (scanButton.getText().equals("Scan") && !isVisible) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setPackage("com.fgtit.reader"); + /* if(intent.resolveActivity(RegisterTutor.this.getPackageManager())!=null) { + }*/ + intent.putExtra("Enrol tutor", true); + if (!getAddress().equals("No_Address")) + intent.putExtra("bt_Address", getAddress()); + + enrolTutorLauncher.launch(intent); + } else if (scanButton.getText().equals("next") && !isVisible && !chosenCourse.equals("")){ + OTPText.setVisibility(View.VISIBLE); + OTPText.setHint("0XXX - XXX - XXXX"); + statusView.setVisibility(View.VISIBLE); + statusView.setText("Please input phone number"); + authStage = PHONE; + } + } + } else { + Toast.makeText(this, "Please connect to the network.", Toast.LENGTH_SHORT).show(); + } + } + private void getOTP(String... courses){ + class getOTPAsynctask extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + statusView.setText("Loading..."); + scanButton.setEnabled(false); + isVisible = true; + } + + @Override + protected void onPostExecute(String otp) { + super.onPostExecute(otp); + if (otp!=null) trueOTP = otp; + OTPText.setVisibility(View.VISIBLE); + OTPText.setHint("XXXXXX"); + statusView.setText("Please input PIN code"); + scanButton.setEnabled(true); + isVisible = false; + } + + + @Override + protected String doInBackground(String... strings) { + String login_url = "http://www.gstcbunza2012.org.ng/fas/otp_data.php"; + + try { + java.net.URL url = new URL(login_url); + try { + HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); + httpURLConnection.setRequestMethod("POST"); + httpURLConnection.setReadTimeout(15000); + httpURLConnection.setConnectTimeout(15000); + httpURLConnection.setDoInput(true); + httpURLConnection.setDoOutput(true); + + OutputStream outputStream = httpURLConnection.getOutputStream(); + BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8")); + String post_data = URLEncoder.encode("course", "UTF-8") + "=" + URLEncoder.encode(strings[0], "UTF-8") + "&" + + URLEncoder.encode("phone", "UTF-8") + "=" + URLEncoder.encode(strings[1], "UTF-8"); + bufferedWriter.write(post_data); + bufferedWriter.flush(); + bufferedWriter.close(); + + InputStream inputStream = httpURLConnection.getInputStream(); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "iso-8859-1")); + StringBuilder result = new StringBuilder(); + String line; + + while ((line = bufferedReader.readLine()) != null) { + result.append(line); + } + String rslt = result.toString().trim(); + + if (rslt.length()==6) { + return rslt; + + } + bufferedReader.close(); + inputStream.close(); + httpURLConnection.disconnect(); + } catch (Exception e) { + e.printStackTrace(); + } + + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + } + getOTPAsynctask getOTPAsynctask = new getOTPAsynctask(); + getOTPAsynctask.execute(courses); + } + private void storeCourseTutor(byte[] result, List tutorCourses) { + + StringBuilder Courses = new StringBuilder(); + for (int i=0; i { + @Override + protected void onPreExecute() { + super.onPreExecute(); + pb.setVisibility(View.VISIBLE); + isVisible = true; + } + + @Override + protected void onPostExecute(Boolean saved) { + super.onPostExecute(saved); + if (saved){ + Toast.makeText(RegisterTutor.this, "fine!", Toast.LENGTH_SHORT).show(); + statusView.setTextColor(Color.GREEN); + statusView.setText("Data uploaded successfully!"); + for (String course : tutorCourses) { + SharedPreferences sharedPref = getSharedPreferences(course, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString("tutorFP", Base64.encodeToString(result, Base64.DEFAULT)); + editor.apply(); + } + } else { + statusView.setTextColor(Color.RED); + statusView.setText("Data upload failed!"); + } + pb.setVisibility(View.INVISIBLE); + isVisible = false; + } + + + @Override + protected Boolean doInBackground(String... strings) { + String login_url = "http://www.gstcbunza2012.org.ng/fas/tutor.php"; + + try { + java.net.URL url = new URL(login_url); + try { + HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); + httpURLConnection.setRequestMethod("POST"); + httpURLConnection.setRequestProperty("Content-type", "application/json"); + httpURLConnection.setRequestProperty("Connection", "Keep-Alive"); + httpURLConnection.setReadTimeout(15000); + httpURLConnection.setConnectTimeout(15000); + httpURLConnection.setDoInput(true); + httpURLConnection.setDoOutput(true); + httpURLConnection.connect(); + + JSONObject obj = new JSONObject(); + obj.put("phone", strings[0]); + obj.put("Fingerprint", strings[1]); + obj.put("courses", strings[2]); + + OutputStream outputStream = httpURLConnection.getOutputStream(); + + outputStream.write(obj.toString().getBytes(StandardCharsets.UTF_8)); + + InputStream inputStream = httpURLConnection.getInputStream(); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "iso-8859-1")); + StringBuilder result = new StringBuilder(); + String line; + + while ((line = bufferedReader.readLine()) != null) { + result.append(line); + } + String rslt = result.toString().trim(); + if (rslt.equals("fine")) { + return true; + + } + bufferedReader.close(); + inputStream.close(); + httpURLConnection.disconnect(); + } catch (Exception e) { + e.printStackTrace(); + } + + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + } + UploadFP uploadFP = new UploadFP(); + uploadFP.execute(data); + } + + public boolean isWifiCxd() { + ConnectivityManager connMgr = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); + + return networkInfo!=null && networkInfo.isConnected(); + + } + + public void saveAddress(String mAddress) { + SharedPreferences sharedPref = getSharedPreferences(MainActivity.AddressFile, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString("Address", mAddress); + editor.apply(); + } + private String getAddress(){ + SharedPreferences preferences = this.getSharedPreferences(MainActivity.AddressFile, MODE_PRIVATE); + String defaultAddress = "No_Address"; + return preferences.getString("Address", defaultAddress); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/ScanTutor.java b/app/src/main/java/com/example/attendancemgr/ScanTutor.java new file mode 100644 index 0000000..57cb9c2 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/ScanTutor.java @@ -0,0 +1,93 @@ +package com.example.attendancemgr; + +import androidx.activity.OnBackPressedCallback; +import androidx.activity.OnBackPressedDispatcher; +import androidx.appcompat.app.AppCompatActivity; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.View; +import android.widget.EditText; +import android.widget.TextView; + +import static com.example.attendancemgr.ui.Courses.CoursesFragment.TUTOR_OK; + +public class ScanTutor extends AppCompatActivity { +TextView infoView; +EditText passcodeText, periodNumberText; +String passcode; +int period; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_scan_tutor); + infoView = findViewById(R.id.infoView); + passcodeText = findViewById(R.id.textPasscode); + periodNumberText = findViewById(R.id.textPeriodNumber); + periodNumberText.setVisibility(View.GONE); + + Intent intent = getIntent(); + if (intent.hasExtra("passcode")){ + passcode = intent.getExtras().getString("passcode"); + } + + passcodeText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { + if (charSequence.length()==6){ + if (passcode.equals(charSequence.toString())){ + periodNumberText.setVisibility(View.VISIBLE); + passcodeText.setVisibility(View.INVISIBLE); + infoView.setText("Please input period"); + } + } + } + + @Override + public void afterTextChanged(Editable editable) { + + } + }); + + periodNumberText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { + if (charSequence.length()>0){ + period = Integer.parseInt((String) charSequence); + Intent intent1 = new Intent(); + intent1.putExtra("period", period); + setResult(TUTOR_OK, intent1); + finish(); + } + } + + @Override + public void afterTextChanged(Editable editable) { + + } + }); + + OnBackPressedDispatcher dispatcher = new OnBackPressedDispatcher(); + dispatcher.addCallback(new OnBackPressedCallback(true) { + @Override + public void handleOnBackPressed() { + setResult(Activity.RESULT_CANCELED); + finish(); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/data/LoginDataSource.java b/app/src/main/java/com/example/attendancemgr/data/LoginDataSource.java new file mode 100644 index 0000000..7b0388e --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/data/LoginDataSource.java @@ -0,0 +1,63 @@ +package com.example.attendancemgr.data; + +import android.content.SharedPreferences; +import android.os.AsyncTask; + +import androidx.annotation.NonNull; + +import com.example.attendancemgr.MainActivity2; +import com.example.attendancemgr.data.model.LoggedInUser; + +import org.json.JSONArray; +import org.json.JSONException; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.ArrayList; + +import static android.content.Context.MODE_PRIVATE; +import static com.example.attendancemgr.MainActivity2.AVAILABLE_JOBS; +import static com.example.attendancemgr.MainActivity2.JOBS_FILE; +import static com.example.attendancemgr.MainActivity2.NO_DATA; + +/** + * Class that handles authentication w/ login credentials and retrieves user information. + */ +public class LoginDataSource { + boolean dataBack; + + public static final String DEFAULT_ID = "id"; + public static final String DEFAULT_DISPLAY_NAME = "display name"; + + public Result login(String username, String password, String isFirst) { + + try { + + return new Result.Success(null); + + + } catch (Exception e) { + return new Result.Error(new IOException("Error logging in", e)); + } + } + + public void logout() { + } + + +private static class InvalidUserException extends Exception{ + @NonNull + @Override + public String toString() { + return "Invalid Username or Password"; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/data/LoginRepository.java b/app/src/main/java/com/example/attendancemgr/data/LoginRepository.java new file mode 100644 index 0000000..98d8109 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/data/LoginRepository.java @@ -0,0 +1,54 @@ +package com.example.attendancemgr.data; + +import com.example.attendancemgr.data.model.LoggedInUser; + +/** + * Class that requests authentication and user information from the remote data source and + * maintains an in-memory cache of login status and user credentials information. + */ +public class LoginRepository { + + private static volatile LoginRepository instance; + + private LoginDataSource dataSource; + + // If user credentials will be cached in local storage, it is recommended it be encrypted + // @see https://developer.android.com/training/articles/keystore + private LoggedInUser user = null; + + // private constructor : singleton access + private LoginRepository(LoginDataSource dataSource) { + this.dataSource = dataSource; + } + + public static LoginRepository getInstance(LoginDataSource dataSource) { + if (instance == null) { + instance = new LoginRepository(dataSource); + } + return instance; + } + + public boolean isLoggedIn() { + return user != null; + } + + public void logout() { + user = null; + dataSource.logout(); + } + + private void setLoggedInUser(LoggedInUser user) { + this.user = user; + // If user credentials will be cached in local storage, it is recommended it be encrypted + // @see https://developer.android.com/training/articles/keystore + } + + public Result login(String username, String password, String isFirst) { + // handle login + Result result = dataSource.login(username, password, isFirst); + if (result instanceof Result.Success) { + setLoggedInUser(((Result.Success) result).getData()); + } + return result; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/data/Result.java b/app/src/main/java/com/example/attendancemgr/data/Result.java new file mode 100644 index 0000000..b41f497 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/data/Result.java @@ -0,0 +1,48 @@ +package com.example.attendancemgr.data; + +/** + * A generic class that holds a result success w/ data or an error exception. + */ +public class Result { + // hide the private constructor to limit subclass types (Success, Error) + private Result() { + } + + @Override + public String toString() { + if (this instanceof Result.Success) { + Result.Success success = (Result.Success) this; + return "Success[data=" + success.getData().toString() + "]"; + } else if (this instanceof Result.Error) { + Result.Error error = (Result.Error) this; + return "Error[exception=" + error.getError().toString() + "]"; + } + return ""; + } + + // Success sub-class + public final static class Success extends Result { + private T data; + + public Success(T data) { + this.data = data; + } + + public T getData() { + return this.data; + } + } + + // Error sub-class + public final static class Error extends Result { + private Exception error; + + public Error(Exception error) { + this.error = error; + } + + public Exception getError() { + return this.error; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/data/model/CoursesModel.java b/app/src/main/java/com/example/attendancemgr/data/model/CoursesModel.java new file mode 100644 index 0000000..74e817a --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/data/model/CoursesModel.java @@ -0,0 +1,16 @@ +package com.example.attendancemgr.data.model; + +public class CoursesModel { + private String mCourse; + public CoursesModel(String course) { + this.mCourse = course; + } + + public String getmCourse() { + return mCourse; + } + + public void setmCourse(String mCourse) { + this.mCourse = mCourse; + } +} diff --git a/app/src/main/java/com/example/attendancemgr/data/model/DepartmentsModel.java b/app/src/main/java/com/example/attendancemgr/data/model/DepartmentsModel.java new file mode 100644 index 0000000..c5c2b28 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/data/model/DepartmentsModel.java @@ -0,0 +1,19 @@ +package com.example.attendancemgr.data.model; + +public class DepartmentsModel { + private String mDept; + + public DepartmentsModel(String deptName) { + this.mDept = deptName; + } + + public String getmDept() { + return mDept; + } + + public void setmDept(String mDept1) { + this.mDept = mDept1; + } + + +} diff --git a/app/src/main/java/com/example/attendancemgr/data/model/LoggedInUser.java b/app/src/main/java/com/example/attendancemgr/data/model/LoggedInUser.java new file mode 100644 index 0000000..fb5de21 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/data/model/LoggedInUser.java @@ -0,0 +1,19 @@ +package com.example.attendancemgr.data.model; + +/** + * Data class that captures user information for logged in users retrieved from LoginRepository + */ +public class LoggedInUser { + private String mData; + + + +public LoggedInUser(String data){ + this.mData = data; +} + public String getmData() { + return mData; + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/database/AgentCourse.java b/app/src/main/java/com/example/attendancemgr/database/AgentCourse.java new file mode 100644 index 0000000..97e2d9b --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/database/AgentCourse.java @@ -0,0 +1,100 @@ +package com.example.attendancemgr.database; + + +import androidx.annotation.NonNull; +import androidx.room.ColumnInfo; +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "AgentCourse_table") +public class AgentCourse { + public enum Att_Status { + open, closed + } + public enum Sub_Status { + submitted, unsubmitted + } + @PrimaryKey(autoGenerate = true) + private int id; + + @NonNull + @ColumnInfo(name = "faculty") + private String faculty; + + @NonNull + public String getFaculty() { + return faculty; + } + + public void setFaculty(@NonNull String faculty) { + this.faculty = faculty; + } + + @NonNull + @ColumnInfo(name = "department") + private String dept; + + @NonNull + @ColumnInfo(name = "course") + private String course; + + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + @ColumnInfo(name = "attendance_status") + private Att_Status att_status; + + @ColumnInfo(name = "submission_status") + private Sub_Status sub_status; + + @NonNull + public String getDept() { + return dept; + } + + public void setDept(@NonNull String dept) { + this.dept = dept; + } + + public void setCourse(@NonNull String course) { + this.course = course; + } + + public AgentCourse(@NonNull String faculty, @NonNull String dept, @NonNull String course, Att_Status att_status, Sub_Status sub_status){ + this.faculty = faculty; + this.dept = dept; + this.course = course; + this.att_status = att_status; + this.sub_status = sub_status; + + } + + @NonNull + public String getCourse() { + return course; + } + + + + public AgentCourse.Att_Status getAtt_status() { + return att_status; + } + + public void setAtt_status(AgentCourse.Att_Status att_status) { + this.att_status = att_status; + } + + public AgentCourse.Sub_Status getSub_status() { + return sub_status; + } + + public void setSub_status(AgentCourse.Sub_Status sub_status) { + this.sub_status = sub_status; + } +} diff --git a/app/src/main/java/com/example/attendancemgr/database/AttendanceCourse.java b/app/src/main/java/com/example/attendancemgr/database/AttendanceCourse.java new file mode 100644 index 0000000..180a08c --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/database/AttendanceCourse.java @@ -0,0 +1,142 @@ +package com.example.attendancemgr.database; + +import androidx.annotation.NonNull; +import androidx.room.ColumnInfo; +import androidx.room.Entity; +import androidx.room.Ignore; +import androidx.room.PrimaryKey; + +@Entity(tableName = "AttendanceCourse_table") +public class AttendanceCourse { + public enum Sub_Status { + submitted, unsubmitted + } + public enum Cap_Status { + captured, uncaptured + } + @NonNull + @ColumnInfo(name = "course") + private String course; + + @PrimaryKey(autoGenerate = true) + private int id; + + @ColumnInfo(name = "sub_status") + private Sub_Status sub_status; + + @ColumnInfo(name = "cap_status") + private Cap_Status cap_status; + + @ColumnInfo(name = "admission_number") + private String Adm_No; + + @ColumnInfo(name = "fingerprint") + private byte[] FP; + + @ColumnInfo(name = "date") + private String Date; + + @ColumnInfo(name = "period") + private int period; + public AttendanceCourse(@NonNull String course, String Adm_No, byte[] FP, Sub_Status sub_status, Cap_Status cap_status, String Date, int period){ + this.course = course; + this.sub_status = sub_status; + this.Adm_No = Adm_No; + this.FP = FP; + this.cap_status = cap_status; + this.Date = Date; + this.period = period; + } + + @Ignore +public AttendanceCourse(int id, @NonNull String course, String admNo, byte[] fp, Sub_Status status, Cap_Status cap_status, String Date, int period){ + this.course = course; + this.sub_status = status; + this.Adm_No = admNo; + this.FP = fp; + this.id = id; + this.cap_status = cap_status; + this.Date = Date; + this.period = period; + +} + @NonNull + public String getCourse() { + return course; + } + + public String getAdmNo() { + return Adm_No; + } + + public void setAdmNo(String AdmNo) { + this.Adm_No = AdmNo; + } + + public int getPeriod() { + return period; + } + + public void setPeriod(int period) { + this.period = period; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public byte[] getFP() { + return FP; + } + + public Cap_Status getCap_status() { + return cap_status; + } + + public void setCap_status(Cap_Status cap_status) { + this.cap_status = cap_status; + } + + public void setFP(byte[] fp) { + FP = fp; + } + + public Sub_Status getSub_Status() { + return sub_status; + } + + public void setSub_Status(Sub_Status sub_status) { + this.sub_status = sub_status; + } + + public Sub_Status getSub_status() { + return sub_status; + } + + public String getAdm_No() { + return Adm_No; + } + public void setSub_status(Sub_Status sub_status) { + this.sub_status = sub_status; + } + + public void setAdm_No(String adm_No) { + Adm_No = adm_No; + } + + public void setCourse(@NonNull String course) { + this.course = course; + } + + public String getDate() { + return Date; + } + + public void setDate(String date) { + Date = date; + } +} diff --git a/app/src/main/java/com/example/attendancemgr/database/CourseDao.java b/app/src/main/java/com/example/attendancemgr/database/CourseDao.java new file mode 100644 index 0000000..66c11d6 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/database/CourseDao.java @@ -0,0 +1,62 @@ +package com.example.attendancemgr.database; + +import androidx.lifecycle.LiveData; +import androidx.room.Dao; +import androidx.room.Delete; +import androidx.room.Insert; +import androidx.room.OnConflictStrategy; +import androidx.room.Query; +import androidx.room.Update; + +import java.util.LinkedList; +import java.util.List; +@Dao +public interface CourseDao { + @Insert(onConflict = OnConflictStrategy.IGNORE) + void insert(AgentCourse agCourse); + + @Insert(onConflict = OnConflictStrategy.IGNORE) + void insert(AttendanceCourse atCourse); + + @Insert(onConflict = OnConflictStrategy.IGNORE) + void insert(EnrolCourse enCourse); + + @Query("DELETE FROM AgentCourse_table") + void deleteAllAgentCourse(); + + @Query("DELETE FROM AttendanceCourse_table") + void deleteAllAttendanceCourse(); + + @Query("DELETE FROM EnrolCourse_table") + void deleteAllEnrolCourse(); + + @Delete + void deleteAgentCourse(AgentCourse agCourse); + + @Delete + void deleteAttendanceCourse(AttendanceCourse atCourse); + + @Delete + void deleteEnrolCourse(EnrolCourse enCourse); + + @Query("SELECT * from AgentCourse_table ORDER BY course ASC") + LiveData> getAllAgentCourses(); + + @Query("SELECT * from AttendanceCourse_table WHERE sub_status == 'unsubmitted' ORDER BY course ASC") + LiveData> getAllAttendanceCourses(); + + @Query("SELECT * from EnrolCourse_table WHERE status == 'unsubmitted' ORDER BY course ASC") + LiveData> getAllEnrolCourses(); + + @Update + void update(AgentCourse... courses); + + @Update() + void update(AttendanceCourse... courses); + + @Update + void update(EnrolCourse... courses); + + @Query("SELECT * from AgentCourse_table LIMIT 1") + AgentCourse[] getAnyCourse(); +} diff --git a/app/src/main/java/com/example/attendancemgr/database/CourseRepository.java b/app/src/main/java/com/example/attendancemgr/database/CourseRepository.java new file mode 100644 index 0000000..23c0064 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/database/CourseRepository.java @@ -0,0 +1,176 @@ +package com.example.attendancemgr.database; + +import android.app.Application; +import android.os.AsyncTask; + +import androidx.lifecycle.LiveData; + +import java.util.List; + +public class CourseRepository { + private CourseDao mCourseDao; + private LiveData> mAllAgentCourses; + private LiveData> mAllEnrolCourses; + private LiveData> mAllAttendanceCourses; + + CourseRepository(Application application) { + CourseRoomDatabase db = CourseRoomDatabase.getDatabase(application); + mCourseDao = db.courseDao(); + mAllAgentCourses = mCourseDao.getAllAgentCourses(); + mAllAttendanceCourses = mCourseDao.getAllAttendanceCourses(); + mAllEnrolCourses = mCourseDao.getAllEnrolCourses(); + } + LiveData> getAllAgentCourses() { + return mAllAgentCourses; + } + LiveData> getAllEnrolCourses() { + return mAllEnrolCourses; + } + LiveData> getAllAttendanceCourses() { + return mAllAttendanceCourses; + } + + public void insert(AgentCourse course) { + new insertAsyncTask(mCourseDao).execute(course); + } + public void insert(AttendanceCourse course) { + new insertAsyncTask(mCourseDao).execute(course); + } + public void insert(EnrolCourse course) { + new insertAsyncTask(mCourseDao).execute(course); + } + + public void update(AgentCourse course) { + new updateCourseAsyncTask(mCourseDao).execute(course); + } + public void update(AttendanceCourse course) { + new updateCourseAsyncTask(mCourseDao).execute(course); + } + public void update(EnrolCourse course) { + new updateCourseAsyncTask(mCourseDao).execute(course); + } + + public void deleteAllAgentCourses() { + new deleteAllAgentCoursesAsyncTask(mCourseDao).execute(); + } + public void deleteAllAttendanceCourses() { + new deleteAllAttendanceCoursesAsyncTask(mCourseDao).execute(); + } + public void deleteAllEnrolCourses() { + new deleteAllEnrolCoursesAsyncTask(mCourseDao).execute(); + } + + public void deleteAgentCourse(AgentCourse course) { + new deleteCourseAsyncTask(mCourseDao).execute(course); + } + public void deleteAttendanceCourse(AttendanceCourse course) { + new deleteCourseAsyncTask(mCourseDao).execute(course); + } + public void deleteEnrolCourse(EnrolCourse course) { + new deleteCourseAsyncTask(mCourseDao).execute(course); + } + public AgentCourse[] getAnyCourse(){ + return mCourseDao.getAnyCourse(); + } + private static class insertAsyncTask extends AsyncTask { + + private CourseDao mAsyncTaskDao; + + insertAsyncTask(CourseDao dao) { + mAsyncTaskDao = dao; + } + + @Override + protected Void doInBackground(final Object... params) { + if(AgentCourse.class.getName().equals(params[0].getClass().getName())){ + mAsyncTaskDao.insert((AgentCourse) params[0]); + }else if(EnrolCourse.class.getName().equals(params[0].getClass().getName())){ + mAsyncTaskDao.insert((EnrolCourse) params[0]); + }else if(AttendanceCourse.class.getName().equals(params[0].getClass().getName())){ + mAsyncTaskDao.insert((AttendanceCourse) params[0]); + } + + return null; + } + } + + private static class deleteAllAgentCoursesAsyncTask extends AsyncTask { + private CourseDao mAsyncTaskDao; + + deleteAllAgentCoursesAsyncTask(CourseDao dao) { + mAsyncTaskDao = dao; + } + + @Override + protected Void doInBackground(Void... voids) { + mAsyncTaskDao.deleteAllAgentCourse(); + return null; + } + } + private static class deleteAllEnrolCoursesAsyncTask extends AsyncTask { + private CourseDao mAsyncTaskDao; + + deleteAllEnrolCoursesAsyncTask(CourseDao dao) { + mAsyncTaskDao = dao; + } + + @Override + protected Void doInBackground(Void... voids) { + mAsyncTaskDao.deleteAllEnrolCourse(); + return null; + } + } + private static class deleteAllAttendanceCoursesAsyncTask extends AsyncTask { + private CourseDao mAsyncTaskDao; + + deleteAllAttendanceCoursesAsyncTask(CourseDao dao) { + mAsyncTaskDao = dao; + } + + @Override + protected Void doInBackground(Void... voids) { + mAsyncTaskDao.deleteAllAttendanceCourse(); + return null; + } + } + + private static class deleteCourseAsyncTask extends AsyncTask { + private CourseDao mAsyncTaskDao; + + private deleteCourseAsyncTask(CourseDao Dao) { + this.mAsyncTaskDao = Dao; + } + + @Override + protected Void doInBackground(final Object... params) { + if(AgentCourse.class.getName().equals(params[0].getClass().getName())){ + mAsyncTaskDao.deleteAgentCourse((AgentCourse) params[0]); + }else if(EnrolCourse.class.getName().equals(params[0].getClass().getName())){ + mAsyncTaskDao.deleteEnrolCourse((EnrolCourse) params[0]); + }else if(AttendanceCourse.class.getName().equals(params[0].getClass().getName())){ + mAsyncTaskDao.deleteAttendanceCourse((AttendanceCourse) params[0]); + } + return null; + } + } + + private static class updateCourseAsyncTask extends AsyncTask { + private CourseDao mAsyncTaskDao; + + updateCourseAsyncTask(CourseDao dao) { + mAsyncTaskDao = dao; + } + + @Override + protected Void doInBackground(final Object... params) { + if(AgentCourse.class.getName().equals(params[0].getClass().getName())){ + mAsyncTaskDao.update((AgentCourse) params[0]); + }else if(EnrolCourse.class.getName().equals(params[0].getClass().getName())){ + mAsyncTaskDao.update((EnrolCourse) params[0]); + }else if(AttendanceCourse.class.getName().equals(params[0].getClass().getName())){ + mAsyncTaskDao.update((AttendanceCourse) params[0]); + } + return null; + } + } +} diff --git a/app/src/main/java/com/example/attendancemgr/database/CourseRoomDatabase.java b/app/src/main/java/com/example/attendancemgr/database/CourseRoomDatabase.java new file mode 100644 index 0000000..eda307e --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/database/CourseRoomDatabase.java @@ -0,0 +1,64 @@ +package com.example.attendancemgr.database; + +import android.content.Context; +import android.os.AsyncTask; +import androidx.room.Database; +import androidx.annotation.NonNull; +import androidx.room.Room; +import androidx.room.RoomDatabase; +import androidx.sqlite.db.SupportSQLiteDatabase; + +@Database(entities = {AgentCourse.class, AttendanceCourse.class, EnrolCourse.class}, version = 3, exportSchema = false) +public abstract class CourseRoomDatabase extends RoomDatabase { + + public abstract CourseDao courseDao(); + + private static CourseRoomDatabase INSTANCE; + + public static CourseRoomDatabase getDatabase(final Context context) { + if (INSTANCE == null) { + synchronized (CourseRoomDatabase.class) { + if (INSTANCE == null) { + // Create database here. + INSTANCE = Room.databaseBuilder(context.getApplicationContext(), + CourseRoomDatabase.class, "Attendance_database") + // Wipes and rebuilds instead of migrating if no Migration object. + // Migration is not part of this practical. + .fallbackToDestructiveMigration() + .addCallback(sRoomDatabaseCallback) + .build(); + } + } + } + return INSTANCE; + } + + private static RoomDatabase.Callback sRoomDatabaseCallback = + new RoomDatabase.Callback(){ + + @Override + public void onOpen (@NonNull SupportSQLiteDatabase db){ + super.onOpen(db); + } + }; + + + + private static class PopulateDbAsync extends AsyncTask { + + private final CourseDao mDao; + + // Initial data set + + PopulateDbAsync(CourseRoomDatabase db) { + mDao = db.courseDao(); + } + + @Override + protected Void doInBackground(final Void... params) { + // If we have no words, then create the initial list of words. + + return null; + } + } +} diff --git a/app/src/main/java/com/example/attendancemgr/database/CourseViewModel.java b/app/src/main/java/com/example/attendancemgr/database/CourseViewModel.java new file mode 100644 index 0000000..c5c1e09 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/database/CourseViewModel.java @@ -0,0 +1,81 @@ +package com.example.attendancemgr.database; + +import android.app.Application; + +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MediatorLiveData; +import androidx.lifecycle.Observer; + +import java.util.LinkedList; +import java.util.List; + +public class CourseViewModel extends AndroidViewModel { + private CourseRepository mRepository; + private LiveData> mAllAgentCourses; + private LiveData> mAllAttendanceCourses; + private LiveData> mAllEnrolCourses; + private MediatorLiveData> mAgentCourses = new MediatorLiveData<>(); + public CourseViewModel(@NonNull Application application) { + super(application); + /*db = CourseRoomDatabase.getDatabase(application); + mCourseDao = db.courseDao();*/ + mRepository = new CourseRepository(application); + this.mAllAgentCourses = mRepository.getAllAgentCourses(); + this.mAllAttendanceCourses = mRepository.getAllAttendanceCourses(); + this.mAllEnrolCourses = mRepository.getAllEnrolCourses(); + } + public LiveData> getAllAgentCourses() { + return mAllAgentCourses; + } + public LiveData> getAllAttendanceCourses() { + return mAllAttendanceCourses; + } + public LiveData> getAllEnrolCourses() { + return mAllEnrolCourses; + } +public AgentCourse[] getAnyCourse(){ + return mRepository.getAnyCourse(); +} + public void insert(AgentCourse course) { + mRepository.insert(course); + } + public void insert(AttendanceCourse course) { + mRepository.insert(course); + } + public void insert(EnrolCourse course) { + mRepository.insert(course); + } + + public void deleteAllAgentCourses() { + mRepository.deleteAllAgentCourses(); + } + public void deleteAllAttendanceCourses() { + mRepository.deleteAllAttendanceCourses(); + } + public void deleteAllEnrolCourses() { + mRepository.deleteAllEnrolCourses(); + } + + public void delete(AgentCourse course) { + mRepository.deleteAgentCourse(course); + } + public void delete(AttendanceCourse course) { + mRepository.deleteAttendanceCourse(course); + } + public void delete(EnrolCourse course) { + mRepository.deleteEnrolCourse(course); + } + + public void update(AgentCourse course) { + mRepository.update(course); + } + public void update(AttendanceCourse course) { + mRepository.update(course); + } + public void update(EnrolCourse course) { + mRepository.update(course); + } + +} diff --git a/app/src/main/java/com/example/attendancemgr/database/EnrolCourse.java b/app/src/main/java/com/example/attendancemgr/database/EnrolCourse.java new file mode 100644 index 0000000..1b7b1ec --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/database/EnrolCourse.java @@ -0,0 +1,87 @@ +package com.example.attendancemgr.database; + +import androidx.annotation.NonNull; +import androidx.room.ColumnInfo; +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "EnrolCourse_table") +public class EnrolCourse { + public enum Sub_Status { + submitted, unsubmitted + } + @NonNull + @ColumnInfo(name = "course") + private String course; + + @PrimaryKey(autoGenerate = true) + private int id; + + @ColumnInfo(name = "ID_Image") + private byte[] IDImg; + + @ColumnInfo(name = "admission_number") + private String Adm_No; + + @ColumnInfo(name = "status") + private Sub_Status sub_status; + + @ColumnInfo(name = "fingerprint") + private byte[] FP; + + public String getAdm_No() { + return Adm_No; + } + + public EnrolCourse(@NonNull String course, String Adm_No, byte[] FP, Sub_Status sub_status, byte[] IDImg){ + this.course = course; + this.IDImg = IDImg; + this.Adm_No = Adm_No; + this.FP = FP; + this.sub_status = sub_status; + } + + @NonNull + public String getCourse() { + return course; + } + + + public byte[] getIDImg() { + return IDImg; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + public void setIDImg(byte[] IDImg) { + this.IDImg = IDImg; + } + + public String getAdmNo() { + return Adm_No; + } + + public void setAdmNo(String AdmNo) { + this.Adm_No = AdmNo; + } + + public byte[] getFP() { + return FP; + } + public void setFP(byte[] fp) { + FP = fp; + } + + public Sub_Status getSub_status() { + return sub_status; + } + + public void setSub_status(Sub_Status sub_status) { + this.sub_status = sub_status; + } +} diff --git a/app/src/main/java/com/example/attendancemgr/database/myAdapter.java b/app/src/main/java/com/example/attendancemgr/database/myAdapter.java new file mode 100644 index 0000000..4c0f366 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/database/myAdapter.java @@ -0,0 +1,25 @@ +package com.example.attendancemgr.database; + +import android.content.Context; +import android.widget.ArrayAdapter; + +import androidx.annotation.NonNull; + +import java.util.List; + +public class myAdapter extends ArrayAdapter { + private List mAgentCourses; + private Context mContext; + private int mResource; + public myAdapter(@NonNull Context context, int resource, List objects) { + super(context, resource, objects); + this.mAgentCourses = objects; + this.mContext = context; + this.mResource = resource; + } + public void setAgentCourses(List courses) { + mAgentCourses = courses; + notifyDataSetChanged(); + } + +} diff --git a/app/src/main/java/com/example/attendancemgr/enrolActivity.java b/app/src/main/java/com/example/attendancemgr/enrolActivity.java new file mode 100644 index 0000000..587a5af --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/enrolActivity.java @@ -0,0 +1,366 @@ +package com.example.attendancemgr; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.os.Bundle; +import android.view.Menu; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.activity.result.ActivityResult; +import androidx.activity.result.ActivityResultCallback; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.ViewModelProvider; + +import com.example.attendancemgr.database.AgentCourse; +import com.example.attendancemgr.database.AttendanceCourse; +import com.example.attendancemgr.database.CourseViewModel; +import com.example.attendancemgr.database.EnrolCourse; + +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class enrolActivity extends AppCompatActivity { +private static String chosenCourse, admNo, receivedCourse, MODE; +EditText admNoView; +Spinner coursesSpinner, deptSpinner, facultySpinner; +LiveData> attLiveData; + +TextView selectedCourses; +Button snapButton; +private Map>> facsMap; +private Map> selectedFacultyMap; +private ArrayList selectedDeptCourses; +private List allCourses; +private List attendanceCourses; + +private ArrayAdapter deptSpinnerAdapter, coursesAdapter; +private final String ADD = "Add courses"; +private final String NEW = "new enrollment"; + private Toolbar mToolbar; +private byte[] IDCardbytes; +private List coursesList; + + private final ActivityResultCallback scanCallback = result -> { + if (result.getResultCode() == Activity.RESULT_OK) { + MODE = NEW; + mToolbar.setSubtitle("New Enrollment"); + selectedCourses.setText(""); + snapButton.setText(R.string.capture_id); + Intent resultIntent = result.getData(); + Object[] resultObj = (Object[]) resultIntent.getExtras().get("Result Object"); + processResult(resultObj); + admNoView.setText(""); + + } + if (result.getData().hasExtra("BTAddress")) saveAddress((String) result.getData().getExtras().get("BTAddress")); + + }; + private final ActivityResultCallback snapCallback = result -> { + if(result!=null) { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + result.compress(Bitmap.CompressFormat.PNG, 100, stream); + IDCardbytes = stream.toByteArray(); + launchFPScan(); + } + }; +private boolean isFirst = true; +private String[][] un_enrolled; +private CourseViewModel mCourseViewModel; +private final String[] Courses = {"", "PHY101", "PHY102"}; +ActivityResultLauncher scanLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), scanCallback); +ActivityResultLauncher snapLauncher = registerForActivityResult(new ActivityResultContracts.TakePicturePreview(), snapCallback); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_enrol); + + if (getIntent().hasExtra("chosenCourse")) receivedCourse = getIntent().getExtras().getString("chosenCourse"); + if (getIntent().hasExtra("Un_enrolled")) un_enrolled = (String[][]) getIntent().getExtras().get("Un_enrolled"); + mCourseViewModel = new ViewModelProvider(this).get(CourseViewModel.class); + LiveData> allCoursesLiveData = mCourseViewModel.getAllAgentCourses(); + + coursesList = new LinkedList<>(); + + admNoView = findViewById(R.id.adm_No); + coursesSpinner = findViewById(R.id.coursesSpinnerEnrol); + selectedCourses = findViewById(R.id.selectedCourses); + snapButton = findViewById(R.id.captureIDbutton); + deptSpinner = findViewById(R.id.deptSpinnerEnrol); + facultySpinner = findViewById(R.id.facultySpinnerEnrol); + mToolbar = findViewById(R.id.toolbar); + setSupportActionBar(mToolbar); + mToolbar.setBackgroundResource(R.color.black); + mToolbar.setSubtitle("New Enrollment"); + facsMap = new HashMap<>(); + MODE = NEW; + attLiveData = mCourseViewModel.getAllAttendanceCourses(); + + attLiveData.observe(enrolActivity.this, attendanceCourses1 -> attendanceCourses = attendanceCourses1); + + + ArrayAdapter facSpinnerAdapter = new ArrayAdapter<>(enrolActivity.this, R.layout.faculty_spinner_text); + deptSpinnerAdapter = new ArrayAdapter<>(enrolActivity.this, R.layout.dept_spinner_text); + coursesAdapter = new ArrayAdapter<>(enrolActivity.this, R.layout.spinner_text); + + facultySpinner.setAdapter(facSpinnerAdapter); + facSpinnerAdapter.insert("Select faculty", 0); + + deptSpinnerAdapter.add("Select department"); + deptSpinner.setAdapter(deptSpinnerAdapter); + deptSpinnerAdapter.setNotifyOnChange(true); + + coursesAdapter.add("Select course"); + coursesSpinner.setAdapter(coursesAdapter); + coursesAdapter.setNotifyOnChange(true); + + facultySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + if (i>0){ + if (facsMap.containsKey((String) facultySpinner.getAdapter().getItem(i))){ + selectedFacultyMap = facsMap.get((String) facultySpinner.getAdapter().getItem(i)); + + Set deptsSet = selectedFacultyMap.keySet(); + deptSpinnerAdapter.clear(); + deptSpinnerAdapter.add("Select Department"); + deptSpinnerAdapter.addAll(deptsSet); + deptSpinnerAdapter.notifyDataSetChanged(); + + } + + } + } + + @Override + public void onNothingSelected(AdapterView adapterView) { + + } + }); + deptSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + if(i>0){ + if (selectedFacultyMap.containsKey(deptSpinnerAdapter.getItem(i))) { + selectedDeptCourses = selectedFacultyMap.get(deptSpinnerAdapter.getItem(i)); + coursesAdapter.clear(); + coursesAdapter.add("Select Course"); + coursesAdapter.addAll(selectedDeptCourses); + coursesAdapter.notifyDataSetChanged(); + } + } + } + + @Override + public void onNothingSelected(AdapterView adapterView) { + + } + }); + coursesSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (position>0) { + chosenCourse = selectedDeptCourses.get(position-1); + String viewText = selectedCourses.getText().toString(); + if (!viewText.isEmpty() && !coursesList.contains(chosenCourse)) { + String concat = viewText + " - " + chosenCourse; + selectedCourses.setText(concat); + coursesList.add(chosenCourse); + } else if (coursesList.isEmpty()) { + selectedCourses.setText(chosenCourse); + coursesList.add(chosenCourse); + } else if (viewText.contains(chosenCourse)) { + Toast.makeText(enrolActivity.this, "Course has already been selected", Toast.LENGTH_LONG).show(); + } + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + + allCoursesLiveData.observe(this, agentCourses -> { + allCourses = agentCourses; + initMaps(); + + ArrayList facs = new ArrayList<>(); + for (Object obj : facsMap.keySet()) { + facs.add(obj.toString()); + } + facSpinnerAdapter.addAll(facs); + facSpinnerAdapter.notifyDataSetChanged(); + }); + selectedCourses.setOnLongClickListener(view -> { + selectedCourses.setText(""); + return true; + }); + snapButton.setOnClickListener(view -> { + if (MODE.equals(NEW)) { + if (admNoView.getText().length() != 0 && selectedCourses.getText().length() != 0 /*&& !pb_is_Visible*/) { + admNo = admNoView.getText().toString(); + snapLauncher.launch(null); + } else { + Toast.makeText(this, "Admission number or courses field empty!", Toast.LENGTH_SHORT).show(); + } + + }else if (MODE.equals(ADD)) { + if (admNoView.getText().length() != 0 && selectedCourses.getText().length() != 0) { + admNo = admNoView.getText().toString(); + addCourses(); + }else { + Toast.makeText(this, "Admission number or courses field empty!", Toast.LENGTH_SHORT).show(); + } + } + }); + + /* admNoView.setOnEditorActionListener((textView, i, keyEvent) -> { + if (!statusView.getText().toString().isEmpty()) {statusView.setText(""); return true;} + return false; + });*/ + + mToolbar.setOnMenuItemClickListener(onMenuItemClick); + + } + private final Toolbar.OnMenuItemClickListener onMenuItemClick = menuItem -> { + + switch (menuItem.getTitle().toString()){ + case "New": + mToolbar.setSubtitle("New Enrollment"); + mToolbar.setSubtitleTextColor(Color.WHITE); + snapButton.setText(R.string.capture_id); + MODE = NEW; + break; + case "Add": + mToolbar.setSubtitle("Add courses"); + snapButton.setText("Add"); + mToolbar.setSubtitleTextColor(Color.WHITE); + MODE = ADD; + break; + } + return true; + }; + private void initMaps() { + Set facSet = new HashSet<>(); + Set depSet = new HashSet<>(); + for (AgentCourse course : allCourses) { + facSet.add(course.getFaculty()); + depSet.add(course.getDept()); + } + Map>> facMap = new HashMap<>(); + + for (String fac : facSet) { + Map> deptMap = new HashMap<>(); + for (String dep : depSet) { + ArrayList deptCourses = new ArrayList<>(); + for (AgentCourse course : allCourses) { + if (dep.equals(course.getDept()) && fac.equals(course.getFaculty())){ + deptCourses.add(course.getCourse()); + } + } + + if (deptCourses.size()>0) deptMap.put(dep, deptCourses); + } + facMap.put(fac, deptMap); + } + + facsMap = facMap; + } + + + private void processResult(Object[] resultObj){ + isFirst = (boolean) resultObj[1]; + if ((int) resultObj[2] < 0) { + mCourseViewModel.insert(new EnrolCourse(Arrays.toString(coursesList.toArray()), + admNoView.getText().toString(), + (byte[]) resultObj[0], + EnrolCourse.Sub_Status.unsubmitted, + IDCardbytes)); + + } else { + mCourseViewModel.insert(new EnrolCourse(Arrays.toString(coursesList.toArray()), + admNoView.getText().toString(), + (byte[]) resultObj[0], + EnrolCourse.Sub_Status.unsubmitted, + IDCardbytes)); + String date = null; + int period = 0; + if (receivedCourse != null) + for (AttendanceCourse course: attendanceCourses) { + if ((int) resultObj[2] == course.getId()){ + date = course.getDate(); + period = course.getPeriod(); + } + } + if (period>0){ mCourseViewModel.update(new AttendanceCourse((int) resultObj[2], + receivedCourse, + admNo, + (byte[]) resultObj[0], + AttendanceCourse.Sub_Status.unsubmitted, + AttendanceCourse.Cap_Status.captured, date, period)); + } + } + mToolbar.setSubtitle("Success!"); + mToolbar.setSubtitleTextColor(Color.GREEN); + coursesList.clear(); + + } + private void launchFPScan(){ + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setPackage("com.fgtit.reader"); + if(intent.resolveActivity(enrolActivity.this.getPackageManager())!=null) { + } + if (un_enrolled!=null && isFirst) intent.putExtra("un_enrolled", un_enrolled); + if (!getAddress().equals("No_Address")) intent.putExtra("bt_Address", getAddress()); + intent.putExtra("Enrol student", true); + scanLauncher.launch(intent); + } + public void saveAddress(String mAddress) { + SharedPreferences sharedPref = getSharedPreferences(MainActivity.AddressFile, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString("Address", mAddress); + editor.apply(); + } + private String getAddress(){ + SharedPreferences preferences = this.getSharedPreferences(MainActivity.AddressFile, MODE_PRIVATE); + String defaultAddress = "No_Address"; + return preferences.getString("Address", defaultAddress); + } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + private void addCourses(){ + mCourseViewModel.insert(new EnrolCourse(Arrays.toString(coursesList.toArray()), + admNo, + null, + EnrolCourse.Sub_Status.unsubmitted, + null)); + admNoView.setText(""); + selectedCourses.setText(""); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/ui/Contact_Us/ContactUsFragment.java b/app/src/main/java/com/example/attendancemgr/ui/Contact_Us/ContactUsFragment.java new file mode 100644 index 0000000..e92eb81 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/ui/Contact_Us/ContactUsFragment.java @@ -0,0 +1,76 @@ +package com.example.attendancemgr.ui.Contact_Us; + +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.example.attendancemgr.R; + +import java.util.ArrayList; +import java.util.List; + +/** + * A fragment representing a list of Items. + */ +public class ContactUsFragment extends Fragment { + + private static final String ARG_COLUMN_COUNT = "column-count"; + private int mColumnCount = 1; + List contacts; + String[] channels = {"Phone:", "Email:", "WhatsApp:"}; + String[] ids = {"08036110256", "helpdesk@Atenda.com.ng", "08036110256"}; + + /** + * Mandatory empty constructor for the fragment manager to instantiate the + * fragment (e.g. upon screen orientation changes). + */ + public ContactUsFragment() { + } + + @SuppressWarnings("unused") + public static ContactUsFragment newInstance(int columnCount) { + ContactUsFragment fragment = new ContactUsFragment(); + Bundle args = new Bundle(); + args.putInt(ARG_COLUMN_COUNT, columnCount); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + contacts = new ArrayList<>(); + for (int i = 0; i { + + private final List mValues; + + public MyContactUsRecyclerViewAdapter(List items) { + mValues = items; + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.fragment_contact_us, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(final ViewHolder holder, int position) { + holder.mItem = mValues.get(position); + holder.mIdView.setText(mValues.get(position)[0]); + holder.mContentView.setText(mValues.get(position)[1]); + } + + @Override + public int getItemCount() { + return mValues.size(); + } + + public class ViewHolder extends RecyclerView.ViewHolder { + public final View mView; + public final TextView mIdView; + public final TextView mContentView; + public String[] mItem; + + public ViewHolder(View view) { + super(view); + mView = view; + mIdView = (TextView) view.findViewById(R.id.item_number); + mContentView = (TextView) view.findViewById(R.id.content); + } + + @Override + public String toString() { + return super.toString() + " '" + mContentView.getText() + "'"; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/ui/CourseListAdapter.java b/app/src/main/java/com/example/attendancemgr/ui/CourseListAdapter.java new file mode 100644 index 0000000..1917f3c --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/ui/CourseListAdapter.java @@ -0,0 +1,101 @@ +package com.example.attendancemgr.ui; + +import androidx.recyclerview.widget.RecyclerView; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.example.attendancemgr.R; +import com.example.attendancemgr.database.AgentCourse; + +import java.util.List; + +public class CourseListAdapter extends RecyclerView.Adapter { + private List mCourseList; + private final LayoutInflater mInflater; + + class courseViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + + public final TextView courseItemView; + final CourseListAdapter mAdapter; + + /** + * Creates a new custom view holder to hold the view to display in the RecyclerView. + * + * @param itemView The view in which to display the data. + * @param adapter The adapter that manages the the data and views for the RecyclerView. + */ + public courseViewHolder(View itemView, CourseListAdapter adapter) { + super(itemView); + courseItemView = (TextView) itemView.findViewById(R.id.course); + this.mAdapter = adapter; + itemView.setOnClickListener(this); + } + + @Override + public void onClick(View v) { + // All we do here is prepend "Clicked! " to the text in the view, to verify that + // the correct item was clicked. The underlying data does not change. + courseItemView.setText ("Clicked! "+ courseItemView.getText()); + mCourseList.remove(v.getId()); // remove course on click + + + } + } + + public CourseListAdapter(Context context, List courseList) { + mInflater = LayoutInflater.from(context); + this.mCourseList = courseList; + } + + public void setAgentCourses(List courses) { + mCourseList = courses; + notifyDataSetChanged(); + } + /** + * Inflates an item view and returns a new view holder that contains it. + * Called when the RecyclerView needs a new view holder to represent an item. + * + * @param parent The view group that holds the item views. + * @param viewType Used to distinguish views, if more than one type of item view is used. + * @return a view holder. + */ + @Override + public courseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + // Inflate an item view. + View mItemView = mInflater.inflate(R.layout.courselist_item, parent, false); + return new courseViewHolder(mItemView, this); + } + + /** + * Sets the contents of an item at a given position in the RecyclerView. + * Called by RecyclerView to display the data at a specificed position. + * + * @param holder The view holder for that position in the RecyclerView. + * @param position The position of the item in the RecycerView. + */ + @Override + public void onBindViewHolder(courseViewHolder holder, int position) { + // Retrieve the data for that position. + AgentCourse mCurrent = mCourseList.get(position); + // Add the data to the view holder. + holder.courseItemView.setText(mCurrent.getCourse()); + + + } + + /** + * Returns the size of the container that holds the data. + * + * @return Size of the list of data. + */ + @Override + public int getItemCount() { + return mCourseList.size(); + } +} + + diff --git a/app/src/main/java/com/example/attendancemgr/ui/Courses/CoursesFragment.java b/app/src/main/java/com/example/attendancemgr/ui/Courses/CoursesFragment.java new file mode 100644 index 0000000..54d566e --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/ui/Courses/CoursesFragment.java @@ -0,0 +1,650 @@ +package com.example.attendancemgr.ui.Courses; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.util.Base64; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Filter; +import android.widget.Filterable; +import android.widget.ProgressBar; +import android.widget.SearchView; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.activity.result.ActivityResult; +import androidx.activity.result.ActivityResultCallback; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.example.attendancemgr.R; +import com.example.attendancemgr.ScanTutor; +import com.example.attendancemgr.data.model.DepartmentsModel; +import com.example.attendancemgr.database.AgentCourse; +import com.example.attendancemgr.database.AttendanceCourse; +import com.example.attendancemgr.database.CourseViewModel; +import com.example.attendancemgr.enrolActivity; +import com.google.android.material.snackbar.Snackbar; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; + +import static android.content.Context.MODE_PRIVATE; +import static com.example.attendancemgr.MainActivity2.ATD_TUTOR_FP; +import static com.example.attendancemgr.MainActivity2.ATT_COURSE; +import static com.example.attendancemgr.MainActivity2.ATT_STDs_FPs; +import static com.example.attendancemgr.MainActivity2.ATT_STDs_IDs; +import static com.example.attendancemgr.MainActivity2.ATT_STDs_NOs; +import static com.example.attendancemgr.MainActivity2.NO_DATA; +import static com.example.attendancemgr.MainActivity2.STUDENT_MATCH; +import static com.example.attendancemgr.ui.login.LoginActivity.AddressFile; +import static com.example.attendancemgr.ui.login.LoginActivity.ID_FILE; +import static com.example.attendancemgr.ui.login.LoginActivity.USERNAME; + +public class CoursesFragment extends Fragment { + LiveData> orgLiveData; + private CourseViewModel mCourseViewModel; + List itemsModelList = new ArrayList<>(); + String chosenCourse; + RecyclerAdapter mAdapter; + String mDep; + RecyclerView mRecyclerView; + ProgressBar pb; + AgentCourse openedCourse; + boolean isDownloading; + private List mAgentCourses; + private List attendanceCourses; + public static final int TUTOR_OK = 234; + private final int ATT_OK = 432; + private LinkedList IDs; + private LinkedList AdmNos; + + LiveData> attLiveData; + private LinkedList studentsFP, lecturersFP; + + Bundle args; + ActivityResultCallback tutorScanCallback = result -> { + if (result.getResultCode() == TUTOR_OK){ + if(result.getData().hasExtra("period")){ + + savePeriod(result.getData().getExtras().getInt("period")); + for (AgentCourse course : mAgentCourses) { + if (course.getCourse().equals(chosenCourse)) { + course.setAtt_status(AgentCourse.Att_Status.open); + openedCourse = course; + } + break; + } + getAtt_EnrolData(null); + } + } + }; + + + private ActivityResultCallback attCallback = result -> { + + //Pattern pat; + //Matcher mat; + if(result.getResultCode() == ATT_OK){ + if(result.getData().hasExtra(ATT_STDs_NOs)) { + for (String data : result.getData().getExtras().getStringArrayList(ATT_STDs_NOs)) { + if (data.length() > 20) { + // ga wanda ya fara daukar attendance na farko kafin enrollment + mCourseViewModel.insert(new AttendanceCourse(chosenCourse, + null, + Base64.decode(data, Base64.DEFAULT), + AttendanceCourse.Sub_Status.unsubmitted, + AttendanceCourse.Cap_Status.captured, + new SimpleDateFormat("yyyyMMdd").format(new Date()), getPeriod())); + } else { + /* pat = Pattern.compile(data); + mat = pat.matcher("&"); + if (mat.find()) {*/ + String[] admNo_id = data.split("&"); + // for the enrolled + + mCourseViewModel.update(new AttendanceCourse(Integer.parseInt(admNo_id[0]), + chosenCourse, + admNo_id[1], + null, + AttendanceCourse.Sub_Status.unsubmitted, + AttendanceCourse.Cap_Status.captured, + new SimpleDateFormat("yyyyMMdd").format(new Date()), getPeriod())); + + /*}else { + mCourseViewModel.insert(new AttendanceCourse(chosenCourse, data, null, AttendanceCourse.Sub_Status.unsubmitted, AttendanceCourse.Cap_Status.captured)); + }*/ + } + } + if (openedCourse != null ){ + openedCourse.setAtt_status(AgentCourse.Att_Status.closed); + mCourseViewModel.update(openedCourse); + } + + if (result.getData().hasExtra("BTAddress")) saveAddress((String) result.getData().getExtras().get("BTAddress")); + }} + }; + + + ActivityResultLauncher attLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), attCallback); + + ActivityResultLauncher tutorScanLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), tutorScanCallback); + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + args = getArguments(); + mCourseViewModel = new ViewModelProvider(this).get(CourseViewModel.class); + + orgLiveData = mCourseViewModel.getAllAgentCourses(); + attLiveData = mCourseViewModel.getAllAttendanceCourses(); + + lecturersFP = new LinkedList<>(); + AdmNos = new LinkedList<>(); + studentsFP = new LinkedList<>(); + IDs = new LinkedList<>(); + } + + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.courses, container, false); + mRecyclerView = root.findViewById(R.id.coursesRecycler); + SearchView searchView = root.findViewById(R.id.coursesSearchView); + pb = root.findViewById(R.id.coursesProgressBar); + pb.setVisibility(View.INVISIBLE); + mDep = args.getString("dep"); + mAdapter = new RecyclerAdapter(getContext(), itemsModelList); + attLiveData.observe(getViewLifecycleOwner(), attendanceCourses1 -> attendanceCourses = attendanceCourses1); + orgLiveData.observe(getViewLifecycleOwner(), agentCourses -> { + mAgentCourses = agentCourses; + if(agentCourses.size()>0) { + if (mDep == null) { + itemsModelList.clear(); + Set courseSet = new HashSet<>(); + for (AgentCourse course : agentCourses) { + courseSet.add(course.getCourse()); + } + + for (String course : courseSet) { + itemsModelList.add(new DepartmentsModel(course)); + } + mAdapter.notifyDataSetChanged(); + } else { + itemsModelList.clear(); + Set courseSet = new HashSet<>(); + + for (AgentCourse course : agentCourses) { + if (course.getDept().equals(mDep)) + courseSet.add(course.getCourse()); + } + + for (String course : courseSet) { + itemsModelList.add(new DepartmentsModel(course)); + } + mAdapter.notifyDataSetChanged(); + } + } + + }); + mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + + mRecyclerView.setAdapter(mAdapter); + searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + + @Override + public boolean onQueryTextSubmit(String query) { + + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + mAdapter.getFilter().filter(newText); + return true; + } + + }); + + return root; + } + public void saveAddress(String mAddress) { + SharedPreferences sharedPref = getActivity().getSharedPreferences(AddressFile, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString("Address", mAddress); + + editor.apply(); + } + class RecyclerAdapter extends RecyclerView.Adapter implements Filterable { + private final List departmentsModelList; + private LayoutInflater mInflater; + private List departmentsModelListFiltered; + RecyclerAdapter(Context context, List departmentsModelList) { + this.departmentsModelList = departmentsModelList; + this.departmentsModelListFiltered = departmentsModelList; + mInflater = LayoutInflater.from(context); + } + + + class ItemsViewHolder extends RecyclerView.ViewHolder{ + final CoursesFragment.RecyclerAdapter mAdapter; + TextView myDept; + + + ItemsViewHolder(View itemView, CoursesFragment.RecyclerAdapter adapter) { + super(itemView); + myDept = itemView.findViewById(R.id.deptName); + this.mAdapter = adapter; + } + } + @NonNull + @Override + public CoursesFragment.RecyclerAdapter.ItemsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View mItemView = mInflater.inflate(R.layout.depts_list_view, parent, false); + return new CoursesFragment.RecyclerAdapter.ItemsViewHolder(mItemView,this); + + + } + + @Override + public void onBindViewHolder(@NonNull CoursesFragment.RecyclerAdapter.ItemsViewHolder holder, int position) { + final String mDept = departmentsModelListFiltered.get(position).getmDept(); + + holder.myDept.setText(mDept); + + holder.itemView.setOnClickListener(v -> { + chosenCourse = holder.myDept.getText().toString(); + if (isRecordSubmitted()) { + if (!isDownloading) { + final AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setMessage("Fetch course data?"); + builder.setPositiveButton("Yes", (dialog, which) -> { + if (!getUsername().equals(NO_DATA)) getCourseData(getUsername()); + dialog.cancel(); + }); + + builder.setNegativeButton("No", (dialog, which) -> dialog.cancel()); + + AlertDialog dialog = builder.create(); + + dialog.show(); + } else { + Toast.makeText(getContext(), "Please wait...", Toast.LENGTH_SHORT).show(); + } + } else { + Toast.makeText(getContext(), "Please submit record first!", Toast.LENGTH_SHORT).show(); + } + }); + + } + + @Override + public int getItemCount() { + return departmentsModelListFiltered !=null? departmentsModelListFiltered.size():0; + } + + @Override + public Filter getFilter() { + + Filter filter = new Filter() { + @Override + protected FilterResults performFiltering(CharSequence constraint) { + FilterResults filterResults = new FilterResults(); + + if (constraint == null || constraint.length() == 0) { + filterResults.count = departmentsModelList.size(); + filterResults.values = departmentsModelList; + } else { + String searchStr = constraint.toString().toLowerCase(); + List resultData = new ArrayList<>(); + + for (DepartmentsModel item : departmentsModelList) { + if (item.getmDept().toLowerCase().contains(searchStr)) { + resultData.add(item); + } + + filterResults.count = resultData.size(); + filterResults.values = resultData; + } + } + return filterResults; + } + + @Override + protected void publishResults(CharSequence constraint, FilterResults results) { + departmentsModelListFiltered = (List) results.values; + notifyDataSetChanged(); + } + }; + return filter; + } + + } + private String[][] getAllUn_enrolledAttendees(){ + String[][] temp = new String[attendanceCourses.size()][]; + int count = 0; + for (int j=0; j0) return temp; + return new String[0][0]; + } + private boolean prepareAttData(){ + + int count2 = 0; + for (AttendanceCourse course : attendanceCourses) { + if (course.getCap_status().equals(AttendanceCourse.Cap_Status.uncaptured)) { + studentsFP.add(course.getFP()); + AdmNos.add(course.getAdmNo()); + IDs.add(course.getId()); + + } + } + SharedPreferences preferences = getActivity().getSharedPreferences("Tutors", MODE_PRIVATE); + String defaultFP = ""; + String tutorsFP = preferences.getString("tutorsFP", defaultFP); + String[] tutorsFPArray = tutorsFP.split(" "); + for (String fp : tutorsFPArray) { + lecturersFP.add(Base64.decode(fp, Base64.DEFAULT)); + count2++; + } + + return count2>0; + } + private boolean isRecordSubmitted() { + if (attendanceCourses != null) { + for (AttendanceCourse course : attendanceCourses) { + if (course.getCourse().equals(chosenCourse) && course.getSub_Status() == AttendanceCourse.Sub_Status.unsubmitted && course.getCap_status().equals(AttendanceCourse.Cap_Status.captured)) + return false; + } + } + return true; + } + private void getAtt_EnrolData(Integer position) { + final AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setMessage("What would you like to do?"); + //builder.setTitle("Register Entry or Add Fingerprint"); + + builder.setPositiveButton("Enrol Student", (dialog, which) -> { + String[][] un_enrolled = getAllUn_enrolledAttendees(); + Intent intent = new Intent(getContext(), enrolActivity.class); + if (un_enrolled.length!=0) intent.putExtra("Un_enrolled", un_enrolled); + intent.putExtra("chosenCourse", chosenCourse); + startActivity(intent); + }); + + builder.setNegativeButton("Take attendance", (dialog, which) -> { + if (position==null || mAgentCourses.get(position).getAtt_status().equals(AgentCourse.Att_Status.open)) { + if (prepareAttData()) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setPackage("com.fgtit.reader"); + if (!getAddress().equals("No_Address")) + intent.putExtra("bt_Address", getAddress()); + + intent.putExtra(ATT_COURSE, chosenCourse); + intent.putExtra(ATD_TUTOR_FP, lecturersFP.toArray()); + if (studentsFP != null) intent.putExtra(ATT_STDs_FPs, studentsFP.toArray()); + if (AdmNos != null) intent.putExtra(ATT_STDs_NOs, AdmNos.toArray()); + if (IDs != null) intent.putExtra(ATT_STDs_IDs, IDs.toArray()); + intent.putExtra(STUDENT_MATCH, true); + + attLauncher.launch(intent); + dialog.cancel(); + } else { + Toast.makeText(getContext(), "An error has occured!", Toast.LENGTH_LONG).show(); + } + } else { + Toast.makeText(getContext(), "Attendance is closed for the course.", Toast.LENGTH_SHORT).show(); + } + }); + AlertDialog dialog = builder.create(); + dialog.show(); + } + private String getAddress(){ + SharedPreferences preferences = getActivity().getSharedPreferences(AddressFile, MODE_PRIVATE); + String defaultAddress = "No_Address"; + return preferences.getString("Address", defaultAddress); + } + public LinkedList getLecturerFP() { + SharedPreferences preferences = getActivity().getSharedPreferences("Tutors", MODE_PRIVATE); + String defaultData = "No_FP"; + LinkedList lecturerFP = new LinkedList<>(); + String fpStrings = preferences.getString("tutorsFP", defaultData); + if (!fpStrings.equals(defaultData)) { + String[] fpStringsArray = fpStrings.split(" "); + for (String fp : fpStringsArray) { + lecturerFP.add(Base64.decode(fp, Base64.DEFAULT)); + } + } + return lecturerFP; + } + private void scanTutorFP() { + Intent intent = new Intent(getActivity(), ScanTutor.class); + //todo + intent.putExtra("passcode", getPassCode()); + tutorScanLauncher.launch(intent); + + } + + private String getPassCode() { + SharedPreferences preferences = getActivity().getSharedPreferences("pass code file", MODE_PRIVATE); + String defaultCode = "No_code"; + return preferences.getString("passcode", defaultCode); + } + + private void getCourseData(String Username) { + class DownloadFP extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + pb.setVisibility(View.VISIBLE); + isDownloading = true; + } + + @Override + protected void onPostExecute(String data) { + super.onPostExecute(data); + if (data.equals("timeout")){ + Toast.makeText(getActivity(), "Connection timeout!", Toast.LENGTH_SHORT).show(); + } else if (!data.isEmpty()){ + try { + storeAttendanceData(data); + } catch (JSONException e) { + e.printStackTrace(); + } + scanTutorFP(); + } + else { + Toast.makeText(getActivity(), "Lecturer or course details incomplete!", Toast.LENGTH_SHORT).show(); + + } + pb.setVisibility(View.INVISIBLE); + isDownloading = false; + + } + + @Override + protected String doInBackground(Void... voids) { + + String login_url = "http://www.gstcbunza2012.org.ng/fas/fetch_data.php"; + try { + java.net.URL url = new URL(login_url); + try { + HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); + httpURLConnection.setRequestMethod("POST"); + httpURLConnection.setReadTimeout(15000); + httpURLConnection.setConnectTimeout(15000); + httpURLConnection.setDoInput(true); + httpURLConnection.setDoOutput(true); + + OutputStream outputStream = httpURLConnection.getOutputStream(); + BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8")); + String post_data = URLEncoder.encode("course", "UTF-8") + "=" + URLEncoder.encode(chosenCourse, "UTF-8") + + "&" + URLEncoder.encode("uname", "UTF-8") + "=" + URLEncoder.encode(Username, "UTF-8"); + bufferedWriter.write(post_data); + bufferedWriter.flush(); + bufferedWriter.close(); + + InputStream inputStream = httpURLConnection.getInputStream(); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "iso-8859-1")); + StringBuilder result = new StringBuilder(); + String line; + new Timer().schedule(new TimerTask() { + @RequiresApi(api = Build.VERSION_CODES.O) + @Override + public void run() { + if (inputStream == null){ + onPostExecute("timeout"); + httpURLConnection.disconnect(); + + } + } + + }, 15000); + while ((line = bufferedReader.readLine()) != null) { + result.append(line); + } + String rslt = result.toString().trim(); + if (!rslt.isEmpty() & rslt.length()>20) { + return rslt; + + } + bufferedReader.close(); + inputStream.close(); + httpURLConnection.disconnect(); + } catch (Exception e) { + e.printStackTrace(); + } + + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + } + DownloadFP downloadFP = new DownloadFP(); + if (isWifiCxd()) { + downloadFP.execute(); + } else { + Snackbar.make(getActivity(), mRecyclerView, "No network connection", Snackbar.LENGTH_LONG); + } + } + private void storeAttendanceData(String data) throws JSONException { + JSONArray jsonArray = new JSONArray(data); + JSONObject tutorFP; + + StringBuilder builder = new StringBuilder(); + String tutorFPStrings; + + // JSONObject object = jsonArray.getJSONObject(i); + JSONArray tutorsArray = jsonArray.getJSONArray(0); + + for (int j=0; j mText; + + public CoursesViewModel() { + mText = new MutableLiveData<>(); + mText.setValue("This is gallery fragment"); + } + + public LiveData getText() { + return mText; + } + public void setText(MutableLiveData text){ + mText = text; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/ui/Departments/DepartmentsFragment.java b/app/src/main/java/com/example/attendancemgr/ui/Departments/DepartmentsFragment.java new file mode 100644 index 0000000..3104fbf --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/ui/Departments/DepartmentsFragment.java @@ -0,0 +1,219 @@ +package com.example.attendancemgr.ui.Departments; + +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Color; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Filter; +import android.widget.Filterable; +import android.widget.SearchView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.ViewModelProvider; +import androidx.navigation.NavController; +import androidx.navigation.fragment.NavHostFragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.example.attendancemgr.R; +import com.example.attendancemgr.data.model.DepartmentsModel; +import com.example.attendancemgr.database.AgentCourse; +import com.example.attendancemgr.database.CourseViewModel; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static android.content.Context.MODE_PRIVATE; +import static com.example.attendancemgr.MainActivity2.AVAILABLE_JOBS; +import static com.example.attendancemgr.MainActivity2.JOBS_FILE; +import static com.example.attendancemgr.MainActivity2.NO_DATA; + +public class DepartmentsFragment extends Fragment { + + LiveData> orgLiveData; + private CourseViewModel mCourseViewModel; + List itemsModelList = new ArrayList<>(); + RecyclerAdapter mAdapter; + String mFac; + NavController controller; + + Bundle args; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + args = getArguments(); +// NavDestination destination = Navigation.findNavController(getView()).getGraph().findNode(R.id.courses); + + + mCourseViewModel = new ViewModelProvider(this).get(CourseViewModel.class); + orgLiveData = mCourseViewModel.getAllAgentCourses(); + + NavHostFragment navHostFragment =(NavHostFragment) getActivity().getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment); + if (navHostFragment != null) controller = navHostFragment.getNavController(); + } + + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + + View root = inflater.inflate(R.layout.departments, container, false); + RecyclerView mRecyclerView = root.findViewById(R.id.deptsRecycler); + SearchView searchView = root.findViewById(R.id.deptsSearchView); + mAdapter = new RecyclerAdapter(getContext(), itemsModelList); + mFac = args.getString("fac"); + orgLiveData.observe(getViewLifecycleOwner(), agentCourses -> { + if(agentCourses.size()>0) { + if (mFac == null) { + itemsModelList.clear(); + Set depSet = new HashSet<>(); + for (AgentCourse course : agentCourses) { + depSet.add(course.getDept()); + } + + for (String dep : depSet) { + itemsModelList.add(new DepartmentsModel(dep)); + } + mAdapter.notifyDataSetChanged(); + } else { + itemsModelList.clear(); + Set depSet = new HashSet<>(); + + for (AgentCourse course : agentCourses) { + if(course.getFaculty().equals(mFac)) + depSet.add(course.getDept()); + } + + for (String dep : depSet) { + itemsModelList.add(new DepartmentsModel(dep)); + } + mAdapter.notifyDataSetChanged(); + } + } + + }); + mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + + mRecyclerView.setAdapter(mAdapter); + + searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + + @Override + public boolean onQueryTextSubmit(String query) { + + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + mAdapter.getFilter().filter(newText); + return true; + } + + }); + + return root; + + } + private String getData(){ + SharedPreferences preferences = getActivity().getSharedPreferences(JOBS_FILE, MODE_PRIVATE); + return preferences.getString(AVAILABLE_JOBS, NO_DATA); + } + + class RecyclerAdapter extends RecyclerView.Adapter implements Filterable { + private final List departmentsModelList; + private LayoutInflater mInflater; + private List departmentsModelListFiltered; + RecyclerAdapter(Context context, List departmentsModelList) { + this.departmentsModelList = departmentsModelList; + this.departmentsModelListFiltered = departmentsModelList; + mInflater = LayoutInflater.from(context); + } + + + class ItemsViewHolder extends RecyclerView.ViewHolder{ + final RecyclerAdapter mAdapter; + TextView myDept; + + + ItemsViewHolder(View itemView, RecyclerAdapter adapter) { + super(itemView); + myDept = itemView.findViewById(R.id.deptName); + this.mAdapter = adapter; + } + } + @NonNull + @Override + public RecyclerAdapter.ItemsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View mItemView = mInflater.inflate(R.layout.depts_list_view, parent, false); + return new ItemsViewHolder(mItemView,this); + + + } + + @Override + public void onBindViewHolder(@NonNull RecyclerAdapter.ItemsViewHolder holder, int position) { + final String mDept = departmentsModelListFiltered.get(position).getmDept(); + + holder.myDept.setText(mDept); + + holder.itemView.setOnClickListener(v -> { + if (controller != null){ + Bundle bundle = new Bundle(); + bundle.putString("dep", holder.myDept.getText().toString()); + controller.navigate(R.id.courses, bundle); + } + }); + + } + + @Override + public int getItemCount() { + return departmentsModelListFiltered !=null? departmentsModelListFiltered.size():0; + } + + @Override + public Filter getFilter() { + + return new Filter() { + @Override + protected FilterResults performFiltering(CharSequence constraint) { + FilterResults filterResults = new FilterResults(); + + if (constraint == null || constraint.length() == 0) { + filterResults.count = departmentsModelList.size(); + filterResults.values = departmentsModelList; + } else { + String searchStr = constraint.toString().toLowerCase(); + List resultData = new ArrayList<>(); + + for (DepartmentsModel item : departmentsModelList) { + if (item.getmDept().toLowerCase().contains(searchStr)) { + resultData.add(item); + } + + filterResults.count = resultData.size(); + filterResults.values = resultData; + } + } + return filterResults; + } + + @Override + protected void publishResults(CharSequence constraint, FilterResults results) { + departmentsModelListFiltered = (List) results.values; + notifyDataSetChanged(); + } + }; + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/ui/Departments/DeptsViewModel.java b/app/src/main/java/com/example/attendancemgr/ui/Departments/DeptsViewModel.java new file mode 100644 index 0000000..bb91916 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/ui/Departments/DeptsViewModel.java @@ -0,0 +1,22 @@ +package com.example.attendancemgr.ui.Departments; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +public class DeptsViewModel extends ViewModel { + + private MutableLiveData mText; + + public DeptsViewModel() { + mText = new MutableLiveData<>(); + //mText.setValue("This is home fragment"); + } + + public LiveData getText() { + return mText; + } + public void setText(MutableLiveData text){ + this.mText = text; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/ui/Faculties/FacultiesFragment.java b/app/src/main/java/com/example/attendancemgr/ui/Faculties/FacultiesFragment.java new file mode 100644 index 0000000..aea5ed4 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/ui/Faculties/FacultiesFragment.java @@ -0,0 +1,209 @@ +package com.example.attendancemgr.ui.Faculties; + +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Color; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Filter; +import android.widget.Filterable; +import android.widget.SearchView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; +import androidx.navigation.fragment.NavHostFragment; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.example.attendancemgr.R; +import com.example.attendancemgr.data.model.DepartmentsModel; +import com.example.attendancemgr.database.AgentCourse; +import com.example.attendancemgr.database.CourseViewModel; +import com.example.attendancemgr.ui.Departments.DepartmentsFragment; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import static android.content.Context.MODE_PRIVATE; +import static com.example.attendancemgr.MainActivity2.COMPLETED_JOBS; +import static com.example.attendancemgr.MainActivity2.JOBS_FILE; +import static com.example.attendancemgr.MainActivity2.NO_DATA; + +public class FacultiesFragment extends Fragment { + + private FacultiesViewModel facultiesViewModel; + private CourseViewModel mCourseViewModel; + LiveData> orgLiveData; + RecyclerAdapter mAdapter; + NavController controller; + List itemsModelList = new ArrayList<>(); + Bundle args; + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + args = getArguments(); + + facultiesViewModel = + new ViewModelProvider(this).get(FacultiesViewModel.class); + mCourseViewModel = + new ViewModelProvider(this).get(CourseViewModel.class); + + + NavHostFragment navHostFragment =(NavHostFragment) getActivity().getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment); + if (navHostFragment != null) controller = navHostFragment.getNavController(); + } + + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + + View root = inflater.inflate(R.layout.faculties, container, false); + RecyclerView mRecyclerView = root.findViewById(R.id.facultiesRecycler); + SearchView searchView = root.findViewById(R.id.facultiesSearchView); + + orgLiveData = mCourseViewModel.getAllAgentCourses(); + mAdapter = new RecyclerAdapter(getContext(), itemsModelList); + + orgLiveData.observe(getActivity(), agentCourses -> { + if(agentCourses.size()>0) { + itemsModelList.clear(); + Set facSet = new HashSet<>(); + for (AgentCourse course : agentCourses) { + facSet.add(course.getFaculty()); + } + + for (String fac : facSet) { + itemsModelList.add(new DepartmentsModel(fac)); + } + mAdapter.notifyDataSetChanged(); + } + + }); + + mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + + mRecyclerView.setAdapter(mAdapter); + + searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + + @Override + public boolean onQueryTextSubmit(String query) { + + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + mAdapter.getFilter().filter(newText); + return true; + } + + }); + return root; + } + + class RecyclerAdapter extends RecyclerView.Adapter implements Filterable { + private final List departmentsModelList; + private LayoutInflater mInflater; + private List departmentsModelListFiltered; + RecyclerAdapter(Context context, List departmentsModelList) { + this.departmentsModelList = departmentsModelList; + this.departmentsModelListFiltered = departmentsModelList; + mInflater = LayoutInflater.from(context); + } + + + class ItemsViewHolder extends RecyclerView.ViewHolder{ + final FacultiesFragment.RecyclerAdapter mAdapter; + TextView myDept; + + ItemsViewHolder(View itemView, FacultiesFragment.RecyclerAdapter adapter) { + super(itemView); + myDept = itemView.findViewById(R.id.deptName); + this.mAdapter = adapter; + } + } + @NonNull + @Override + public RecyclerAdapter.ItemsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View mItemView = mInflater.inflate(R.layout.depts_list_view, parent, false); + return new FacultiesFragment.RecyclerAdapter.ItemsViewHolder(mItemView,this); + + + } + + @Override + public void onBindViewHolder(@NonNull FacultiesFragment.RecyclerAdapter.ItemsViewHolder holder, int position) { + final String mDept = departmentsModelListFiltered.get(position).getmDept(); + + holder.myDept.setText(mDept); + + holder.itemView.setOnClickListener(v -> { + if (controller != null){ + Bundle bundle = new Bundle(); + bundle.putString("fac", holder.myDept.getText().toString()); + controller.navigate(R.id.departments, bundle); + } + }); + + } + + @Override + public int getItemCount() { + return departmentsModelListFiltered !=null? departmentsModelListFiltered.size():0; + } + + @Override + public Filter getFilter() { + + return new Filter() { + @Override + protected FilterResults performFiltering(CharSequence constraint) { + FilterResults filterResults = new FilterResults(); + + if (constraint == null || constraint.length() == 0) { + filterResults.count = departmentsModelList.size(); + filterResults.values = departmentsModelList; + } else { + String searchStr = constraint.toString().toLowerCase(); + List resultData = new ArrayList<>(); + + for (DepartmentsModel item : departmentsModelList) { + if (item.getmDept().toLowerCase().contains(searchStr)) { + resultData.add(item); + } + + filterResults.count = resultData.size(); + filterResults.values = resultData; + } + } + return filterResults; + } + + @Override + protected void publishResults(CharSequence constraint, FilterResults results) { + departmentsModelListFiltered = (List) results.values; + notifyDataSetChanged(); + } + }; + } + + } + private String getData(){ + SharedPreferences preferences = getActivity().getSharedPreferences(JOBS_FILE, MODE_PRIVATE); + return preferences.getString(COMPLETED_JOBS, NO_DATA); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/ui/Faculties/FacultiesViewModel.java b/app/src/main/java/com/example/attendancemgr/ui/Faculties/FacultiesViewModel.java new file mode 100644 index 0000000..a5e4f63 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/ui/Faculties/FacultiesViewModel.java @@ -0,0 +1,22 @@ +package com.example.attendancemgr.ui.Faculties; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +public class FacultiesViewModel extends ViewModel { + + private MutableLiveData mText; + + public FacultiesViewModel() { + mText = new MutableLiveData<>(); + // mText.setValue("This is slideshow fragment"); + } + + public LiveData getText() { + return mText; + } + public void setText(MutableLiveData text){ + this.mText = text; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/ui/login/LoggedInUserView.java b/app/src/main/java/com/example/attendancemgr/ui/login/LoggedInUserView.java new file mode 100644 index 0000000..ce7008b --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/ui/login/LoggedInUserView.java @@ -0,0 +1,21 @@ +package com.example.attendancemgr.ui.login; + +/** + * Class exposing authenticated user details to the UI. + */ +class LoggedInUserView { + + private String mData; + + //... other data fields that may be accessible to the UI + LoggedInUserView(String data) { + + this.mData = data; + } + + + public String getmData() { + return mData; + } + +} diff --git a/app/src/main/java/com/example/attendancemgr/ui/login/LoginActivity.java b/app/src/main/java/com/example/attendancemgr/ui/login/LoginActivity.java new file mode 100644 index 0000000..c0bb19b --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/ui/login/LoginActivity.java @@ -0,0 +1,318 @@ +package com.example.attendancemgr.ui.login; + +import android.app.Activity; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.AsyncTask; +import android.os.Bundle; + +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.appcompat.app.AppCompatActivity; +import androidx.work.WorkManager; + +import android.text.Editable; +import android.text.TextWatcher; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ProgressBar; +import android.widget.Toast; + +import com.example.attendancemgr.MainActivity2; +import com.example.attendancemgr.R; +import com.example.attendancemgr.data.model.LoggedInUser; +import com.example.attendancemgr.database.AgentCourse; +import com.example.attendancemgr.database.AttendanceCourse; +import com.example.attendancemgr.database.CourseViewModel; +import com.example.attendancemgr.database.EnrolCourse; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.List; + +import static com.example.attendancemgr.data.LoginDataSource.DEFAULT_DISPLAY_NAME; + +public class LoginActivity extends AppCompatActivity { + private String isFirst, mUsername; + private LoginViewModel loginViewModel; + private CourseViewModel mCourseViewModel; + public static final String ID_FILE = "id file"; + public static final String USERNAME = "table id"; + ArrayList mCoursesData; + private LoggedInUser fetchedUser; + public static final String AddressFile = "bt_address_file"; + private ProgressBar loadingProgressBar; + private EditText usernameEditText; + private EditText passwordEditText; + private static final String VALID_USER = "valid user"; + private static final String INVALID_USER = "invalid user[]"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_login); + loginViewModel = new ViewModelProvider(this, new LoginViewModelFactory()) + .get(LoginViewModel.class); + mCourseViewModel = new ViewModelProvider(this).get(CourseViewModel.class); + + Thread t = new Thread(() -> isFirst = mCourseViewModel.getAnyCourse().length<1? "yes":"yes"); + t.start(); + usernameEditText = findViewById(R.id.username); + passwordEditText = findViewById(R.id.password); + final Button loginButton = findViewById(R.id.login); + loadingProgressBar = findViewById(R.id.loading); + + mCoursesData = new ArrayList<>(); + + loginViewModel.getLoginFormState().observe(this, loginFormState -> { + if (loginFormState == null) { + return; + } + loginButton.setEnabled(loginFormState.isDataValid()); + if (loginFormState.getUsernameError() != null) { + usernameEditText.setError(getString(loginFormState.getUsernameError())); + } + if (loginFormState.getPasswordError() != null) { + passwordEditText.setError(getString(loginFormState.getPasswordError())); + } + }); + + loginViewModel.getLoginResult().observe(this, loginResult -> { + if (loginResult == null) { + return; + } + if (loginResult.getError() != null) { + showLoginFailed(loginResult.getError()); + } + if (loginResult.getSuccess() != null) { + try { + updateUiWithUser(loginResult.getSuccess()); + } catch (JSONException e) { + e.printStackTrace(); + } + } + setResult(Activity.RESULT_OK); + + //Complete and destroy login activity once successful + }); + + TextWatcher afterTextChangedListener = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // ignore + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // ignore + } + + @Override + public void afterTextChanged(Editable s) { + loginViewModel.loginDataChanged(usernameEditText.getText().toString(), + passwordEditText.getText().toString()); + } + }; + usernameEditText.addTextChangedListener(afterTextChangedListener); + passwordEditText.addTextChangedListener(afterTextChangedListener); + passwordEditText.setOnEditorActionListener((v, actionId, event) -> { + if (actionId == EditorInfo.IME_ACTION_DONE) { + if (isWifiCxd()) { + loadingProgressBar.setVisibility(View.VISIBLE); + mUsername = usernameEditText.getText().toString(); + authUser(usernameEditText.getText().toString(), + passwordEditText.getText().toString(), isFirst); + } else { + Toast.makeText(this, "No network connection", Toast.LENGTH_SHORT).show(); + } + } + return false; + }); + + loginButton.setOnClickListener(v -> { + /* String[] days = {"Sunday","Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; + String[] months = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};*/ + if (isWifiCxd()) { + loadingProgressBar.setVisibility(View.VISIBLE); + mUsername = usernameEditText.getText().toString(); + authUser(usernameEditText.getText().toString(), + passwordEditText.getText().toString(), isFirst); + } else { + Toast.makeText(LoginActivity.this, "No network connection", Toast.LENGTH_SHORT).show(); + } + /* Calendar cal = Calendar.getInstance(); + Toast.makeText(this, days[cal.get(Calendar.DAY_OF_WEEK)-1], Toast.LENGTH_SHORT).show(); + +*/ + }); + } + + private void updateUiWithUser(LoggedInUserView model) throws JSONException { + if (model.getmData().equals(DEFAULT_DISPLAY_NAME)){ + Intent intent = new Intent(LoginActivity.this, MainActivity2.class); + startActivity(intent); + } else { + storeUsername(mUsername); + storeCoursesData(model.getmData()); + Intent intent = new Intent(LoginActivity.this, MainActivity2.class); + startActivity(intent); + } + resetTextfields(); + loadingProgressBar.setVisibility(View.GONE); + } + + private void resetTextfields() { + usernameEditText.setText(""); + passwordEditText.setText(""); + } + + + + private void storeCoursesData(String getmData) throws JSONException { + mCourseViewModel.deleteAllAgentCourses(); + JSONArray jsonArray = new JSONArray(getmData); + for (int i = 0 ; i < jsonArray.length(); i++) { + JSONObject obj = jsonArray.getJSONObject(i); + AgentCourse course = new AgentCourse(obj.getString("fac"), obj.getString("dept"), + obj.getString("course"), + AgentCourse.Att_Status.closed, + AgentCourse.Sub_Status.unsubmitted); + mCourseViewModel.insert(course); + } + + } + + private void storeUsername(String mUsername) { + SharedPreferences sharedPref = getSharedPreferences(ID_FILE, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString(USERNAME, mUsername); + editor.apply(); + } + + private void showLoginFailed(@StringRes Integer errorString) { + resetTextfields(); + Toast.makeText(getApplicationContext(), errorString, Toast.LENGTH_SHORT).show(); + + } + public boolean isWifiCxd() { + ConnectivityManager connMgr = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); + + return networkInfo!=null && networkInfo.isConnected(); + + } + private LoggedInUser authUser(String username, String password, String isFirst){ + class authUserAsynctask extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + } + + @Override + protected void onPostExecute(String userDetails) { + super.onPostExecute(userDetails); + + if (userDetails != null ) { + if (userDetails.equals(INVALID_USER)) { + showLoginFailed(R.string.login_failed); + } else if (userDetails.equals(VALID_USER)) { + fetchedUser = new LoggedInUser(DEFAULT_DISPLAY_NAME); + try { + updateUiWithUser(new LoggedInUserView(fetchedUser.getmData())); + } catch (JSONException e) { + e.printStackTrace(); + } + } else { + fetchedUser = new LoggedInUser(userDetails); + try { + updateUiWithUser(new LoggedInUserView(fetchedUser.getmData())); + } catch (JSONException e) { + //Toast.makeText(LoginActivity.this, "Invalid data format", Toast.LENGTH_SHORT).show(); + } + } + } + + resetTextfields(); + loadingProgressBar.setVisibility(View.GONE); + + } + + + @Override + protected String doInBackground(String... strings) { + String login_url = "http://www.gstcbunza2012.org.ng/fas/authentication.php"; + try { + java.net.URL url = new URL(login_url); + try { + HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); + httpURLConnection.setRequestMethod("POST"); + httpURLConnection.setReadTimeout(15000); + httpURLConnection.setConnectTimeout(15000); + httpURLConnection.setDoInput(true); + httpURLConnection.setDoOutput(true); + + OutputStream outputStream = httpURLConnection.getOutputStream(); + BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8")); + String post_data = URLEncoder.encode("uname", "UTF-8") + "=" + URLEncoder.encode(strings[0], "UTF-8") + "&" + + URLEncoder.encode("pword", "UTF-8") + "=" + URLEncoder.encode(strings[1], "UTF-8") + "&" + + URLEncoder.encode("first", "UTF-8") + "=" + URLEncoder.encode(strings[2], "UTF-8"); + bufferedWriter.write(post_data); + bufferedWriter.flush(); + bufferedWriter.close(); + + InputStream inputStream = httpURLConnection.getInputStream(); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "iso-8859-1")); + StringBuilder result = new StringBuilder(); + String line; + + while ((line = bufferedReader.readLine()) != null) { + result.append(line); + } + String rslt = result.toString().trim(); + + if (rslt.length() > 0) { + return rslt; + } + bufferedReader.close(); + inputStream.close(); + httpURLConnection.disconnect(); + } catch (Exception e) { + e.printStackTrace(); + } + + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + } + authUserAsynctask authUserAsynctask = new authUserAsynctask(); + authUserAsynctask.execute(username, password, isFirst); + return fetchedUser; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/ui/login/LoginFormState.java b/app/src/main/java/com/example/attendancemgr/ui/login/LoginFormState.java new file mode 100644 index 0000000..668ff8a --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/ui/login/LoginFormState.java @@ -0,0 +1,40 @@ +package com.example.attendancemgr.ui.login; + +import androidx.annotation.Nullable; + +/** + * Data validation state of the login form. + */ +class LoginFormState { + @Nullable + private Integer usernameError; + @Nullable + private Integer passwordError; + private boolean isDataValid; + + LoginFormState(@Nullable Integer usernameError, @Nullable Integer passwordError) { + this.usernameError = usernameError; + this.passwordError = passwordError; + this.isDataValid = false; + } + + LoginFormState(boolean isDataValid) { + this.usernameError = null; + this.passwordError = null; + this.isDataValid = isDataValid; + } + + @Nullable + Integer getUsernameError() { + return usernameError; + } + + @Nullable + Integer getPasswordError() { + return passwordError; + } + + boolean isDataValid() { + return isDataValid; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/ui/login/LoginResult.java b/app/src/main/java/com/example/attendancemgr/ui/login/LoginResult.java new file mode 100644 index 0000000..ae2b450 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/ui/login/LoginResult.java @@ -0,0 +1,31 @@ +package com.example.attendancemgr.ui.login; + +import androidx.annotation.Nullable; + +/** + * Authentication result : success (user details) or error message. + */ +class LoginResult { + @Nullable + private LoggedInUserView success; + @Nullable + private Integer error; + + LoginResult(@Nullable Integer error) { + this.error = error; + } + + LoginResult(@Nullable LoggedInUserView success) { + this.success = success; + } + + @Nullable + LoggedInUserView getSuccess() { + return success; + } + + @Nullable + Integer getError() { + return error; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/ui/login/LoginViewModel.java b/app/src/main/java/com/example/attendancemgr/ui/login/LoginViewModel.java new file mode 100644 index 0000000..c0c72b2 --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/ui/login/LoginViewModel.java @@ -0,0 +1,70 @@ +package com.example.attendancemgr.ui.login; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +import android.util.Patterns; + +import com.example.attendancemgr.data.LoginRepository; +import com.example.attendancemgr.data.Result; +import com.example.attendancemgr.data.model.LoggedInUser; +import com.example.attendancemgr.R; + +public class LoginViewModel extends ViewModel { + + private MutableLiveData loginFormState = new MutableLiveData<>(); + private MutableLiveData loginResult = new MutableLiveData<>(); + private LoginRepository loginRepository; + + LoginViewModel(LoginRepository loginRepository) { + this.loginRepository = loginRepository; + } + + LiveData getLoginFormState() { + return loginFormState; + } + + LiveData getLoginResult() { + return loginResult; + } + + public void login(String username, String password, String isFirst) { + // can be launched in a separate asynchronous job + Result result = loginRepository.login(username, password, isFirst); + + if (result instanceof Result.Success) { + LoggedInUser data = ((Result.Success) result).getData(); + loginResult.setValue(new LoginResult(new LoggedInUserView(data.getmData()))); + } else if(result instanceof Result.Error) { + loginResult.setValue(new LoginResult(R.string.login_failed)); + } + } + + public void loginDataChanged(String username, String password) { + if (!isUserNameValid(username)) { + loginFormState.setValue(new LoginFormState(R.string.invalid_username, null)); + } else if (!isPasswordValid(password)) { + loginFormState.setValue(new LoginFormState(null, R.string.invalid_password)); + } else { + loginFormState.setValue(new LoginFormState(true)); + } + } + + // A placeholder username validation check + private boolean isUserNameValid(String username) { + if (username == null) { + return false; + } + if (username.contains("@")) { + return Patterns.EMAIL_ADDRESS.matcher(username).matches(); + } else { + return !username.trim().isEmpty(); + } + } + + // A placeholder password validation check + private boolean isPasswordValid(String password) { + return password != null && password.trim().length() > 5; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/attendancemgr/ui/login/LoginViewModelFactory.java b/app/src/main/java/com/example/attendancemgr/ui/login/LoginViewModelFactory.java new file mode 100644 index 0000000..f0fad9a --- /dev/null +++ b/app/src/main/java/com/example/attendancemgr/ui/login/LoginViewModelFactory.java @@ -0,0 +1,26 @@ +package com.example.attendancemgr.ui.login; + +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; +import androidx.annotation.NonNull; + +import com.example.attendancemgr.data.LoginDataSource; +import com.example.attendancemgr.data.LoginRepository; + +/** + * ViewModel provider factory to instantiate LoginViewModel. + * Required given LoginViewModel has a non-empty constructor + */ +public class LoginViewModelFactory implements ViewModelProvider.Factory { + + @NonNull + @Override + @SuppressWarnings("unchecked") + public T create(@NonNull Class modelClass) { + if (modelClass.isAssignableFrom(LoginViewModel.class)) { + return (T) new LoginViewModel(LoginRepository.getInstance(new LoginDataSource())); + } else { + throw new IllegalArgumentException("Unknown ViewModel class"); + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_add_24dp.xml b/app/src/main/res/drawable-v24/ic_add_24dp.xml new file mode 100644 index 0000000..0258249 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_add_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_assignment_24.xml b/app/src/main/res/drawable/ic_baseline_assignment_24.xml new file mode 100644 index 0000000..a08ebc4 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_assignment_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_assignment_late_24.xml b/app/src/main/res/drawable/ic_baseline_assignment_late_24.xml new file mode 100644 index 0000000..891c2f1 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_assignment_late_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_assignment_turned_in_24.xml b/app/src/main/res/drawable/ic_baseline_assignment_turned_in_24.xml new file mode 100644 index 0000000..5c97f42 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_assignment_turned_in_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_check_circle_24.xml b/app/src/main/res/drawable/ic_baseline_check_circle_24.xml new file mode 100644 index 0000000..3f7fa6b --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_check_circle_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_pending_actions_24.xml b/app/src/main/res/drawable/ic_baseline_pending_actions_24.xml new file mode 100644 index 0000000..776ec02 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_pending_actions_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_post_add_24.xml b/app/src/main/res/drawable/ic_baseline_post_add_24.xml new file mode 100644 index 0000000..73cd739 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_post_add_24.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_baseline_warning_24.xml b/app/src/main/res/drawable/ic_baseline_warning_24.xml new file mode 100644 index 0000000..e81cdc5 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_warning_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_menu_slideshow.xml b/app/src/main/res/drawable/ic_menu_slideshow.xml new file mode 100644 index 0000000..5e9e163 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_slideshow.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/side_nav_bar.xml b/app/src/main/res/drawable/side_nav_bar.xml new file mode 100644 index 0000000..6d81870 --- /dev/null +++ b/app/src/main/res/drawable/side_nav_bar.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_enrol.xml b/app/src/main/res/layout/activity_enrol.xml new file mode 100644 index 0000000..6940268 --- /dev/null +++ b/app/src/main/res/layout/activity_enrol.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + +