diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index aaedc7e..896b9ba 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -42,4 +42,3 @@ jobs: path: app/build/outputs/apk/release/*.apk - \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 702a8cf..698474a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.quick_calculation"> + + android:exported="false" + android:screenOrientation="portrait" + android:windowSoftInputMode="adjustResize" /> + - - - \ No newline at end of file + + diff --git a/app/src/main/java/io/github/subhamtyagi/quickcalculation/LaunchActivity.java b/app/src/main/java/io/github/subhamtyagi/quickcalculation/LaunchActivity.java deleted file mode 100644 index 2c642ca..0000000 --- a/app/src/main/java/io/github/subhamtyagi/quickcalculation/LaunchActivity.java +++ /dev/null @@ -1,264 +0,0 @@ -package io.github.subhamtyagi.quickcalculation; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.Toast; - -import androidx.annotation.RequiresApi; -import androidx.appcompat.app.AppCompatActivity; - -import com.google.android.material.color.DynamicColors; - -import java.util.Calendar; - -import io.github.subhamtyagi.quickcalculation.utils.CrashUtils; -import io.github.subhamtyagi.quickcalculation.utils.SpUtil; -import io.github.subhamtyagi.quickcalculation.utils.Utils; - - -public class LaunchActivity extends AppCompatActivity { - - AlarmManager manager; - PendingIntent pendingIntent; - private Spinner operationsSpinner, timerSpinner; - private String operationName = "mix", timer = "30"; - private EditText lower1, lower2, upper1, upper2; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - new CrashUtils(getApplicationContext(), ""); - SpUtil.getInstance().init(this); - /*setTheme(Utils.getTheme(this));*/ - DynamicColors.applyToActivityIfAvailable(this); - setContentView(R.layout.activity_launch); - - operationsSpinner = findViewById(R.id.spinner_operator); - timerSpinner = findViewById(R.id.spinner_timer); - lower1 = findViewById(R.id.et_range1_lower); - lower2 = findViewById(R.id.et_range2_lower); - upper1 = findViewById(R.id.et_range1_upper); - upper2 = findViewById(R.id.et_range2_upper); - - lower1.setText(SpUtil.getInstance().getString(Utils.LOWER_1, "11")); - lower2.setText(SpUtil.getInstance().getString(Utils.LOWER_2, "11")); - upper1.setText(SpUtil.getInstance().getString(Utils.UPPER_1, "99")); - upper2.setText(SpUtil.getInstance().getString(Utils.UPPER_2, "99")); - - setAdapters(); - - operationsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView adapterView, View view, int position, long l) { - switch (position) { - case 0: - operationName = Utils.SUM; - break; - case 1: - operationName = Utils.SUBSTRACT; - break; - case 2: - operationName = Utils.MULTIPLICATION; - break; - case 3: - operationName = Utils.DIVISION; - break; - case 4: - operationName = Utils.SUM_SERIES; - break; - case 5: - operationName = Utils.SIMPLIFICATION; - break; - case 6: - operationName = Utils.SIMPLIFICATION_ADVANCE; - break; - default: - operationName = Utils.MIX; - - } - } - - @Override - public void onNothingSelected(AdapterView adapterView) { - - } - }); - - timerSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView adapterView, View view, int position, long l) { - switch (position) { - case 0: - timer = "30"; - break; - case 1: - timer = "45"; - break; - case 2: - timer = "60"; - break; - case 3: - timer = "90"; - break; - case 4: - timer = "120"; - break; - case 5: - timer = "300"; - break; - case 6: - timer = "600"; - break; - - } - } - - @Override - public void onNothingSelected(AdapterView adapterView) { - - } - }); - - findViewById(R.id.btn_start).setOnClickListener(view -> { - Intent i = new Intent(this, QuizActivity.class); - i.putExtra(Utils.OPERATIONS, operationName); - i.putExtra(Utils.TIME, timer); - String l1 = getValueOf(lower1), - l2 = getValueOf(lower2), - u2 = getValueOf(upper2), - u1 = getValueOf(upper1); - - if (l1 == null) { - lower1.requestFocus(); - } else if (u1 == null) { - upper1.requestFocus(); - } else if (l2 == null) { - lower2.requestFocus(); - } else if (u2 == null) { - upper2.requestFocus(); - } else { - int iL1 = Integer.parseInt(l1); - int iL2 = Integer.parseInt(l2); - int iU1 = Integer.parseInt(u1); - int iU2 = Integer.parseInt(u2); - if (iU2 >= iL2) { - if (iU1 >= iL1) { - i.putExtra(Utils.LOWER_1, iL1); - i.putExtra(Utils.LOWER_2, iL2); - i.putExtra(Utils.UPPER_2, iU2); - i.putExtra(Utils.UPPER_1, iU1); - SpUtil.getInstance().putString(Utils.LOWER_1, String.valueOf(iL1)); - SpUtil.getInstance().putString(Utils.LOWER_2, String.valueOf(iL2)); - SpUtil.getInstance().putString(Utils.UPPER_1, String.valueOf(iU1)); - SpUtil.getInstance().putString(Utils.UPPER_2, String.valueOf(iU2)); - startActivity(i); - overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); - } else { - upper1.requestFocus(); - Toast.makeText(this, getResources().getString(R.string.upper_value_must_be_greater), Toast.LENGTH_SHORT).show(); - } - } else { - upper2.requestFocus(); - Toast.makeText(this, getResources().getString(R.string.upper_value_must_be_greater), Toast.LENGTH_SHORT).show(); - } - } - }); - - } - - @RequiresApi(api = Build.VERSION_CODES.M) - private void setNotificationIfAny() { - boolean isNotificationEnable = SpUtil.getInstance().getBoolean(getString(R.string.pf_notification_switch), false); - Intent alarmIntent = new Intent(this, AlarmReceiver.class); - if (pendingIntent != null && manager != null) { - manager.cancel(pendingIntent); - } else { - pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); - } - - if (isNotificationEnable) { - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(System.currentTimeMillis()); - int hour = SpUtil.getInstance().getInt(getString(R.string.key_notification_hours), 8); - int min = SpUtil.getInstance().getInt(getString(R.string.key_notification_minutes), 0); - calendar.set(Calendar.HOUR_OF_DAY, hour); - calendar.set(Calendar.MINUTE, min); - calendar.set(Calendar.SECOND, 1); - manager.setRepeating(AlarmManager.RTC_WAKEUP, - calendar.getTimeInMillis(), - AlarmManager.INTERVAL_DAY, pendingIntent); - - } else { - manager.cancel(pendingIntent); - } - } - - public void setAdapters() { - ArrayAdapter opAdapter; - opAdapter = ArrayAdapter.createFromResource(this, R.array.operations, android.R.layout.simple_spinner_item); - opAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - ArrayAdapter timerAdapter = ArrayAdapter.createFromResource(this, R.array.timer, android.R.layout.simple_spinner_item); - timerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - operationsSpinner.setAdapter(opAdapter); - timerSpinner.setAdapter(timerAdapter); - } - - String getValueOf(EditText editText) { - String string = editText.getText().toString(); - if (string.isEmpty()) - return null; - else return string; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.menu_main, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int id = item.getItemId(); - if (id == R.id.action_settings) { - SpUtil.getInstance().putBoolean(getString(R.string.is_theme_changed), false); - startActivity(new Intent(this, SettingsActivity.class)); - return true; - } - return super.onOptionsItemSelected(item); - } - - @Override - protected void onResume() { - super.onResume(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - // setNotificationIfAny(); - } - - /* if (SpUtil.getInstance().getBoolean(getString(R.string.is_theme_changed),false)){ - SpUtil.getInstance().putBoolean(getString(R.string.is_theme_changed),false); - recreate(); - }*/ - } - - public static class AlarmReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - // send notification - // - } - } -} \ No newline at end of file diff --git a/app/src/main/java/io/github/subhamtyagi/quickcalculation/QuestionResult.java b/app/src/main/java/io/github/subhamtyagi/quickcalculation/QuestionResult.java new file mode 100644 index 0000000..c30729e --- /dev/null +++ b/app/src/main/java/io/github/subhamtyagi/quickcalculation/QuestionResult.java @@ -0,0 +1,20 @@ +package io.github.subhamtyagi.quickcalculation; + +import java.io.Serializable; + +public class QuestionResult implements Serializable { + private String question; + private String userAnswer; + private String correctAnswer; + + public QuestionResult(String question, String userAnswer, String correctAnswer) { + this.question = question; + this.userAnswer = userAnswer; + this.correctAnswer = correctAnswer; + } + + public String getQuestion() { return question; } + public String getUserAnswer() { return userAnswer; } + public String getCorrectAnswer() { return correctAnswer; } + } + \ No newline at end of file diff --git a/app/src/main/java/io/github/subhamtyagi/quickcalculation/QuizActivity.java b/app/src/main/java/io/github/subhamtyagi/quickcalculation/QuizActivity.java deleted file mode 100644 index 321b717..0000000 --- a/app/src/main/java/io/github/subhamtyagi/quickcalculation/QuizActivity.java +++ /dev/null @@ -1,207 +0,0 @@ -package io.github.subhamtyagi.quickcalculation; - -import android.annotation.SuppressLint; -import android.content.Intent; -import android.os.Build; -import android.os.Bundle; -import android.os.CountDownTimer; -import android.os.VibrationEffect; -import android.os.Vibrator; -import android.view.View; -import android.widget.Button; -import android.widget.TextView; - -import androidx.appcompat.app.AppCompatActivity; -import androidx.constraintlayout.widget.ConstraintLayout; -import com.google.android.material.color.DynamicColors; -import io.github.subhamtyagi.quickcalculation.factory.GenerateQuestion; -import io.github.subhamtyagi.quickcalculation.factory.Question; -import io.github.subhamtyagi.quickcalculation.utils.Utils; -import io.github.subhamtyagi.quickcalculation.utils.SpUtil; - -public class QuizActivity extends AppCompatActivity { - - private ConstraintLayout mReportCardView; - private Button mOption1Button; - private Button mOption2Button; - private Button mOption3Button; - private Button mOption4Button; - private Button mPlayAgainButton; - private TextView mScoreTextView; - private TextView mTimerTextView; - private TextView mQuestionTextView; - private TextView mResultTextView; - private TextView mTotalAttemptTextView; - private TextView mTotalCorrectTextView; - - private Vibrator vibrator; - private CountDownTimer countDownTimer; - - private int mCorrectCount = 0; - private int mTotalQuestionCount = 0; - private int mLowerRange1; - private int mLowerRange2; - private int mUpperRange1; - private int mUpperRange2; - - private String operation = null; - private String timer = null; - - private Question question; - boolean isVibrationEnable ; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - DynamicColors.applyToActivityIfAvailable(this); - setContentView(R.layout.activity_quiz); - SpUtil.getInstance().init(this); - - mOption1Button = findViewById(R.id.button0); - mOption2Button = findViewById(R.id.button1); - mOption3Button = findViewById(R.id.button2); - mOption4Button = findViewById(R.id.button3); - - mPlayAgainButton = findViewById(R.id.btn_play_again); - mReportCardView = findViewById(R.id.results); - mResultTextView = findViewById(R.id.resultText); - mQuestionTextView = findViewById(R.id.question); - mTimerTextView = findViewById(R.id.timer); - mScoreTextView = findViewById(R.id.score); - mTotalAttemptTextView = findViewById(R.id.tv_total_attempt_result); - mTotalCorrectTextView = findViewById(R.id.tv_total_correct_result); - - vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); - isVibrationEnable = SpUtil.getInstance().getBoolean(getString(R.string.pf_vibration_switch), true); - - mOption1Button.setOnClickListener(this::checkForAnswer); - mOption2Button.setOnClickListener(this::checkForAnswer); - mOption3Button.setOnClickListener(this::checkForAnswer); - mOption4Button.setOnClickListener(this::checkForAnswer); - findViewById(R.id.btn_submit).setOnClickListener(view -> { - countDownTimer.onFinish(); - countDownTimer.cancel(); - }); - - mPlayAgainButton.setOnClickListener(this::play); - - mReportCardView.setVisibility(View.INVISIBLE); - - Intent get = getIntent(); - operation = get.getStringExtra(Utils.OPERATIONS); - - if (operation.equals(Utils.SIMPLIFICATION) || operation.equals(Utils.SIMPLIFICATION_ADVANCE) || operation.equals(Utils.MIX) || operation.equals(Utils.SUM_SERIES)) { - mQuestionTextView.setTextSize(22); - } - timer = get.getStringExtra(Utils.TIME); - mLowerRange1 = get.getIntExtra(Utils.LOWER_1, 11); - mLowerRange2 = get.getIntExtra(Utils.LOWER_2, 11); - mUpperRange2 = get.getIntExtra(Utils.UPPER_2, 99); - mUpperRange1 = get.getIntExtra(Utils.UPPER_1, 99); - - play(mPlayAgainButton); - } - - public void play(View v) { - mReportCardView.setVisibility(View.INVISIBLE); - // mReportCardView.animate().translationY(-5000); - mResultTextView.setText(""); - mScoreTextView.setText("0 / 0"); - mCorrectCount = 0; - mTotalQuestionCount = 0; - showNewQuestion(); - - mPlayAgainButton.setVisibility(View.INVISIBLE); - startTimer(); - } - - private void showNewQuestion() { - switch (operation) { - case Utils.SUM: - question = GenerateQuestion.addition(mLowerRange1, mUpperRange1, mLowerRange2, mUpperRange2); - break; - case Utils.SUBSTRACT: - question = GenerateQuestion.subtract(mLowerRange1, mUpperRange1, mLowerRange2, mUpperRange2); - break; - case Utils.MULTIPLICATION: - question = GenerateQuestion.multiplication(mLowerRange1, mUpperRange1, mLowerRange2, mUpperRange2); - break; - case Utils.DIVISION: - question = GenerateQuestion.division(mLowerRange1, mUpperRange1, mLowerRange2, mUpperRange2); - break; - case Utils.SUM_SERIES: - question = GenerateQuestion.sumSeries(mLowerRange1, mUpperRange1); - break; - case Utils.SIMPLIFICATION: - question = GenerateQuestion.simplification(mLowerRange1, mUpperRange1); - break; - case Utils.SIMPLIFICATION_ADVANCE: - question = GenerateQuestion.simplificationAdvance(mLowerRange1, mUpperRange1, mLowerRange2, mUpperRange2); - break; - default: - question = GenerateQuestion.mix(mLowerRange1, mUpperRange1, mLowerRange2, mUpperRange2); - - - } - //set options - mQuestionTextView.setText(question.getQuestion()); - mOption1Button.setText(question.getOption1()); - mOption2Button.setText(question.getOption2()); - mOption3Button.setText(question.getOption3()); - mOption4Button.setText(question.getOption4()); - } - - public void startTimer() { - int t = Integer.parseInt(timer) * 1000 + 100; - countDownTimer = new CountDownTimer(t, 1000) { - @Override - public void onTick(long l) { - String time = (int) l / 1000 + "s"; - mTimerTextView.setText(time); - } - - @SuppressLint("SetTextI18n") - @Override - public void onFinish() { - mResultTextView.setText(getString(R.string.done)); - mReportCardView.setVisibility(View.VISIBLE); - mReportCardView.setY(2000); - mReportCardView.animate().translationYBy(-1990).setDuration(1350); - mTotalAttemptTextView.setText(Integer.toString(mTotalQuestionCount)); - mTotalCorrectTextView.setText(Integer.toString(mCorrectCount)); - mPlayAgainButton.setVisibility(View.VISIBLE); - } - }.start(); - } - - @SuppressLint("SetTextI18n") - public void checkForAnswer(View view) { - int clickedAnswer = Integer.parseInt(((Button) view).getText().toString()); - if (clickedAnswer == question.getCorrectAnswer()) { - mResultTextView.setText(R.string.correct); - mResultTextView.setTextColor(getResources().getColor(R.color.materialGreen)); - mCorrectCount++; - } else { - mResultTextView.setText(R.string.wrong); - mResultTextView.setTextColor(getResources().getColor(R.color.materialRed)); - //vibrate phone - if(isVibrationEnable){ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); - } else { - vibrator.vibrate(100); - } - } - } - showNewQuestion(); - mTotalQuestionCount++; - mScoreTextView.setText(mCorrectCount + "/" + mTotalQuestionCount); - } - - /* - * - * BottomSheetResultsFragment bottomSheetResultsFragment = BottomSheetResultsFragment.newInstance(text); - * bottomSheetResultsFragment.show(getSupportFragmentManager(), "bottomSheetResultsFragment"); - * - */ -} \ No newline at end of file diff --git a/app/src/main/java/io/github/subhamtyagi/quickcalculation/utils/LaunchActivity.java b/app/src/main/java/io/github/subhamtyagi/quickcalculation/utils/LaunchActivity.java new file mode 100644 index 0000000..c46e157 --- /dev/null +++ b/app/src/main/java/io/github/subhamtyagi/quickcalculation/utils/LaunchActivity.java @@ -0,0 +1,172 @@ +package io.github.subhamtyagi.quickcalculation; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.Switch; +import android.widget.Toast; + +import androidx.annotation.RequiresApi; +import androidx.appcompat.app.AppCompatActivity; + +import com.google.android.material.color.DynamicColors; +import java.util.Calendar; + +import io.github.subhamtyagi.quickcalculation.utils.CrashUtils; +import io.github.subhamtyagi.quickcalculation.utils.SpUtil; +import io.github.subhamtyagi.quickcalculation.utils.Utils; + +public class LaunchActivity extends AppCompatActivity { + + AlarmManager manager; + PendingIntent pendingIntent; + private Spinner operationsSpinner, timerSpinner; + private String operationName = "mix", timer = "30"; + private EditText lower1, lower2, upper1, upper2; + private Switch keyboardModeSwitch; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + new CrashUtils(getApplicationContext(), ""); + SpUtil.getInstance().init(this); + DynamicColors.applyToActivityIfAvailable(this); + setContentView(R.layout.activity_launch); + + operationsSpinner = findViewById(R.id.spinner_operator); + timerSpinner = findViewById(R.id.spinner_timer); + lower1 = findViewById(R.id.et_range1_lower); + lower2 = findViewById(R.id.et_range2_lower); + upper1 = findViewById(R.id.et_range1_upper); + upper2 = findViewById(R.id.et_range2_upper); + keyboardModeSwitch = findViewById(R.id.switch_keyboard_mode); + + lower1.setText(SpUtil.getInstance().getString(Utils.LOWER_1, "11")); + lower2.setText(SpUtil.getInstance().getString(Utils.LOWER_2, "11")); + upper1.setText(SpUtil.getInstance().getString(Utils.UPPER_1, "99")); + upper2.setText(SpUtil.getInstance().getString(Utils.UPPER_2, "99")); + + boolean savedKeyboardPref = SpUtil.getInstance().getBoolean("PREF_KEYBOARD_MODE", false); + keyboardModeSwitch.setChecked(savedKeyboardPref); + + setAdapters(); + + operationsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int position, long l) { + switch (position) { + case 0: operationName = Utils.SUM; break; + case 1: operationName = Utils.SUBSTRACT; break; + case 2: operationName = Utils.MULTIPLICATION; break; + case 3: operationName = Utils.DIVISION; break; + case 4: operationName = Utils.SUM_SERIES; break; + case 5: operationName = Utils.SIMPLIFICATION; break; + case 6: operationName = Utils.SIMPLIFICATION_ADVANCE; break; + default: operationName = Utils.MIX; + } + } + @Override + public void onNothingSelected(AdapterView adapterView) {} + }); + + timerSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int position, long l) { + switch (position) { + case 0: timer = "30"; break; + case 1: timer = "45"; break; + case 2: timer = "60"; break; + case 3: timer = "90"; break; + case 4: timer = "120"; break; + case 5: timer = "300"; break; + case 6: timer = "600"; break; + case 7: timer = "-1"; break; + } + } + @Override + public void onNothingSelected(AdapterView adapterView) {} + }); + + findViewById(R.id.btn_start).setOnClickListener(view -> { + Intent i = new Intent(this, QuizActivity.class); + i.putExtra(Utils.OPERATIONS, operationName); + i.putExtra(Utils.TIME, timer); + + i.putExtra("KEYBOARD_MODE", keyboardModeSwitch.isChecked()); + SpUtil.getInstance().putBoolean("PREF_KEYBOARD_MODE", keyboardModeSwitch.isChecked()); + + String l1 = getValueOf(lower1), l2 = getValueOf(lower2), u2 = getValueOf(upper2), u1 = getValueOf(upper1); + + if (l1 == null) lower1.requestFocus(); + else if (u1 == null) upper1.requestFocus(); + else if (l2 == null) lower2.requestFocus(); + else if (u2 == null) upper2.requestFocus(); + else { + try { + int iL1 = Integer.parseInt(l1), iL2 = Integer.parseInt(l2), iU1 = Integer.parseInt(u1), iU2 = Integer.parseInt(u2); + if (iU2 >= iL2 && iU1 >= iL1) { + i.putExtra(Utils.LOWER_1, iL1); i.putExtra(Utils.LOWER_2, iL2); + i.putExtra(Utils.UPPER_2, iU2); i.putExtra(Utils.UPPER_1, iU1); + SpUtil.getInstance().putString(Utils.LOWER_1, String.valueOf(iL1)); + SpUtil.getInstance().putString(Utils.LOWER_2, String.valueOf(iL2)); + SpUtil.getInstance().putString(Utils.UPPER_1, String.valueOf(iU1)); + SpUtil.getInstance().putString(Utils.UPPER_2, String.valueOf(iU2)); + startActivity(i); + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + } else { + if (iU2 < iL2) upper2.requestFocus(); else upper1.requestFocus(); + Toast.makeText(this, getResources().getString(R.string.upper_value_must_be_greater), Toast.LENGTH_SHORT).show(); + } + } catch (NumberFormatException e) { + Toast.makeText(this, "Number is too large!", Toast.LENGTH_SHORT).show(); + } + } + }); + } + + public void setAdapters() { + ArrayAdapter opAdapter = ArrayAdapter.createFromResource(this, R.array.operations, android.R.layout.simple_spinner_item); + opAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + ArrayAdapter timerAdapter = ArrayAdapter.createFromResource(this, R.array.timer, android.R.layout.simple_spinner_item); + timerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + operationsSpinner.setAdapter(opAdapter); + timerSpinner.setAdapter(timerAdapter); + } + + String getValueOf(EditText editText) { + String string = editText.getText().toString(); + return string.isEmpty() ? null : string; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.action_settings) { + SpUtil.getInstance().putBoolean(getString(R.string.is_theme_changed), false); + startActivity(new Intent(this, SettingsActivity.class)); + return true; + } + return super.onOptionsItemSelected(item); + } + + public static class AlarmReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) {} + } +} diff --git a/app/src/main/java/io/github/subhamtyagi/quickcalculation/utils/QuestionReviewAdapter.java b/app/src/main/java/io/github/subhamtyagi/quickcalculation/utils/QuestionReviewAdapter.java new file mode 100644 index 0000000..25250c4 --- /dev/null +++ b/app/src/main/java/io/github/subhamtyagi/quickcalculation/utils/QuestionReviewAdapter.java @@ -0,0 +1,79 @@ +package io.github.subhamtyagi.quickcalculation; + +import android.graphics.Color; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.ListAdapter; +import androidx.recyclerview.widget.RecyclerView; + +public class QuestionReviewAdapter extends ListAdapter { + + private static final int COLOR_CORRECT = Color.parseColor("#4CAF50"); + private static final int COLOR_WRONG = Color.parseColor("#F44336"); + private static final int COLOR_SKIPPED = Color.parseColor("#FF9800"); + + public QuestionReviewAdapter() { + super(DIFF_CALLBACK); + } + + private static final DiffUtil.ItemCallback DIFF_CALLBACK = new DiffUtil.ItemCallback() { + @Override + public boolean areItemsTheSame(@NonNull QuestionResult oldItem, @NonNull QuestionResult newItem) { + return oldItem.getQuestion().equals(newItem.getQuestion()); + } + + @Override + public boolean areContentsTheSame(@NonNull QuestionResult oldItem, @NonNull QuestionResult newItem) { + return oldItem.getQuestion().equals(newItem.getQuestion()) && + oldItem.getUserAnswer().equals(newItem.getUserAnswer()) && + oldItem.getCorrectAnswer().equals(newItem.getCorrectAnswer()); + } + }; + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_question_review, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + QuestionResult result = getItem(position); + + holder.questionText.setText(result.getQuestion()); + holder.userAnswerText.setText("Your Answer: " + result.getUserAnswer()); + + String skipText = holder.itemView.getContext().getString(R.string.skip); + + if (result.getUserAnswer().equals(result.getCorrectAnswer())) { + holder.userAnswerText.setTextColor(COLOR_CORRECT); + holder.correctAnswerText.setVisibility(View.GONE); + } else if (skipText.equals(result.getUserAnswer()) || "Skipped".equals(result.getUserAnswer())) { + holder.userAnswerText.setTextColor(COLOR_SKIPPED); + holder.correctAnswerText.setVisibility(View.VISIBLE); + holder.correctAnswerText.setText("Correct Answer: " + result.getCorrectAnswer()); + holder.correctAnswerText.setTextColor(COLOR_CORRECT); + } else { + holder.userAnswerText.setTextColor(COLOR_WRONG); + holder.correctAnswerText.setVisibility(View.VISIBLE); + holder.correctAnswerText.setText("Correct Answer: " + result.getCorrectAnswer()); + holder.correctAnswerText.setTextColor(COLOR_CORRECT); + } + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + TextView questionText, userAnswerText, correctAnswerText; + + public ViewHolder(@NonNull View itemView) { + super(itemView); + questionText = itemView.findViewById(R.id.tvQuestion); + userAnswerText = itemView.findViewById(R.id.tvUserAnswer); + correctAnswerText = itemView.findViewById(R.id.tvCorrectAnswer); + } + } +} diff --git a/app/src/main/java/io/github/subhamtyagi/quickcalculation/utils/QuizActivity.java b/app/src/main/java/io/github/subhamtyagi/quickcalculation/utils/QuizActivity.java new file mode 100644 index 0000000..124aa42 --- /dev/null +++ b/app/src/main/java/io/github/subhamtyagi/quickcalculation/utils/QuizActivity.java @@ -0,0 +1,345 @@ +package io.github.subhamtyagi.quickcalculation; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Color; +import android.os.Build; +import android.os.Bundle; +import android.os.CountDownTimer; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.core.content.ContextCompat; +import androidx.gridlayout.widget.GridLayout; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.material.color.DynamicColors; + +import io.github.subhamtyagi.quickcalculation.factory.GenerateQuestion; +import io.github.subhamtyagi.quickcalculation.factory.Question; +import io.github.subhamtyagi.quickcalculation.utils.Utils; +import io.github.subhamtyagi.quickcalculation.utils.SpUtil; +import java.util.ArrayList; + +public class QuizActivity extends AppCompatActivity { + + private ConstraintLayout mReportCardView; + private GridLayout mOptionsGridLayout; + private EditText mAnswerInput; + private Button mOption1Button, mOption2Button, mOption3Button, mOption4Button; + private Button mPlayAgainButton, mSubmitEndButton, mSkipButton; + private TextView mScoreTextView, mTimerTextView, mQuestionTextView, mResultTextView; + private TextView mTotalAttemptTextView, mTotalCorrectTextView; + + private RecyclerView mRecyclerView; + private QuestionReviewAdapter mAdapter; + + private Vibrator vibrator; + private CountDownTimer countDownTimer; + + private int mCorrectCount = 0; + private int mTotalQuestionCount = 0; + private int mLowerRange1, mLowerRange2, mUpperRange1, mUpperRange2; + + private String operation = null; + private String timer = null; + private boolean isKeyboardMode = false; + private boolean isQuizEnded = false; + + private Question question; + boolean isVibrationEnable; + + private ArrayList questionResultsList = new ArrayList<>(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + DynamicColors.applyToActivityIfAvailable(this); + setContentView(R.layout.activity_quiz); + SpUtil.getInstance().init(this); + + mOptionsGridLayout = findViewById(R.id.options); + mAnswerInput = findViewById(R.id.et_answer_input); + mOption1Button = findViewById(R.id.button0); + mOption2Button = findViewById(R.id.button1); + mOption3Button = findViewById(R.id.button2); + mOption4Button = findViewById(R.id.button3); + mSubmitEndButton = findViewById(R.id.btn_submit); + mSkipButton = findViewById(R.id.btn_skip); + mPlayAgainButton = findViewById(R.id.btn_play_again); + mReportCardView = findViewById(R.id.results); + mResultTextView = findViewById(R.id.resultText); + mQuestionTextView = findViewById(R.id.question); + mTimerTextView = findViewById(R.id.timer); + mScoreTextView = findViewById(R.id.score); + mTotalAttemptTextView = findViewById(R.id.tv_total_attempt_result); + mTotalCorrectTextView = findViewById(R.id.tv_total_correct_result); + + mRecyclerView = findViewById(R.id.rv_results); + mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); + mAdapter = new QuestionReviewAdapter(); + mRecyclerView.setAdapter(mAdapter); + + vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); + isVibrationEnable = SpUtil.getInstance().getBoolean(getString(R.string.pf_vibration_switch), true); + + operation = getIntent().getStringExtra(Utils.OPERATIONS); + if (operation == null) { + operation = Utils.MIX; + } + + timer = getIntent().getStringExtra(Utils.TIME); + isKeyboardMode = getIntent().getBooleanExtra("KEYBOARD_MODE", false); + + mLowerRange1 = getIntent().getIntExtra(Utils.LOWER_1, 11); + mLowerRange2 = getIntent().getIntExtra(Utils.LOWER_2, 11); + mUpperRange2 = getIntent().getIntExtra(Utils.UPPER_2, 99); + mUpperRange1 = getIntent().getIntExtra(Utils.UPPER_1, 99); + + if (isKeyboardMode) { + mOptionsGridLayout.setVisibility(View.GONE); + mAnswerInput.setVisibility(View.VISIBLE); + mAnswerInput.requestFocus(); + showSoftKeyboard(mAnswerInput); + } else { + mOptionsGridLayout.setVisibility(View.VISIBLE); + mAnswerInput.setVisibility(View.GONE); + } + + mOption1Button.setOnClickListener(this::checkForAnswer); + mOption2Button.setOnClickListener(this::checkForAnswer); + mOption3Button.setOnClickListener(this::checkForAnswer); + mOption4Button.setOnClickListener(this::checkForAnswer); + + mSkipButton.setOnClickListener(view -> skipQuestion()); + + mAnswerInput.setOnEditorActionListener((v, actionId, event) -> { + if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_NEXT || + (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN)) { + + if (isQuizEnded) return true; + + String input = mAnswerInput.getText().toString().trim(); + if (!input.isEmpty() && !input.equals("-")) { + try { + int parsedAnswer = Integer.parseInt(input); + processAnswer(parsedAnswer); + } catch (NumberFormatException e) { + Toast.makeText(this, "Invalid number!", Toast.LENGTH_SHORT).show(); + } + mAnswerInput.setText(""); + } + return true; + } + return false; + }); + + mSubmitEndButton.setOnClickListener(view -> endQuiz()); + mPlayAgainButton.setOnClickListener(this::play); + mReportCardView.setVisibility(View.INVISIBLE); + + if (operation.equals(Utils.SIMPLIFICATION) || operation.equals(Utils.SIMPLIFICATION_ADVANCE) || operation.equals(Utils.MIX) || operation.equals(Utils.SUM_SERIES)) { + mQuestionTextView.setTextSize(22); + } + + play(mPlayAgainButton); + } + + public void play(View v) { + mReportCardView.animate().cancel(); + + mReportCardView.setVisibility(View.INVISIBLE); + mResultTextView.setText(""); + mScoreTextView.setText("0 / 0"); + mCorrectCount = 0; + mTotalQuestionCount = 0; + isQuizEnded = false; + questionResultsList.clear(); + mAdapter.submitList(new ArrayList<>()); + + if (isKeyboardMode) { + mAnswerInput.setText(""); + showSoftKeyboard(mAnswerInput); + } + + showNewQuestion(); + mPlayAgainButton.setVisibility(View.INVISIBLE); + startTimer(); + } + + private void showNewQuestion() { + switch (operation) { + case Utils.SUM: question = GenerateQuestion.addition(mLowerRange1, mUpperRange1, mLowerRange2, mUpperRange2); break; + case Utils.SUBSTRACT: question = GenerateQuestion.subtract(mLowerRange1, mUpperRange1, mLowerRange2, mUpperRange2); break; + case Utils.MULTIPLICATION: question = GenerateQuestion.multiplication(mLowerRange1, mUpperRange1, mLowerRange2, mUpperRange2); break; + case Utils.DIVISION: question = GenerateQuestion.division(mLowerRange1, mUpperRange1, mLowerRange2, mUpperRange2); break; + case Utils.SUM_SERIES: question = GenerateQuestion.sumSeries(mLowerRange1, mUpperRange1); break; + case Utils.SIMPLIFICATION: question = GenerateQuestion.simplification(mLowerRange1, mUpperRange1); break; + case Utils.SIMPLIFICATION_ADVANCE: question = GenerateQuestion.simplificationAdvance(mLowerRange1, mUpperRange1, mLowerRange2, mUpperRange2); break; + default: question = GenerateQuestion.mix(mLowerRange1, mUpperRange1, mLowerRange2, mUpperRange2); + } + + mQuestionTextView.setText(question.getQuestion()); + + if (!isKeyboardMode) { + mOption1Button.setText(question.getOption1()); + mOption2Button.setText(question.getOption2()); + mOption3Button.setText(question.getOption3()); + mOption4Button.setText(question.getOption4()); + } + } + + public void startTimer() { + if (countDownTimer != null) { + countDownTimer.cancel(); + } + + int t = 60; + try { + if (timer != null) { + t = Integer.parseInt(timer); + } + } catch (NumberFormatException ignored) {} + + if (t <= 0) { + mTimerTextView.setText("∞"); + } else { + int timeInMillis = t * 1000 + 100; + countDownTimer = new CountDownTimer(timeInMillis, 1000) { + @Override + public void onTick(long l) { + mTimerTextView.setText((int) l / 1000 + "s"); + } + @Override + public void onFinish() { + endQuiz(); + } + }.start(); + } + } + + @SuppressLint("SetTextI18n") + private void endQuiz() { + if (isFinishing() || isDestroyed()) return; + + if (isQuizEnded) return; + isQuizEnded = true; + + if (countDownTimer != null) { + countDownTimer.cancel(); + } + + hideSoftKeyboard(); + + mResultTextView.setText(getString(R.string.done)); + mTotalAttemptTextView.setText(Integer.toString(mTotalQuestionCount)); + mTotalCorrectTextView.setText(Integer.toString(mCorrectCount)); + mPlayAgainButton.setVisibility(View.VISIBLE); + + mAdapter.submitList(new ArrayList<>(questionResultsList)); + + mReportCardView.setAlpha(0f); + mReportCardView.setVisibility(View.VISIBLE); + mReportCardView.animate().alpha(1f).setDuration(500); + } + + public void checkForAnswer(View view) { + if (isQuizEnded) return; + int clickedAnswer = Integer.parseInt(((Button) view).getText().toString()); + processAnswer(clickedAnswer); + } + + @SuppressLint("SetTextI18n") + private void processAnswer(int clickedAnswer) { + if (clickedAnswer == question.getCorrectAnswer()) { + mResultTextView.setText(R.string.correct); + mResultTextView.setTextColor(ContextCompat.getColor(this, R.color.materialGreen)); + mCorrectCount++; + } else { + mResultTextView.setText(R.string.wrong); + mResultTextView.setTextColor(ContextCompat.getColor(this, R.color.materialRed)); + + if (isVibrationEnable && vibrator != null && vibrator.hasVibrator()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); + } else { + vibrator.vibrate(100); + } + } + } + + QuestionResult result = new QuestionResult(mQuestionTextView.getText().toString(), String.valueOf(clickedAnswer), String.valueOf(question.getCorrectAnswer())); + questionResultsList.add(result); + + showNewQuestion(); + mTotalQuestionCount++; + mScoreTextView.setText(mCorrectCount + "/" + mTotalQuestionCount); + + if (isKeyboardMode) { + mAnswerInput.requestFocus(); + } + } + + @SuppressLint("SetTextI18n") + private void skipQuestion() { + if (isQuizEnded) return; + + QuestionResult result = new QuestionResult(mQuestionTextView.getText().toString(), getString(R.string.skip), String.valueOf(question.getCorrectAnswer())); + questionResultsList.add(result); + + mResultTextView.setText(getString(R.string.skip).toUpperCase()); + mResultTextView.setTextColor(Color.GRAY); + + mTotalQuestionCount++; + mScoreTextView.setText(mCorrectCount + "/" + mTotalQuestionCount); + showNewQuestion(); + + if (isKeyboardMode) { + mAnswerInput.setText(""); + mAnswerInput.requestFocus(); + } + } + + private void showSoftKeyboard(View view) { + view.post(() -> { + if (view.requestFocus()) { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); + } + } + }); + } + + private void hideSoftKeyboard() { + View view = this.getCurrentFocus(); + if (view == null) { + view = findViewById(android.R.id.content); + } + if (view != null) { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (countDownTimer != null) { + countDownTimer.cancel(); + } + } +} diff --git a/app/src/main/res/layout/activity_launch.xml b/app/src/main/res/layout/activity_launch.xml index a12079a..f32f8b3 100644 --- a/app/src/main/res/layout/activity_launch.xml +++ b/app/src/main/res/layout/activity_launch.xml @@ -1,236 +1,213 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + android:layout_height="match_parent"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_quiz.xml b/app/src/main/res/layout/activity_quiz.xml index 03eba9e..151d698 100644 --- a/app/src/main/res/layout/activity_quiz.xml +++ b/app/src/main/res/layout/activity_quiz.xml @@ -6,8 +6,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:alwaysDrawnWithCache="true" - tools:context=".QuizActivity" - tools:visibility="visible"> + tools:context=".QuizActivity"> + tools:ignore="HardcodedText" /> + tools:ignore="HardcodedText" /> - + tools:ignore="HardcodedText" /> + app:layout_constraintTop_toBottomOf="@+id/question"> + tools:ignore="HardcodedText" /> + tools:ignore="HardcodedText" /> + tools:ignore="HardcodedText" /> + tools:ignore="HardcodedText" /> + + + + + app:layout_constraintTop_toBottomOf="@id/answer_area_barrier" /> - + app:layout_constraintBottom_toBottomOf="parent"> - + + + + + - + + - + app:layout_constraintStart_toStartOf="parent" /> - - - \ No newline at end of file + diff --git a/app/src/main/res/layout/item_question_review.xml b/app/src/main/res/layout/item_question_review.xml new file mode 100644 index 0000000..6073030 --- /dev/null +++ b/app/src/main/res/layout/item_question_review.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index ab63706..61af059 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -19,25 +19,6 @@ 120 300 600 + Infinite - - - - \ No newline at end of file + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0c98cc2..f47ff97 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,4 @@ - pf_theme pf_notification_switch pf_notification_time @@ -33,6 +32,8 @@ Select Theme Notification Notification Time - - - \ No newline at end of file + + Skip + Use Keyboard Input + End Quiz + diff --git a/gradlew b/gradlew old mode 100644 new mode 100755