From 386bd0a106809251e472a73f89a5d9b9d6f7d37e Mon Sep 17 00:00:00 2001 From: Vivek Maskara Date: Sat, 28 Jul 2018 01:09:23 +0530 Subject: [PATCH] Merge gamification branch with master (#1752) * Quiz (#1629) * Layout inflated * Layout for mcq added * Inflated Basic Layout * Implemented basic flow * Added the basic implementation pf score * Added the result layout * Added the result layout * Added functionality to set result * Changed the launcher intent to Quiz Activity for testing purpose * Explanations of answers added * Improved the layout of quiz result a bit * Fixed some minor issues * Fixed build issues * Api Added and basic structure for calling implemented * Added intents * Added the title * Fixed image error and improved quality of pr * Made separate class for checking quiz * Added counter * Implemented back and next for quiz result * Added back functionality to quiz * Added progressBar * Fixed bugs * Improved code quality * Imporved code Quality * Updated strings * Added share screenshot function * Added checks and improved UI * Removed unused string * Removed unused string * Adding checks and improving code quality * Changed string * Improved code quality * Update strings.xml * Update MediaWikiApi.java * Fix build --- app/build.gradle | 3 + app/src/main/AndroidManifest.xml | 9 +- .../fr/free/nrw/commons/WelcomeActivity.java | 15 ++ .../achievements/AchievementsActivity.java | 2 +- .../contributions/ContributionsActivity.java | 25 +++ .../mwapi/ApacheHttpClientMediaWikiApi.java | 3 +- .../free/nrw/commons/mwapi/MediaWikiApi.java | 6 +- .../free/nrw/commons/quiz/QuizActivity.java | 165 +++++++++++++++ .../fr/free/nrw/commons/quiz/QuizChecker.java | 160 ++++++++++++++ .../free/nrw/commons/quiz/QuizController.java | 64 ++++++ .../free/nrw/commons/quiz/QuizQuestion.java | 62 ++++++ .../nrw/commons/quiz/QuizResultActivity.java | 199 ++++++++++++++++++ .../nrw/commons/quiz/RadioGroupHelper.java | 64 ++++++ .../main/res/drawable/ic_clear_black_24dp.xml | 5 + .../main/res/drawable/ic_done_black_24dp.xml | 5 + app/src/main/res/layout/activity_quiz.xml | 85 ++++++++ .../main/res/layout/activity_quiz_result.xml | 88 ++++++++ app/src/main/res/layout/answer_layout.xml | 65 ++++++ .../main/res/layout/image_alert_layout.xml | 2 +- app/src/main/res/layout/question_layout.xml | 27 +++ app/src/main/res/values/strings.xml | 21 +- 21 files changed, 1067 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/quiz/QuizActivity.java create mode 100644 app/src/main/java/fr/free/nrw/commons/quiz/QuizChecker.java create mode 100644 app/src/main/java/fr/free/nrw/commons/quiz/QuizController.java create mode 100644 app/src/main/java/fr/free/nrw/commons/quiz/QuizQuestion.java create mode 100644 app/src/main/java/fr/free/nrw/commons/quiz/QuizResultActivity.java create mode 100644 app/src/main/java/fr/free/nrw/commons/quiz/RadioGroupHelper.java create mode 100644 app/src/main/res/drawable/ic_clear_black_24dp.xml create mode 100644 app/src/main/res/drawable/ic_done_black_24dp.xml create mode 100644 app/src/main/res/layout/activity_quiz.xml create mode 100644 app/src/main/res/layout/activity_quiz_result.xml create mode 100644 app/src/main/res/layout/answer_layout.xml create mode 100644 app/src/main/res/layout/question_layout.xml diff --git a/app/build.gradle b/app/build.gradle index ea06c5c43..0d16a631a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,9 @@ dependencies { debugImplementation "com.squareup.leakcanary:leakcanary-android:$LEAK_CANARY" releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$LEAK_CANARY" testImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$LEAK_CANARY" + + implementation 'com.borjabravo:readmoretextview:2.1.0' + implementation 'com.dinuscxj:circleprogressbar:1.1.1' } android { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3665042ac..59ffcea19 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,6 +18,7 @@ + @@ -36,6 +37,7 @@ + @@ -93,6 +95,12 @@ android:name=".notification.NotificationActivity" android:label="@string/navigation_item_notification" /> + + + + - diff --git a/app/src/main/java/fr/free/nrw/commons/WelcomeActivity.java b/app/src/main/java/fr/free/nrw/commons/WelcomeActivity.java index 7bfb22890..c1bc37f46 100644 --- a/app/src/main/java/fr/free/nrw/commons/WelcomeActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/WelcomeActivity.java @@ -9,6 +9,7 @@ import com.viewpagerindicator.CirclePageIndicator; import butterknife.BindView; import butterknife.ButterKnife; +import fr.free.nrw.commons.quiz.QuizActivity; import fr.free.nrw.commons.theme.BaseActivity; public class WelcomeActivity extends BaseActivity { @@ -17,6 +18,7 @@ public class WelcomeActivity extends BaseActivity { @BindView(R.id.welcomePagerIndicator) CirclePageIndicator indicator; private WelcomePagerAdapter adapter = new WelcomePagerAdapter(); + private boolean isQuiz; /** * Initialises exiting fields and dependencies @@ -28,6 +30,15 @@ public class WelcomeActivity extends BaseActivity { super.onCreate(savedInstanceState); setContentView(R.layout.activity_welcome); + if(getIntent() != null) { + Bundle bundle = getIntent().getExtras(); + if (bundle != null) { + isQuiz = bundle.getBoolean("isQuiz"); + } + } else{ + isQuiz = false; + } + ButterKnife.bind(this); pager.setAdapter(adapter); @@ -40,6 +51,10 @@ public class WelcomeActivity extends BaseActivity { */ @Override public void onDestroy() { + if(isQuiz){ + Intent i = new Intent(WelcomeActivity.this, QuizActivity.class); + startActivity(i); + } adapter.setCallback(null); super.onDestroy(); } diff --git a/app/src/main/java/fr/free/nrw/commons/achievements/AchievementsActivity.java b/app/src/main/java/fr/free/nrw/commons/achievements/AchievementsActivity.java index 905d0dfbd..d25c1cd8f 100644 --- a/app/src/main/java/fr/free/nrw/commons/achievements/AchievementsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/achievements/AchievementsActivity.java @@ -221,7 +221,7 @@ public class AchievementsActivity extends NavigationBaseActivity { private void setRevertCount(){ if(checkAccount()) { compositeDisposable.add(mediaWikiApi - .getRevertCount(sessionManager.getCurrentAccount().name) + .getRevertRespObjectSingle(sessionManager.getCurrentAccount().name) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java index e5f6f53bb..6500662a5 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java @@ -1,5 +1,6 @@ package fr.free.nrw.commons.contributions; +import android.accounts.Account; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -34,8 +35,10 @@ import fr.free.nrw.commons.auth.AuthenticatedActivity; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.media.MediaDetailPagerFragment; import fr.free.nrw.commons.mwapi.MediaWikiApi; +import fr.free.nrw.commons.quiz.QuizChecker; import fr.free.nrw.commons.settings.Prefs; import fr.free.nrw.commons.upload.UploadService; +import fr.free.nrw.commons.utils.ViewUtil; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; @@ -137,10 +140,17 @@ public class ContributionsActivity getSupportLoaderManager().initLoader(0, null, this); } + requestAuthToken(); initDrawer(); setTitle(getString(R.string.title_activity_contributions)); + + if(checkAccount()) { + new QuizChecker(this, + sessionManager.getCurrentAccount().name, + mediaWikiApi); + } if(!BuildConfig.FLAVOR.equalsIgnoreCase("beta")){ setUploadCount(); } @@ -338,6 +348,21 @@ public class ContributionsActivity } } + /** + * to ensure user is logged in + * @return + */ + private boolean checkAccount() { + Account currentAccount = sessionManager.getCurrentAccount(); + if (currentAccount == null) { + Timber.d("Current account is null"); + ViewUtil.showLongToast(this, getResources().getString(R.string.user_not_logged_in)); + sessionManager.forceLogin(this); + return false; + } + return true; + } + @Override public void onBackStackChanged() { initBackButton(); diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index 6088b774b..516dbcb61 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -894,6 +894,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { } /** + * Checks to see if a user is currently blocked from Commons * @return whether or not the user is blocked from Commons */ @@ -964,7 +965,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { */ @NonNull @Override - public Single getRevertCount(String userName){ + public Single getRevertRespObjectSingle(String userName){ final String fetchRevertCountUrlTemplate = wikiMediaToolforgeUrl + "urbanecmbot/commonsmisc/feedback.py"; return Single.fromCallable(() -> { diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java index 78c990372..8c410dad7 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java @@ -97,14 +97,14 @@ public interface MediaWikiApi { @NonNull Single getUploadCount(String userName); + @NonNull + Single getRevertRespObjectSingle(String userName); + boolean isUserBlockedFromCommons(); @NonNull Single getAchievements(String userName); - @NonNull - Single getRevertCount(String userName); - interface ProgressListener { void onProgress(long transferred, long total); } diff --git a/app/src/main/java/fr/free/nrw/commons/quiz/QuizActivity.java b/app/src/main/java/fr/free/nrw/commons/quiz/QuizActivity.java new file mode 100644 index 000000000..f5c246ee4 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/quiz/QuizActivity.java @@ -0,0 +1,165 @@ +package fr.free.nrw.commons.quiz; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.support.graphics.drawable.VectorDrawableCompat; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.widget.ProgressBar; +import android.widget.RadioButton; +import android.widget.TextView; + +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.drawable.ProgressBarDrawable; +import com.facebook.drawee.generic.GenericDraweeHierarchy; +import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; +import com.facebook.drawee.view.SimpleDraweeView; + +import java.util.ArrayList; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import fr.free.nrw.commons.R; +import fr.free.nrw.commons.contributions.ContributionsActivity; + +public class QuizActivity extends AppCompatActivity { + + @BindView(R.id.question_image) SimpleDraweeView imageView; + @BindView(R.id.question_text) TextView questionText; + @BindView(R.id.question_title) TextView questionTitle; + @BindView(R.id.quiz_positive_answer) RadioButton positiveAnswer; + @BindView(R.id.quiz_negative_answer) RadioButton negativeAnswer; + @BindView(R.id.toolbar) Toolbar toolbar; + + private QuizController quizController = new QuizController(); + private ArrayList quiz = new ArrayList(); + private int questionIndex = 0; + private int score; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_quiz); + Fresco.initialize(this); + + quizController.initialize(this); + ButterKnife.bind(this); + setSupportActionBar(toolbar); + displayQuestion(); + } + + /** + * to move to next question and check whether answer is selected or not + */ + @OnClick(R.id.next_button) + public void setNextQuestion(){ + if( questionIndex <= quiz.size() && (positiveAnswer.isChecked() || negativeAnswer.isChecked())) { + evaluateScore(); + } else if ( !positiveAnswer.isChecked() && !negativeAnswer.isChecked()){ + AlertDialog.Builder alert = new AlertDialog.Builder(this); + alert.setTitle(getResources().getString(R.string.warning)); + alert.setMessage(getResources().getString(R.string.warning_for_no_answer)); + alert.setPositiveButton(R.string.continue_message, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + AlertDialog dialog = alert.create(); + dialog.show(); + } + + } + + /** + * to give warning before ending quiz + */ + @Override + public void onBackPressed() { + AlertDialog.Builder alert = new AlertDialog.Builder(this); + alert.setTitle(getResources().getString(R.string.warning)); + alert.setMessage(getResources().getString(R.string.quiz_back_button)); + alert.setPositiveButton(R.string.continue_message, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent i = new Intent(QuizActivity.this, QuizResultActivity.class); + dialog.dismiss(); + i.putExtra("QuizResult",score); + startActivity(i); + } + }); + alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + dialogInterface.dismiss(); + } + }); + AlertDialog dialog = alert.create(); + dialog.show(); + } + + /** + * to display the question + */ + public void displayQuestion() { + quiz = quizController.getQuiz(); + questionText.setText(quiz.get(questionIndex).getQuestion()); + questionTitle.setText(getResources().getString(R.string.question)+quiz.get(questionIndex).getQuestionNumber()); + imageView.setHierarchy(GenericDraweeHierarchyBuilder + .newInstance(getResources()) + .setFailureImage(VectorDrawableCompat.create(getResources(), + R.drawable.ic_error_outline_black_24dp, getTheme())) + .setProgressBarImage(new ProgressBarDrawable()) + .build()); + + imageView.setImageURI(quiz.get(questionIndex).getUrl()); + new RadioGroupHelper(this, R.id.quiz_positive_answer, R.id.quiz_negative_answer); + positiveAnswer.setChecked(false); + negativeAnswer.setChecked(false); + } + + /** + * to evaluate score and check whether answer is correct or wrong + */ + public void evaluateScore() { + if((quiz.get(questionIndex).isAnswer() && positiveAnswer.isChecked()) || + (!quiz.get(questionIndex).isAnswer() && negativeAnswer.isChecked()) ){ + customAlert(getResources().getString(R.string.correct),quiz.get(questionIndex).getAnswerMessage() ); + score++; + } else{ + customAlert(getResources().getString(R.string.wrong), quiz.get(questionIndex).getAnswerMessage()); + } + } + + /** + * to display explanation after each answer, update questionIndex and move to next question + * @param title + * @param Message + */ + public void customAlert(String title, String Message) { + AlertDialog.Builder alert = new AlertDialog.Builder(this); + alert.setTitle(title); + alert.setMessage(Message); + alert.setPositiveButton(R.string.continue_message, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + questionIndex++; + if(questionIndex == quiz.size()){ + Intent i = new Intent(QuizActivity.this, QuizResultActivity.class); + dialog.dismiss(); + i.putExtra("QuizResult",score); + startActivity(i); + }else { + displayQuestion(); + } + } + }); + AlertDialog dialog = alert.create(); + dialog.show(); + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/quiz/QuizChecker.java b/app/src/main/java/fr/free/nrw/commons/quiz/QuizChecker.java new file mode 100644 index 000000000..b08563660 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/quiz/QuizChecker.java @@ -0,0 +1,160 @@ +package fr.free.nrw.commons.quiz; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.support.v7.app.AlertDialog.Builder; +import android.util.Log; + +import fr.free.nrw.commons.R; +import fr.free.nrw.commons.WelcomeActivity; +import fr.free.nrw.commons.mwapi.MediaWikiApi; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; +import timber.log.Timber; + +/** + * fetches the number of images uploaded and number of images reverted. + * Then it calculates the percentage of the images reverted + * if the percentage of images reverted after last quiz exceeds 50% and number of images uploaded is + * greater than 50, then quiz is popped up + */ +public class QuizChecker { + + private int revertCount ; + private int totalUploadCount ; + private boolean isRevertCountFetched; + private boolean isUploadCountFetched; + private CompositeDisposable compositeDisposable = new CompositeDisposable(); + public Context context; + private String userName; + private MediaWikiApi mediaWikiApi; + private SharedPreferences revertPref; + private SharedPreferences countPref; + + private final int UPLOAD_COUNT_THRESHOLD = 5; + private final String REVERT_PERCENTAGE_FOR_MESSAGE = "50%"; + private final String REVERT_SHARED_PREFERENCE = "revertCount"; + private final String UPLOAD_SHARED_PREFERENCE = "uploadCount"; + + /** + * constructor to set the parameters for quiz + * @param context + * @param userName + * @param mediaWikiApi + */ + public QuizChecker(Context context, String userName, MediaWikiApi mediaWikiApi) { + this.context = context; + this.userName = userName; + this.mediaWikiApi = mediaWikiApi; + revertPref = context.getSharedPreferences(REVERT_SHARED_PREFERENCE, Context.MODE_PRIVATE); + countPref = context.getSharedPreferences(UPLOAD_SHARED_PREFERENCE,Context.MODE_PRIVATE); + setUploadCount(); + setRevertCount(); + } + + /** + * to fet the total number of images uploaded + */ + private void setUploadCount() { + compositeDisposable.add(mediaWikiApi + .getUploadCount(userName) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + uploadCount -> setTotalUploadCount(uploadCount), + t -> Timber.e(t, "Fetching upload count failed") + )); + } + + /** + * set the sub Title of Contibutions Activity and + * call function to check for quiz + * @param uploadCount + */ + private void setTotalUploadCount(int uploadCount) { + totalUploadCount = uploadCount - countPref.getInt(UPLOAD_SHARED_PREFERENCE,0); + if( totalUploadCount < 0){ + totalUploadCount = 0; + countPref.edit().putInt(UPLOAD_SHARED_PREFERENCE,0).apply(); + } + isUploadCountFetched = true; + calculateRevertParameter(); + } + + /** + * To call the API to get reverts count in form of JSONObject + */ + private void setRevertCount() { + compositeDisposable.add(mediaWikiApi + .getRevertRespObjectSingle(userName) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + object -> setRevertParameter(object.getInt("deletedUploads")) + )); + } + + /** + * to calculate the number of images reverted after previous quiz + * @param revertCountFetched + */ + private void setRevertParameter(int revertCountFetched) { + revertCount = revertCountFetched - revertPref.getInt(REVERT_SHARED_PREFERENCE,0); + if(revertCount < 0){ + revertCount = 0; + revertPref.edit().putInt(REVERT_SHARED_PREFERENCE, 0).apply(); + } + isRevertCountFetched = true; + calculateRevertParameter(); + } + + /** + * to check whether the criterion to call quiz is satisfied + */ + private void calculateRevertParameter() { + if( revertCount < 0 || totalUploadCount < 0){ + revertPref.edit().putInt(REVERT_SHARED_PREFERENCE, 0).apply(); + countPref.edit().putInt(UPLOAD_SHARED_PREFERENCE,0).apply(); + return; + } + if (isRevertCountFetched && isUploadCountFetched && + totalUploadCount >= UPLOAD_COUNT_THRESHOLD && + (revertCount * 100) / totalUploadCount >= 50) { + callQuiz(); + } + } + + /** + * Alert which prompts to quiz + */ + public void callQuiz() { + Builder alert = new Builder(context); + alert.setTitle(context.getResources().getString(R.string.quiz)); + alert.setMessage(context.getResources().getString(R.string.quiz_alert_message, + REVERT_PERCENTAGE_FOR_MESSAGE)); + alert.setPositiveButton("Proceed", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + int newRevetSharedPrefs = revertCount+ revertPref.getInt(REVERT_SHARED_PREFERENCE,0); + revertPref.edit().putInt(REVERT_SHARED_PREFERENCE, newRevetSharedPrefs).apply(); + int newUploadCount = totalUploadCount + countPref.getInt(UPLOAD_SHARED_PREFERENCE,0); + countPref.edit().putInt(UPLOAD_SHARED_PREFERENCE,newUploadCount).apply(); + Intent i = new Intent(context, WelcomeActivity.class); + i.putExtra("isQuiz", true); + dialog.dismiss(); + context.startActivity(i); + } + }); + alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + dialogInterface.cancel(); + } + }); + android.support.v7.app.AlertDialog dialog = alert.create(); + dialog.show(); + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/quiz/QuizController.java b/app/src/main/java/fr/free/nrw/commons/quiz/QuizController.java new file mode 100644 index 000000000..fc6b4c15f --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/quiz/QuizController.java @@ -0,0 +1,64 @@ +package fr.free.nrw.commons.quiz; + +import android.content.Context; + +import java.util.ArrayList; + +import butterknife.BindView; +import fr.free.nrw.commons.R; + +/** + * controls the quiz in the Activity + */ +public class QuizController { + + ArrayList quiz = new ArrayList(); + + private final String URL_FOR_SELFIE = "https://i.imgur.com/0fMYcpM.jpg"; + private final String URL_FOR_TAJ_MAHAL = "https://upload.wikimedia.org/wikipedia/commons/1/15/Taj_Mahal-03.jpg"; + private final String URL_FOR_BLURRY_IMAGE = "https://i.imgur.com/Kepb5jR.jpg"; + private final String URL_FOR_SCREENSHOT = "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Social_media_app_mockup_screenshot.svg/500px-Social_media_app_mockup_screenshot.svg.png"; + private final String URL_FOR_EVENT = "https://upload.wikimedia.org/wikipedia/commons/5/51/HouseBuildingInNorthernVietnam.jpg"; + + public void initialize(Context context){ + QuizQuestion q1 = new QuizQuestion(1, + context.getResources().getString(R.string.quiz_question_string), + URL_FOR_SELFIE, + false, + context.getResources().getString(R.string.selfie_answer)); + quiz.add(q1); + + QuizQuestion q2 = new QuizQuestion(2, + context.getResources().getString(R.string.quiz_question_string), + URL_FOR_TAJ_MAHAL, + true, + context.getResources().getString(R.string.taj_mahal_answer)); + quiz.add(q2); + + QuizQuestion q3 = new QuizQuestion(3, + context.getResources().getString(R.string.quiz_question_string), + URL_FOR_BLURRY_IMAGE, + false, + context.getResources().getString(R.string.blurry_image_answer)); + quiz.add(q3); + + QuizQuestion q4 = new QuizQuestion(4, + context.getResources().getString(R.string.quiz_question_string), + URL_FOR_SCREENSHOT, + false, + context.getResources().getString(R.string.screenshot_answer)); + quiz.add(q4); + + QuizQuestion q5 = new QuizQuestion(5, + context.getResources().getString(R.string.quiz_question_string), + URL_FOR_EVENT, + true, + context.getResources().getString(R.string.construction_event_answer)); + quiz.add(q5); + + } + + public ArrayList getQuiz() { + return quiz; + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/quiz/QuizQuestion.java b/app/src/main/java/fr/free/nrw/commons/quiz/QuizQuestion.java new file mode 100644 index 000000000..a21498a40 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/quiz/QuizQuestion.java @@ -0,0 +1,62 @@ +package fr.free.nrw.commons.quiz; + +import android.net.Uri; + +/** + * class contains information about all the quiz questions + */ +public class QuizQuestion { + private int questionNumber; + private String question; + private boolean answer; + private String url; + private String answerMessage; + + QuizQuestion(int questionNumber, String question, String url, boolean answer, String answerMessage){ + this.questionNumber = questionNumber; + this.question = question; + this.url = url; + this.answer = answer; + this.answerMessage = answerMessage; + } + + public Uri getUrl() { + return Uri.parse(url); + } + + public void setUrl(String url) { + this.url = url; + } + + public boolean isAnswer() { + return answer; + } + + public int getQuestionNumber() { + return questionNumber; + } + + public String getQuestion() { + return question; + } + + public void setAnswer(boolean answer) { + this.answer = answer; + } + + public void setQuestion(String question) { + this.question = question; + } + + public void setQuestionNumber(int questionNumber) { + this.questionNumber = questionNumber; + } + + public String getAnswerMessage() { + return answerMessage; + } + + public void setAnswerMessage(String answerMessage) { + this.answerMessage = answerMessage; + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/quiz/QuizResultActivity.java b/app/src/main/java/fr/free/nrw/commons/quiz/QuizResultActivity.java new file mode 100644 index 000000000..e7c40f615 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/quiz/QuizResultActivity.java @@ -0,0 +1,199 @@ +package fr.free.nrw.commons.quiz; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.util.Log; + +import com.dinuscxj.progressbar.CircleProgressBar; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import fr.free.nrw.commons.R; +import fr.free.nrw.commons.contributions.ContributionsActivity; + +import android.support.v7.widget.Toolbar; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import java.io.File; +import java.io.FileOutputStream; + +/** + * Displays the final score of quiz and congratulates the user + */ +public class QuizResultActivity extends AppCompatActivity { + + @BindView(R.id.result_progress_bar) CircleProgressBar resultProgressBar; + @BindView(R.id.toolbar) Toolbar toolbar; + @BindView(R.id.congratulatory_message) TextView congratulatoryMessageText; + + private final int NUMBER_OF_QUESTIONS = 5; + private final int MULTIPLIER_TO_GET_PERCENTAGE = 20; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_quiz_result); + + ButterKnife.bind(this); + setSupportActionBar(toolbar); + + if( getIntent() != null) { + Bundle extras = getIntent().getExtras(); + int score = extras.getInt("QuizResult"); + setScore(score); + }else{ + startActivityWithFlags( + this, ContributionsActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP, + Intent.FLAG_ACTIVITY_SINGLE_TOP); + super.onBackPressed(); + } + } + + /** + * to calculate and display percentage and score + * @param score + */ + public void setScore(int score) { + int per = score * MULTIPLIER_TO_GET_PERCENTAGE; + resultProgressBar.setProgress(per); + resultProgressBar.setProgressTextFormatPattern(score +" / " + NUMBER_OF_QUESTIONS); + String message = getResources().getString(R.string.congratulatory_message_quiz,per + "%"); + congratulatoryMessageText.setText(message); + } + + /** + * to go to Contibutions Activity + */ + @OnClick(R.id.quiz_result_next) + public void launchContributionActivity(){ + startActivityWithFlags( + this, ContributionsActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP, + Intent.FLAG_ACTIVITY_SINGLE_TOP); + } + + @Override + public void onBackPressed() { + startActivityWithFlags( + this, ContributionsActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP, + Intent.FLAG_ACTIVITY_SINGLE_TOP); + super.onBackPressed(); + } + + /** + * Function to call intent to an activity + * @param context + * @param cls + * @param flags + * @param + */ + public static void startActivityWithFlags(Context context, Class cls, int... flags) { + Intent intent = new Intent(context, cls); + for (int flag: flags) { + intent.addFlags(flag); + } + context.startActivity(intent); + } + + /** + * to inflate menu + * @param menu + * @return + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_about, menu); + return true; + } + + /** + * if share option selected then take screenshot and launch alert + * @param item + * @return + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + if (id == R.id.share_app_icon) { + View rootView = getWindow().getDecorView().findViewById(android.R.id.content); + Bitmap screenShot = getScreenShot(rootView); + showAlert(screenShot); + } + + return super.onOptionsItemSelected(item); + } + + /** + * to store the screenshot of image in bitmap variable temporarily + * @param view + * @return + */ + public static Bitmap getScreenShot(View view) { + View screenView = view.getRootView(); + screenView.setDrawingCacheEnabled(true); + Bitmap bitmap = Bitmap.createBitmap(screenView.getDrawingCache()); + screenView.setDrawingCacheEnabled(false); + return bitmap; + } + + /** + * share the screenshot through social media + * @param bitmap + */ + void shareScreen(Bitmap bitmap) { + try { + File file = new File(this.getExternalCacheDir(),"screen.png"); + FileOutputStream fOut = new FileOutputStream(file); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut); + fOut.flush(); + fOut.close(); + file.setReadable(true, false); + final Intent intent = new Intent(android.content.Intent.ACTION_SEND); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file)); + intent.setType("image/png"); + startActivity(Intent.createChooser(intent, "Share image via")); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * It display the alertDialog with Image of screenshot + * @param screenshot + */ + public void showAlert(Bitmap screenshot) { + AlertDialog.Builder alertadd = new AlertDialog.Builder(QuizResultActivity.this); + LayoutInflater factory = LayoutInflater.from(QuizResultActivity.this); + final View view = factory.inflate(R.layout.image_alert_layout, null); + ImageView screenShotImage = (ImageView) view.findViewById(R.id.alert_image); + screenShotImage.setImageBitmap(screenshot); + TextView shareMessage = (TextView) view.findViewById(R.id.alert_text); + shareMessage.setText(R.string.quiz_result_share_message); + alertadd.setView(view); + alertadd.setPositiveButton("Proceed", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + shareScreen(screenshot); + } + }); + alertadd.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + alertadd.show(); + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/quiz/RadioGroupHelper.java b/app/src/main/java/fr/free/nrw/commons/quiz/RadioGroupHelper.java new file mode 100644 index 000000000..9d907fc08 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/quiz/RadioGroupHelper.java @@ -0,0 +1,64 @@ +package fr.free.nrw.commons.quiz; + +import android.app.Activity; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.RadioButton; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used to group to or more radio buttons to ensure + * that at a particular time only one of them is selected + */ +public class RadioGroupHelper { + + public List radioButtons = new ArrayList<>(); + + /** + * Constructor to group radio buttons + * @param radios + */ + public RadioGroupHelper(RadioButton... radios) { + super(); + for (RadioButton rb : radios) { + add(rb); + } + } + + /** + * Constructor to group radio buttons + * @param activity + * @param radiosIDs + */ + public RadioGroupHelper(Activity activity, int... radiosIDs) { + this(activity.findViewById(android.R.id.content),radiosIDs); + } + + /** + * Constructor to group radio buttons + * @param rootView + * @param radiosIDs + */ + public RadioGroupHelper(View rootView, int... radiosIDs) { + super(); + for (int radioButtonID : radiosIDs) { + add((RadioButton)rootView.findViewById(radioButtonID)); + } + } + + private void add(CompoundButton button){ + this.radioButtons.add(button); + button.setOnClickListener(onClickListener); + } + + /** + * listener to ensure only one of the radio button is selected + */ + View.OnClickListener onClickListener = v -> { + for (CompoundButton rb : radioButtons) { + if(rb != v) rb.setChecked(false); + } + }; +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_clear_black_24dp.xml b/app/src/main/res/drawable/ic_clear_black_24dp.xml new file mode 100644 index 000000000..8b45f42a3 --- /dev/null +++ b/app/src/main/res/drawable/ic_clear_black_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_done_black_24dp.xml b/app/src/main/res/drawable/ic_done_black_24dp.xml new file mode 100644 index 000000000..b28231b6e --- /dev/null +++ b/app/src/main/res/drawable/ic_done_black_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_quiz.xml b/app/src/main/res/layout/activity_quiz.xml new file mode 100644 index 000000000..cc8f75781 --- /dev/null +++ b/app/src/main/res/layout/activity_quiz.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + +