diff --git a/***MY README***.md b/***MY README***.md new file mode 100644 index 0000000..47f1ba1 --- /dev/null +++ b/***MY README***.md @@ -0,0 +1,13 @@ +My solution consists an android app as well as a nodejs backend + +The `AirtimeRewards` folder contains the android project + +The `airtime-rewards-backend` folder contains the nodejs backend + +My solution is an app that gives people airtime rewards for answering surveys created by organisations + +I'm making use of the following Africa's Talking APIs +1) Airtime +2) SMS + +Please Enjoy! \ No newline at end of file diff --git a/AirtimeRewards/.gitignore b/AirtimeRewards/.gitignore new file mode 100644 index 0000000..5edb4ee --- /dev/null +++ b/AirtimeRewards/.gitignore @@ -0,0 +1,10 @@ +*.iml +.gradle +/local.properties +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/AirtimeRewards/.idea/assetWizardSettings.xml b/AirtimeRewards/.idea/assetWizardSettings.xml new file mode 100644 index 0000000..524d57f --- /dev/null +++ b/AirtimeRewards/.idea/assetWizardSettings.xml @@ -0,0 +1,47 @@ + + + + + + \ No newline at end of file diff --git a/AirtimeRewards/.idea/caches/build_file_checksums.ser b/AirtimeRewards/.idea/caches/build_file_checksums.ser new file mode 100644 index 0000000..01c7485 Binary files /dev/null and b/AirtimeRewards/.idea/caches/build_file_checksums.ser differ diff --git a/AirtimeRewards/.idea/codeStyles/Project.xml b/AirtimeRewards/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..30aa626 --- /dev/null +++ b/AirtimeRewards/.idea/codeStyles/Project.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AirtimeRewards/.idea/dictionaries/michael.xml b/AirtimeRewards/.idea/dictionaries/michael.xml new file mode 100644 index 0000000..7532c9e --- /dev/null +++ b/AirtimeRewards/.idea/dictionaries/michael.xml @@ -0,0 +1,7 @@ + + + + firebase + + + \ No newline at end of file diff --git a/AirtimeRewards/.idea/gradle.xml b/AirtimeRewards/.idea/gradle.xml new file mode 100644 index 0000000..7ac24c7 --- /dev/null +++ b/AirtimeRewards/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/AirtimeRewards/.idea/misc.xml b/AirtimeRewards/.idea/misc.xml new file mode 100644 index 0000000..c24c0fc --- /dev/null +++ b/AirtimeRewards/.idea/misc.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/AirtimeRewards/.idea/runConfigurations.xml b/AirtimeRewards/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/AirtimeRewards/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/AirtimeRewards/app/.gitignore b/AirtimeRewards/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/AirtimeRewards/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/AirtimeRewards/app/build.gradle b/AirtimeRewards/app/build.gradle new file mode 100644 index 0000000..a6ada45 --- /dev/null +++ b/AirtimeRewards/app/build.gradle @@ -0,0 +1,88 @@ +apply plugin: 'com.android.application' + +apply plugin: 'kotlin-android' + +apply plugin: 'kotlin-android-extensions' + +android { + compileSdkVersion 27 + defaultConfig { + applicationId "com.makerloom.airtimerewards" + minSdkVersion 16 + targetSdkVersion 27 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + multiDexEnabled true + vectorDrawables.useSupportLibrary = true + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + release {} + } + dexOptions { + jumboMode true + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + sourceSets { + main { + res.srcDirs = [ + 'src/main/res', + 'src/main/res/menu' + ] + } + } +} + +ext { + supportLibVersion = '27.1.1' +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" + + // Support Libraries + implementation "com.android.support:appcompat-v7:$supportLibVersion" + implementation "com.android.support:design:$supportLibVersion" + implementation "com.android.support:recyclerview-v7:$supportLibVersion" + implementation "com.android.support:support-annotations:$supportLibVersion" + implementation "com.android.support:cardview-v7:$supportLibVersion" + implementation "com.android.support:support-v4:$supportLibVersion" + implementation "com.android.support:support-vector-drawable:$supportLibVersion" + implementation 'com.android.support:multidex:1.0.3' + implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'com.google.android:flexbox:0.3.2' + + // Firebase & Google Libraries + implementation 'com.google.firebase:firebase-core:16.0.4' + implementation 'com.google.firebase:firebase-functions:16.1.2' + implementation 'com.google.firebase:firebase-firestore:17.1.2' + implementation 'com.google.firebase:firebase-auth:16.0.5' // 16.0.1 + implementation 'com.firebaseui:firebase-ui-auth:4.1.0' // 3.1.2 + + // Miscellaneous Libraries + implementation 'com.github.medyo:fancybuttons:1.8.4' + implementation 'com.karumi:dexter:4.2.0' + implementation 'com.stepstone.stepper:material-stepper:4.3.1' + implementation 'com.github.bumptech.glide:glide:4.5.0' + implementation 'com.google.code.gson:gson:2.8.2' + implementation 'com.squareup.retrofit2:retrofit:2.4.0' + implementation 'com.squareup.retrofit2:converter-gson:2.4.0' + + // Testing Libraries + implementation 'com.android.support:support-v4:27.1.1' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' +} + +apply plugin: 'com.google.gms.google-services' + +com.google.gms.googleservices.GoogleServicesPlugin.config.disableVersionCheck = true diff --git a/AirtimeRewards/app/google-services.json b/AirtimeRewards/app/google-services.json new file mode 100644 index 0000000..382904c --- /dev/null +++ b/AirtimeRewards/app/google-services.json @@ -0,0 +1,55 @@ +{ + "project_info": { + "project_number": "724456800518", + "firebase_url": "https://airtime-rewards.firebaseio.com", + "project_id": "airtime-rewards", + "storage_bucket": "airtime-rewards.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:724456800518:android:2c170b2df8c5b0c7", + "android_client_info": { + "package_name": "com.makerloom.airtimerewards" + } + }, + "oauth_client": [ + { + "client_id": "724456800518-12b1mp2ktot47be5v5umd8gi2r4i1pf3.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.makerloom.airtimerewards", + "certificate_hash": "7d4d3d266a5fcf4953e8806abe4acc99db9b187c" + } + }, + { + "client_id": "724456800518-vb1qk675cg9k5mk7ns89l431kor9414h.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyDEYU_e6C9SDwH4GAiZ7Ulj8xXlcSFPmT4" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "724456800518-vb1qk675cg9k5mk7ns89l431kor9414h.apps.googleusercontent.com", + "client_type": 3 + } + ] + }, + "ads_service": { + "status": 2 + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/AirtimeRewards/app/proguard-rules.pro b/AirtimeRewards/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/AirtimeRewards/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 diff --git a/AirtimeRewards/app/src/androidTest/java/com/makerloom/airtimerewards/ExampleInstrumentedTest.kt b/AirtimeRewards/app/src/androidTest/java/com/makerloom/airtimerewards/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..ef56289 --- /dev/null +++ b/AirtimeRewards/app/src/androidTest/java/com/makerloom/airtimerewards/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.makerloom.airtimerewards + +import android.support.test.InstrumentationRegistry +import android.support.test.runner.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getTargetContext() + assertEquals("com.makerloom.airtimerewards", appContext.packageName) + } +} diff --git a/AirtimeRewards/app/src/main/AndroidManifest.xml b/AirtimeRewards/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..169046b --- /dev/null +++ b/AirtimeRewards/app/src/main/AndroidManifest.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/CompleteActivity.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/CompleteActivity.kt new file mode 100644 index 0000000..b9cae8c --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/CompleteActivity.kt @@ -0,0 +1,17 @@ +package com.makerloom.airtimerewards + +import android.os.Bundle +import android.support.design.widget.Snackbar +import android.support.v7.app.AppCompatActivity + +import kotlinx.android.synthetic.main.activity_complete.* + +class CompleteActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_complete) + setSupportActionBar(toolbar) + } + +} diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/MainActivity.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/MainActivity.kt new file mode 100644 index 0000000..da3fb7c --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/MainActivity.kt @@ -0,0 +1,121 @@ +package com.makerloom.airtimerewards + +import android.app.ProgressDialog +import android.content.Intent +import android.graphics.Typeface +import android.os.Bundle +import android.os.Handler +import android.support.design.widget.Snackbar +import android.support.v7.app.AppCompatActivity +import com.google.gson.Gson +import com.makerloom.airtimerewards.api.API +import com.makerloom.airtimerewards.api.APIService +import com.makerloom.airtimerewards.models.Question +import com.makerloom.airtimerewards.models.RequestSurveyRequest +import com.makerloom.airtimerewards.models.RequestSurveyResponse +import com.makerloom.airtimerewards.models.Survey +import com.makerloom.airtimerewards.utils.JsonUtils +import com.makerloom.airtimerewards.utils.SnackbarUtils + +import kotlinx.android.synthetic.main.activity_main.* +import kotlinx.android.synthetic.main.content_main.* +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + +class MainActivity : AppCompatActivity() { + + val searchButtonFontName = "sans-serif-light" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + setSupportActionBar(toolbar) + + look_for_survey_btn.textViewObject.typeface = Typeface.create(searchButtonFontName, Typeface.BOLD_ITALIC) + look_for_survey_btn.setOnClickListener { + checkForSurveys() + } + } + + private var progressDialog: ProgressDialog? = null + + fun startSurveyProgress () { + progressDialog = ProgressDialog(this@MainActivity) + + progressDialog?.apply { + setMessage("Looking For Surveys ...") + setCancelable(false) + setProgressStyle(ProgressDialog.STYLE_SPINNER) + show() + } + } + + fun stopSurveyProgress () { + try { + progressDialog?.apply { + hide() + dismiss() + } + } + catch (ex: Exception) { + ex.printStackTrace() + } + } + + + fun checkForSurveys () { + startSurveyProgress() + + val surveyRequest = RequestSurveyRequest(true) + + val call: Call = API.Service.requestSurvey(surveyRequest) + + call + .enqueue(object : Callback { + override fun onFailure(call: Call?, t: Throwable?) { + SnackbarUtils.showSnackbar(this@MainActivity, "We were unable to load a survey. Please try again later.") + stopSurveyProgress() + } + + override fun onResponse(call: Call?, response: Response?) { + response?.apply { + if (isSuccessful) { + loadSurvey(body()) + } + else { + SnackbarUtils.showSnackbar(this@MainActivity, "We were unable to load a survey. Please try again later.") + } + } + stopSurveyProgress() + } + }) + +// Handler().postDelayed(Runnable { +// stopSurveyProgress() +// +// val gson = Gson() +// val questions = ArrayList() +// val dummyQuestionsArray = JsonUtils.loadJsonArrayFromAsset(applicationContext, R.raw.questions) +// dummyQuestionsArray?.let { +// for (i in 0 until dummyQuestionsArray.length()) { +// val qJson = dummyQuestionsArray[i] +// val question = gson.fromJson(qJson.toString(), Question::class.java) +// questions.add(question) +// } +// val dummyResponse = RequestSurveyResponse(true, Survey(questions)) +// loadSurvey(dummyResponse) +// } +// }, 4000) + } + + fun loadSurvey (response: RequestSurveyResponse?) { + val survey = Intent(this@MainActivity, SurveyActivity::class.java) + + val gson = Gson() + survey.putExtra(SurveyActivity.SURVEY_DATA_KEY, gson.toJson(response)) + + startActivity(survey) + } + +} diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/MyStepperAdapter.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/MyStepperAdapter.kt new file mode 100644 index 0000000..4f35e6f --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/MyStepperAdapter.kt @@ -0,0 +1,39 @@ +package com.makerloom.airtimerewards + +import android.support.v4.app.FragmentManager +import android.content.Context +import android.util.Log +import com.makerloom.airtimerewards.models.Survey +import com.stepstone.stepper.Step + +import com.stepstone.stepper.adapter.AbstractFragmentStepAdapter +import com.stepstone.stepper.viewmodel.StepViewModel + +class MyStepperAdapter(fragmentManager: FragmentManager, context: Context, survey: Survey) : AbstractFragmentStepAdapter(fragmentManager, context) { + private var survey: Survey + + init { + this.survey = survey + } + + override fun getCount(): Int { + return survey.questions.size + } + + override fun createStep(position: Int): Step { + val questionFragment = SurveyQuestionFragment.newInstance(position, survey.questions.get(position)) + Log.d(TAG, questionFragment.toString()) + + return questionFragment + } + + override fun getViewModel(position: Int): StepViewModel { + return StepViewModel.Builder(context) + .setTitle("Question ${position + 1}") + .create() + } + + companion object { + val TAG = MyStepperAdapter::class.java.simpleName + } +} diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/SurveyActivity.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/SurveyActivity.kt new file mode 100644 index 0000000..d562178 --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/SurveyActivity.kt @@ -0,0 +1,267 @@ +package com.makerloom.airtimerewards + +import android.app.ActionBar +import android.app.ProgressDialog +import android.content.DialogInterface +import android.content.Intent +import android.net.Uri +import android.support.v7.app.AppCompatActivity +import android.os.Bundle +import android.os.Handler +import android.support.v7.app.AlertDialog +import android.util.DisplayMetrics +import android.util.Log +import android.util.TypedValue +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.widget.EditText +import android.widget.LinearLayout +import com.google.gson.Gson +import com.makerloom.airtimerewards.api.API +import com.makerloom.airtimerewards.models.* +import com.makerloom.airtimerewards.utils.DialogUtils +import com.makerloom.airtimerewards.utils.PhoneUtils +import com.makerloom.airtimerewards.utils.SnackbarUtils +import com.makerloom.airtimerewards.utils.SurveyUtils +import com.makerloom.airtimerewards.utils.SurveyUtils.Companion.buildQuestionView +import com.stepstone.stepper.StepperLayout +import com.stepstone.stepper.VerificationError +import kotlinx.android.synthetic.main.activity_main.* +import kotlinx.android.synthetic.main.content_survey.* +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + +class SurveyActivity : AppCompatActivity(), StepperLayout.StepperListener, SurveyQuestionFragment.OnFragmentInteractionListener { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_survey) + setSupportActionBar(toolbar) + supportActionBar?.apply { + setDisplayHomeAsUpEnabled(true) + setHomeButtonEnabled(true) + } + + val surveyData = intent.getStringExtra(SurveyActivity.SURVEY_DATA_KEY) + Log.d(TAG, surveyData) + + val gson = Gson() + val response = gson.fromJson(surveyData, RequestSurveyResponse::class.java) + + stepper_layout.adapter = MyStepperAdapter(supportFragmentManager, this@SurveyActivity, response.survey) + stepper_layout.setListener(this@SurveyActivity) + } + + fun getAmountAndPhoneNumber(post: Runnable) { + amount = getAmount() + + val params = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT) + + val editText = EditText(this@SurveyActivity) + editText.apply { + setSingleLine(true) + setText(DEFAULT_PHONE_NUMBER) + setSelection(DEFAULT_PHONE_NUMBER.length) + layoutParams = params + } + + val layout = LinearLayout(this@SurveyActivity) + layout.apply { + addView(editText) + layoutParams = params + setPadding(paddingLeft + 36, paddingTop, paddingRight + 36, paddingBottom) + } + + var alertDialog: AlertDialog? = null + val alertDialogBuilder = AlertDialog.Builder(this@SurveyActivity) + .setTitle("Survey Completed") + .setMessage("What phone number do you want your airtime reward sent to?") + .setView(layout) + .setCancelable(true) + .setPositiveButton("Continue", object: DialogInterface.OnClickListener { + override fun onClick(dialog: DialogInterface?, which: Int) { + val phoneNumber = editText.text.toString() + Log.d(TAG,"phoneNumber = $phoneNumber\namount = $amount") + if (PhoneUtils.isValid(phoneNumber)) { + this@SurveyActivity.phoneNumber = phoneNumber + post.run() + } + else { + SnackbarUtils.showSnackbar(this@SurveyActivity, "Please enter a valid phone number") + DialogUtils.dismiss(alertDialog) + getAmountAndPhoneNumber(post) + } + } + }) + + alertDialog = alertDialogBuilder.create() + alertDialog.show() + } + + private var amount: Int = getAmount() + + private var phoneNumber: String = DEFAULT_PHONE_NUMBER + + override fun onCompleted(completeButton: View?) { + getAmountAndPhoneNumber(Runnable { + showProgress() + sendAirtime(amount, phoneNumber) + Handler().postDelayed(Runnable { + sendConfirmationText(amount, phoneNumber) + }, 500) + }) + } + + fun goToCompleteActivity () { + startActivity(Intent(this@SurveyActivity, CompleteActivity::class.java)) + finish() + } + + override fun onError(verificationError: VerificationError?) { + Log.d(TAG, "onError $verificationError") + } + + override fun onStepSelected(newStepPosition: Int) { + Log.d(TAG, "onStepSelected $newStepPosition") + } + + override fun onReturn() { + Log.d(TAG, "onReturn") + finish() + } + + override fun onFragmentInteraction(uri: Uri) { + Log.d(TAG, "onFragmentInteraction $uri") + } + + var airtimeRequestComplete = false + fun sendAirtime ( amount: Int, phoneNumber: String) { + airtimeRequestComplete = false + val request = SendAirtimeRequest(true, amount, phoneNumber = phoneNumber, to = phoneNumber) + + val call = API.Service.sendAirtime(request) + call.enqueue(object : Callback { + override fun onFailure(call: Call?, t: Throwable?) { + Log.d(TAG, "onFailure Airtime") + SnackbarUtils.showSnackbar(this@SurveyActivity, "An error occured while submitting the survey, Please try again later") + complete() + } + + override fun onResponse(call: Call?, response: Response?) { + complete() + Log.d(TAG, "onResponse Airtime") + Log.d(TAG, response?.toString()) + response?.apply { + Log.d(TAG, response.body().toString()) + if (isSuccessful && isSuccessful(response.body())) { + Log.d(TAG, "Send Airtime Success") + goToCompleteActivity() + } + else { + SnackbarUtils.showSnackbar(this@SurveyActivity, "An error occured while submitting the survey, Please try again later") + } + } + } + + fun complete () { + airtimeRequestComplete = true + hideProgress() + } + }) + } + + var textRequestComplete = false + fun sendConfirmationText (amount: Int, phoneNumber: String) { + textRequestComplete = false + val request = SendTextRequest(true, amount, to = phoneNumber, phoneNumber = phoneNumber) + + val call = API.Service.sendText(request) + call.enqueue(object : Callback { + override fun onFailure(call: Call?, t: Throwable?) { + Log.d(TAG, "onFailure Text") + SnackbarUtils.showSnackbar(this@SurveyActivity, "An error occured while submitting the survey, Please try again later") + complete() + } + + override fun onResponse(call: Call?, response: Response?) { + complete() + Log.d(TAG, "onResponse Text") + Log.d(TAG, response?.toString()) + response?.apply { + Log.d(TAG, response.body().toString()) + if (!isSuccessful || !isSuccessful(response.body())) { + SnackbarUtils.showSnackbar(this@SurveyActivity, "An error occured while submitting the survey, Please try again later") + } + else { + Log.d(TAG, "Send Text Success") + } + } + } + + fun complete () { + textRequestComplete = true + hideProgress() + } + }) + } + + fun isSuccessful (response: SendAirtimeResponse?): Boolean { + return response != null && response.success + } + + fun isSuccessful (response: SendTextResponse?): Boolean { + return response != null && response.success + } + + fun bothRequestsComplete (): Boolean { + return textRequestComplete && airtimeRequestComplete + } + + private var progressDialog: ProgressDialog? = null + + fun hideProgress () { + if (bothRequestsComplete()) { + try { + progressDialog?.apply { + hide() + dismiss() + } + } catch (ex: Exception) { + ex.printStackTrace() + } + } + } + + fun showProgress () { + progressDialog = ProgressDialog(this@SurveyActivity) + progressDialog?.apply { + setMessage("Submitting Survey Answers & Processing Airtime Reward ...") + setCancelable(false) + setProgressStyle(ProgressDialog.STYLE_SPINNER) + show() + } + } + + companion object { + val SURVEY_DATA_KEY = "SURVEY_DATA" + + val TAG = SurveyActivity::class.java.simpleName + + val DEFAULT_PHONE_NUMBER = "+2349034099658" + } + + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + if (item?.itemId == android.R.id.home) { + onBackPressed() + return true + } + + return super.onOptionsItemSelected(item) + } + + fun getAmount(): Int { + return 50 + } +} diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/SurveyQuestionFragment.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/SurveyQuestionFragment.kt new file mode 100644 index 0000000..7660626 --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/SurveyQuestionFragment.kt @@ -0,0 +1,104 @@ +package com.makerloom.airtimerewards + +import android.content.Context +import android.net.Uri +import android.os.Bundle +import android.support.v4.app.Fragment +import android.support.v7.app.AppCompatActivity +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.gson.Gson +import com.makerloom.airtimerewards.models.Question +import com.makerloom.airtimerewards.utils.SurveyUtils +import com.stepstone.stepper.Step +import com.stepstone.stepper.VerificationError +import java.text.FieldPosition + + +// TODO: Rename parameter arguments, choose names that match +// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER +private const val POSITION_KEY = "param1" +private const val QUESTION_KEY = "param2" + +class SurveyQuestionFragment : Fragment(), Step { + // TODO: Rename and change types of parameters + private var position: Int? = null + private var question: Question? = null + private var listener: OnFragmentInteractionListener? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.let { + position = it.getInt(POSITION_KEY) + + val gson = Gson() + question = gson.fromJson(it.getString(QUESTION_KEY), Question::class.java) + } + } + + override fun verifyStep(): VerificationError? { + Log.d(TAG, "verifyStep") + return null + } + + override fun onError(error: VerificationError) { + Log.d(TAG, "onError") + } + + override fun onSelected() { + Log.d(TAG, "onSelected") + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + // Inflate the layout for this fragment + val view = inflater.inflate(R.layout.fragment_survey_question, container, false) + + (view as ViewGroup).addView(SurveyUtils.buildQuestionView(activity as AppCompatActivity, + question!!)) + + return view + } + + // TODO: Rename method, update argument and hook method into UI event + fun onButtonPressed(uri: Uri) { + listener?.onFragmentInteraction(uri) + } + + override fun onAttach(context: Context) { + super.onAttach(context) + if (context is OnFragmentInteractionListener) { + listener = context + } else { + throw RuntimeException(context.toString() + " must implement OnFragmentInteractionListener") + } + } + + override fun onDetach() { + super.onDetach() + listener = null + } + + interface OnFragmentInteractionListener { + // TODO: Update argument type and name + fun onFragmentInteraction(uri: Uri) + } + + companion object { + val TAG = SurveyQuestionFragment::class.java.simpleName + + // TODO: Rename and change types and number of parameters + @JvmStatic + fun newInstance(position: Int, question: Question) = + SurveyQuestionFragment().apply { + arguments = Bundle().apply { + putInt(POSITION_KEY, position) + + val gson = Gson() + putString(QUESTION_KEY, gson.toJson(question)) + } + } + } +} diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/api/API.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/api/API.kt new file mode 100644 index 0000000..9b79990 --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/api/API.kt @@ -0,0 +1,66 @@ +package com.makerloom.airtimerewards.api + +import android.util.Log + +import java.util.Locale +import java.util.concurrent.TimeUnit + +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +object API { + private val DEFAULT_TIMEOUT_IN_SECONDS = 30 + + private val DEBUG = !true + + private val TAG = API::class.java.simpleName + + private val PROTOCOL = "http" + private val DEBUG_IP_ADDR = "192.168.43.121" + private val DEBUG_DOMAIN = "$DEBUG_IP_ADDR:3000" + private val PRODUCTION_DOMAIN = "airtime-rewards-backend.herokuapp.com" + private val DEBUG_BASE_URL = String.format(Locale.getDefault(), "%s://%s", PROTOCOL, DEBUG_DOMAIN) + private val PRODUCTION_BASE_URL = String.format(Locale.getDefault(), "%s://%s", PROTOCOL, PRODUCTION_DOMAIN) + private val ROUTE = "/api/" + + val baseUrl: String + get() { + val url: String + if (DEBUG) { + url = DEBUG_BASE_URL + } else { + url = PRODUCTION_BASE_URL + } + + Log.d(TAG, url) + return url + } + + val url: String + get() { + val url: String + if (DEBUG) { + url = DEBUG_BASE_URL + ROUTE + } else { + url = PRODUCTION_BASE_URL + ROUTE + } + + Log.d(TAG, url) + return url + } + + private val okHttpClient = OkHttpClient.Builder() + .connectTimeout(DEFAULT_TIMEOUT_IN_SECONDS.toLong(), TimeUnit.SECONDS) + .writeTimeout(DEFAULT_TIMEOUT_IN_SECONDS.toLong(), TimeUnit.SECONDS) + .readTimeout(DEFAULT_TIMEOUT_IN_SECONDS.toLong(), TimeUnit.SECONDS) + .build() + + private val retrofit = Retrofit.Builder() + .baseUrl(url) + .addConverterFactory(GsonConverterFactory.create()) + .client(okHttpClient) + .build() + + val Service = retrofit.create(APIService::class.java) +} \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/api/APIService.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/api/APIService.kt new file mode 100644 index 0000000..d0e35b9 --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/api/APIService.kt @@ -0,0 +1,23 @@ +package com.makerloom.airtimerewards.api + +import com.makerloom.airtimerewards.models.RequestSurveyRequest +import com.makerloom.airtimerewards.models.RequestSurveyResponse +import com.makerloom.airtimerewards.models.SendAirtimeRequest +import com.makerloom.airtimerewards.models.SendAirtimeResponse +import com.makerloom.airtimerewards.models.SendTextRequest +import com.makerloom.airtimerewards.models.SendTextResponse + +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.POST + +interface APIService { + @POST("sendAirtime") + fun sendAirtime(@Body request: SendAirtimeRequest): Call + + @POST("sendText") + fun sendText(@Body request: SendTextRequest): Call + + @POST("requestSurvey") + fun requestSurvey(@Body request: RequestSurveyRequest): Call +} \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/Question.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/Question.kt new file mode 100644 index 0000000..53209d0 --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/Question.kt @@ -0,0 +1,10 @@ +package com.makerloom.airtimerewards.models + +data class Question (val questionText: String, + val imageUrl: String? = null, + + val inputType: Boolean? = false, + val radioButtonType: Boolean? = false, + val checkBoxType: Boolean? = false, + + val options: ArrayList? = null) \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/Request.java b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/Request.java new file mode 100644 index 0000000..8318569 --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/Request.java @@ -0,0 +1,5 @@ +package com.makerloom.airtimerewards.models; + +public class Request { + Request () {} +} diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/RequestSurveyRequest.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/RequestSurveyRequest.kt new file mode 100644 index 0000000..2f35d4c --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/RequestSurveyRequest.kt @@ -0,0 +1,3 @@ +package com.makerloom.airtimerewards.models + +data class RequestSurveyRequest (val success: Boolean) : Request() diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/RequestSurveyResponse.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/RequestSurveyResponse.kt new file mode 100644 index 0000000..36fef21 --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/RequestSurveyResponse.kt @@ -0,0 +1,4 @@ +package com.makerloom.airtimerewards.models + +data class RequestSurveyResponse (val success: Boolean, + val survey: Survey) : Response() diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/Response.java b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/Response.java new file mode 100644 index 0000000..59e38ee --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/Response.java @@ -0,0 +1,5 @@ +package com.makerloom.airtimerewards.models; + +public class Response { + Response () {} +} diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/SendAirtimeRequest.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/SendAirtimeRequest.kt new file mode 100644 index 0000000..b7d9fb7 --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/SendAirtimeRequest.kt @@ -0,0 +1,6 @@ +package com.makerloom.airtimerewards.models + +data class SendAirtimeRequest(val success: Boolean, + val amount: Int, + val phoneNumber: String, + val to: String) : Request() diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/SendAirtimeResponse.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/SendAirtimeResponse.kt new file mode 100644 index 0000000..437d7a0 --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/SendAirtimeResponse.kt @@ -0,0 +1,5 @@ +package com.makerloom.airtimerewards.models + +data class SendAirtimeResponse(val success: Boolean, + val amount: Int, + val phoneNumber: String): Response() \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/SendTextRequest.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/SendTextRequest.kt new file mode 100644 index 0000000..3f796e1 --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/SendTextRequest.kt @@ -0,0 +1,6 @@ +package com.makerloom.airtimerewards.models + +data class SendTextRequest(val success: Boolean, + val amount: Int, + val to: String, + val phoneNumber: String) : Request() \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/SendTextResponse.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/SendTextResponse.kt new file mode 100644 index 0000000..1193711 --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/SendTextResponse.kt @@ -0,0 +1,5 @@ +package com.makerloom.airtimerewards.models + +data class SendTextResponse (val success: Boolean, + val amount: Int, + val to: String) : Response() diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/Survey.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/Survey.kt new file mode 100644 index 0000000..69dd918 --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/models/Survey.kt @@ -0,0 +1,3 @@ +package com.makerloom.airtimerewards.models + +data class Survey (val questions: ArrayList) \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/utils/DialogUtils.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/utils/DialogUtils.kt new file mode 100644 index 0000000..1660ca3 --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/utils/DialogUtils.kt @@ -0,0 +1,16 @@ +package com.makerloom.airtimerewards.utils + +import android.support.v7.app.AlertDialog + +class DialogUtils { + companion object { + fun dismiss (alertDialog: AlertDialog?) { + try { + alertDialog?.dismiss() + } + catch (ex: Exception) { + ex.printStackTrace() + } + } + } +} \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/utils/JsonUtils.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/utils/JsonUtils.kt new file mode 100644 index 0000000..82b4b7a --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/utils/JsonUtils.kt @@ -0,0 +1,54 @@ +package com.makerloom.airtimerewards.utils + +import android.content.Context +import org.json.JSONArray +import org.json.JSONObject + + + +class JsonUtils { + companion object { + fun loadJsonStringFromAsset(context: Context, resId: Int): String? { + var jsonString: String? = null + + try { + val `is` = context.getResources().openRawResource(resId) + val size = `is`.available() + val buffer = ByteArray(size) + `is`.read(buffer) + `is`.close() + jsonString = String(buffer, Charsets.UTF_8) + } + catch (ex: Exception) { + ex.printStackTrace() + return null + } + + return jsonString + } + + fun loadJsonFromAsset(context: Context, resId: Int): JSONObject? { + try { + val jsonString = loadJsonStringFromAsset(context, resId) + return JSONObject(jsonString) + } + catch (ex: Exception) { + ex.printStackTrace() + return null + } + + } + + fun loadJsonArrayFromAsset(context: Context, resId: Int): JSONArray? { + try { + val jsonString = loadJsonStringFromAsset(context, resId) + return JSONArray(jsonString) + } + catch (ex: Exception) { + ex.printStackTrace() + return null + } + + } + } +} \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/utils/PhoneUtils.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/utils/PhoneUtils.kt new file mode 100644 index 0000000..726a73f --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/utils/PhoneUtils.kt @@ -0,0 +1,91 @@ +package com.makerloom.airtimerewards.utils + +import java.util.ArrayList +import java.util.Arrays + +class PhoneUtils { + companion object { + internal val INVALID_NUMBER: Int? = -1 + + internal val MTN_NUMBER = 0 + internal val GLO_NUMBER = 1 + internal val AIRTEL_NUMBER = 2 + internal val ETISALAT_NUMBER = 3 + + internal val allNumberPrefixes: List + get() { + val allNumberPrefixes = ArrayList() + + allNumberPrefixes.addAll(Arrays.asList(*MTN.numberPrefixes)) + allNumberPrefixes.addAll(Arrays.asList(*GLO.numberPrefixes)) + allNumberPrefixes.addAll(Arrays.asList(*Airtel.numberPrefixes)) + allNumberPrefixes.addAll(Arrays.asList(*Etisalat.numberPrefixes)) + + return allNumberPrefixes + } + + internal var ALL_NUMBER_PREFIXES = allNumberPrefixes + + internal val MIN_PHONE_NO_LEN = 11 + internal val MAX_PHONE_NO_LEN = 14 + + internal var NIGERIA_CODE = "+234" + + fun isValid(phoneNumber: String): Boolean { + val len = phoneNumber.length + + for (numberPrefix in ALL_NUMBER_PREFIXES) { + if (phoneNumber.startsWith(numberPrefix) && (len == MIN_PHONE_NO_LEN && !phoneNumber.startsWith("+234") || len == MAX_PHONE_NO_LEN && !phoneNumber.startsWith("0"))) { + return true + } + } + + return false + } + + internal fun getType(phoneNumber: String): Int { + // MTN + for (numberPrefix in MTN.numberPrefixes) { + if (phoneNumber.startsWith(numberPrefix)) { + return MTN_NUMBER + } + } + // GLO + for (numberPrefix in GLO.numberPrefixes) { + if (phoneNumber.startsWith(numberPrefix)) { + return GLO_NUMBER + } + } + // Airtel + for (numberPrefix in Airtel.numberPrefixes) { + if (phoneNumber.startsWith(numberPrefix)) { + return AIRTEL_NUMBER + } + } + // Etisalat + for (numberPrefix in Etisalat.numberPrefixes) { + if (phoneNumber.startsWith(numberPrefix)) { + return ETISALAT_NUMBER + } + } + + return INVALID_NUMBER!! + } + + internal object MTN { + var numberPrefixes = arrayOf("+234703", "+234706", "+234803", "+234806", "+234810", "+234813", "+234814", "+234816", "+234903", "+234906", "0703", "0706", "0803", "0806", "0810", "0813", "0814", "0816", "0903", "0906") + } + + internal object GLO { + var numberPrefixes = arrayOf("+234705", "+234805", "+234807", "+234811", "+234815", "+234905", "0705", "0805", "0807", "0811", "0815", "0905") + } + + internal object Airtel { + var numberPrefixes = arrayOf("+234701", "+234708", "+234802", "+234808", "+234812", "+234902", "+234907", "0701", "0708", "0802", "0808", "0812", "0902", "0907") + } + + internal object Etisalat { + var numberPrefixes = arrayOf("+234809", "+234817", "+234818", "+234908", "+234909", "0809", "0817", "0818", "0908", "0909") + } + } +} \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/utils/SnackbarUtils.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/utils/SnackbarUtils.kt new file mode 100644 index 0000000..6af5b52 --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/utils/SnackbarUtils.kt @@ -0,0 +1,14 @@ +package com.makerloom.airtimerewards.utils + +import android.content.Context +import android.support.design.widget.Snackbar +import android.support.v7.app.AppCompatActivity + +class SnackbarUtils { + companion object { + fun showSnackbar (activity: AppCompatActivity, message: String) { + Snackbar.make(activity.findViewById(android.R.id.content), message, Snackbar.LENGTH_LONG) + .show() + } + } +} \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/utils/SurveyUtils.kt b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/utils/SurveyUtils.kt new file mode 100644 index 0000000..c1f37d5 --- /dev/null +++ b/AirtimeRewards/app/src/main/java/com/makerloom/airtimerewards/utils/SurveyUtils.kt @@ -0,0 +1,105 @@ +package com.makerloom.airtimerewards.utils + +import android.content.Context +import android.graphics.Typeface +import android.support.v4.content.ContextCompat +import android.util.TypedValue +import android.view.Gravity +import android.view.View +import android.widget.* +import android.widget.LinearLayout.HORIZONTAL +import android.widget.LinearLayout.VERTICAL +import com.makerloom.airtimerewards.R +import com.makerloom.airtimerewards.models.Question + +class SurveyUtils { + companion object { + fun buildQuestionView (context: Context, question: Question): View { + val layout = LinearLayout(context) + layout.orientation = LinearLayout.VERTICAL + + layout.addView(buildQuestionTextView(context, question)) + + if (question.checkBoxType != null && question.checkBoxType) { + layout.addView(buildCheckBoxesView(context, question)) + } + else if (question.radioButtonType != null && question.radioButtonType) { + layout.addView(buildRadioButtonsView(context, question)) + } + else { + layout.addView(buildEditTextView(context, question)) + } + + return layout + } + + private fun buildRadioButtonsView (context: Context, question: Question): View { + val radioGroup = RadioGroup(context) + radioGroup.orientation = LinearLayout.VERTICAL + + question.options?.forEach { + val radioButton = RadioButton(context) + + radioButton.apply { + text = it + setTextSize(TypedValue.COMPLEX_UNIT_SP, 16.5f) + typeface = Typeface.DEFAULT + setTextColor(ContextCompat.getColor(context, R.color.material_grey_600)) + } + + radioGroup.addView(radioButton) + } + + return radioGroup + } + + private fun buildCheckBoxesView (context: Context, question: Question): View { + val layout = LinearLayout(context) + layout.orientation = LinearLayout.VERTICAL + + question.options?.forEach { + val checkBox = CheckBox(context) + + checkBox.apply { + text = it + setTextSize(TypedValue.COMPLEX_UNIT_SP, 16.5f) + typeface = Typeface.DEFAULT + setTextColor(ContextCompat.getColor(context, R.color.material_grey_600)) + } + + layout.addView(checkBox) + } + + return layout + } + + private fun buildEditTextView (context: Context, question: Question): View { + val editText = EditText(context) + + editText.apply { + hint = "" + setSingleLine(true) + setTextSize(TypedValue.COMPLEX_UNIT_SP, 16.5f) + typeface = Typeface.DEFAULT + setTextColor(ContextCompat.getColor(context, R.color.material_grey_600)) + } + + return editText + } + + private fun buildQuestionTextView (context: Context, question: Question): View { + val textView = TextView(context) + + textView.apply { + text = question.questionText + setTextSize(TypedValue.COMPLEX_UNIT_SP, 17.0f) + typeface = Typeface.create("sans-serif-light", Typeface.BOLD) + setTextColor(ContextCompat.getColor(context, R.color.material_grey_800)) + setPadding(paddingLeft + 12, paddingTop + 32, paddingRight, paddingBottom + 24) + } + + return textView + } + } + +} \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/AirtimeRewards/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..c7bd21d --- /dev/null +++ b/AirtimeRewards/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/AirtimeRewards/app/src/main/res/drawable/ic_complete.xml b/AirtimeRewards/app/src/main/res/drawable/ic_complete.xml new file mode 100644 index 0000000..8619d39 --- /dev/null +++ b/AirtimeRewards/app/src/main/res/drawable/ic_complete.xml @@ -0,0 +1,5 @@ + + + diff --git a/AirtimeRewards/app/src/main/res/drawable/ic_launcher_background.xml b/AirtimeRewards/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..d5fccc5 --- /dev/null +++ b/AirtimeRewards/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AirtimeRewards/app/src/main/res/layout/activity_complete.xml b/AirtimeRewards/app/src/main/res/layout/activity_complete.xml new file mode 100644 index 0000000..901d2de --- /dev/null +++ b/AirtimeRewards/app/src/main/res/layout/activity_complete.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/res/layout/activity_main.xml b/AirtimeRewards/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..f787496 --- /dev/null +++ b/AirtimeRewards/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/res/layout/activity_survey.xml b/AirtimeRewards/app/src/main/res/layout/activity_survey.xml new file mode 100644 index 0000000..6f1393a --- /dev/null +++ b/AirtimeRewards/app/src/main/res/layout/activity_survey.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/res/layout/content_complete.xml b/AirtimeRewards/app/src/main/res/layout/content_complete.xml new file mode 100644 index 0000000..6a7f80a --- /dev/null +++ b/AirtimeRewards/app/src/main/res/layout/content_complete.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/res/layout/content_main.xml b/AirtimeRewards/app/src/main/res/layout/content_main.xml new file mode 100644 index 0000000..61df34d --- /dev/null +++ b/AirtimeRewards/app/src/main/res/layout/content_main.xml @@ -0,0 +1,57 @@ + + + + + + + + \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/res/layout/content_survey.xml b/AirtimeRewards/app/src/main/res/layout/content_survey.xml new file mode 100644 index 0000000..2574afa --- /dev/null +++ b/AirtimeRewards/app/src/main/res/layout/content_survey.xml @@ -0,0 +1,25 @@ + + + + + + \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/res/layout/fragment_survey_question.xml b/AirtimeRewards/app/src/main/res/layout/fragment_survey_question.xml new file mode 100644 index 0000000..205e0f5 --- /dev/null +++ b/AirtimeRewards/app/src/main/res/layout/fragment_survey_question.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/AirtimeRewards/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/AirtimeRewards/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/AirtimeRewards/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/AirtimeRewards/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/res/mipmap-hdpi/ic_launcher.png b/AirtimeRewards/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..a2f5908 Binary files /dev/null and b/AirtimeRewards/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/AirtimeRewards/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/AirtimeRewards/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..1b52399 Binary files /dev/null and b/AirtimeRewards/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/AirtimeRewards/app/src/main/res/mipmap-mdpi/ic_launcher.png b/AirtimeRewards/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..ff10afd Binary files /dev/null and b/AirtimeRewards/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/AirtimeRewards/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/AirtimeRewards/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..115a4c7 Binary files /dev/null and b/AirtimeRewards/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/AirtimeRewards/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/AirtimeRewards/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..dcd3cd8 Binary files /dev/null and b/AirtimeRewards/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/AirtimeRewards/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/AirtimeRewards/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..459ca60 Binary files /dev/null and b/AirtimeRewards/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/AirtimeRewards/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/AirtimeRewards/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..8ca12fe Binary files /dev/null and b/AirtimeRewards/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/AirtimeRewards/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/AirtimeRewards/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..8e19b41 Binary files /dev/null and b/AirtimeRewards/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/AirtimeRewards/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/AirtimeRewards/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..b824ebd Binary files /dev/null and b/AirtimeRewards/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/AirtimeRewards/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/AirtimeRewards/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..4c19a13 Binary files /dev/null and b/AirtimeRewards/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/AirtimeRewards/app/src/main/res/raw/questions.json b/AirtimeRewards/app/src/main/res/raw/questions.json new file mode 100644 index 0000000..2423057 --- /dev/null +++ b/AirtimeRewards/app/src/main/res/raw/questions.json @@ -0,0 +1,58 @@ +[ + { + "questionText": "What age bracket do you belong to?", + "radioButtonType": true, + "options": [ + "Below 18", + "18-25", + "26-30", + "31-40", + "41-50", + "Above 50" + ] + }, + { + "questionText": "What is your favourite instant noodles product?", + "radioButtonType": true, + "options": [ + "Indomie Noodles", + "Mi Mi Noodles", + "Dangote Noodles", + "Other" + ] + }, + { + "questionText": "What is your party of choice in the upcoming elections?", + "radioButtonType": true, + "options": [ + "PDP", + "APC", + "SDP", + "Other" + ] + }, + { + "questionText": "Who is your candidate of choice in the upcoming elections?", + "radioButtonType": true, + "options": [ + "Donald Duke", + "Atiku Abubakar", + "Mohammadu Buhari", + "Other" + ] + }, + { + "questionText": "What is your favourite car brand?", + "inputType": true + }, + { + "questionText": "Which transport means would you use when travelling across geopolitical zones?", + "checkBoxType": true, + "options": [ + "Train", + "Public Vehicle", + "Private Vehicle", + "Plane" + ] + } +] \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/res/values/colors.xml b/AirtimeRewards/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..516bf70 --- /dev/null +++ b/AirtimeRewards/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + @color/md_blue_600 + @color/md_blue_700 + @color/md_blue_200 + diff --git a/AirtimeRewards/app/src/main/res/values/colors_material.xml b/AirtimeRewards/app/src/main/res/values/colors_material.xml new file mode 100644 index 0000000..f802c39 --- /dev/null +++ b/AirtimeRewards/app/src/main/res/values/colors_material.xml @@ -0,0 +1,338 @@ + + + + + + + @color/md_grey_50 + + #1F000000 + + #61000000 + + #8A000000 + #8A000000 + + #DE000000 + + @color/md_grey_300 + @color/md_grey_100 + @color/md_white_1000 + @color/md_white_1000 + + + @color/md_grey_850 + + #1FFFFFFF + + #4DFFFFFF + + #B3FFFFFF + #B3FFFFFF + + #FFFFFFFF + + @color/md_black_1000 + @color/md_grey_900 + @color/md_grey_800 + @color/md_grey_800 + + + #FFEBEE + #FFCDD2 + #EF9A9A + #E57373 + #EF5350 + #F44336 + #E53935 + #D32F2F + #C62828 + #B71C1C + #FF8A80 + #FF5252 + #FF1744 + #D50000 + + + #FCE4EC + #F8BBD0 + #F48FB1 + #F06292 + #EC407A + #E91E63 + #D81B60 + #C2185B + #AD1457 + #880E4F + #FF80AB + #FF4081 + #F50057 + #C51162 + + + #F3E5F5 + #E1BEE7 + #CE93D8 + #BA68C8 + #AB47BC + #9C27B0 + #8E24AA + #7B1FA2 + #6A1B9A + #4A148C + #EA80FC + #E040FB + #D500F9 + #AA00FF + + + #EDE7F6 + #D1C4E9 + #B39DDB + #9575CD + #7E57C2 + #673AB7 + #5E35B1 + #512DA8 + #4527A0 + #311B92 + #B388FF + #7C4DFF + #651FFF + #6200EA + + + #E8EAF6 + #C5CAE9 + #9FA8DA + #7986CB + #5C6BC0 + #3F51B5 + #3949AB + #303F9F + #283593 + #1A237E + #8C9EFF + #536DFE + #3D5AFE + #304FFE + + + #E3F2FD + #BBDEFB + #90CAF9 + #64B5F6 + #42A5F5 + #2196F3 + #1E88E5 + #1976D2 + #1565C0 + #0D47A1 + #82B1FF + #448AFF + #2979FF + #2962FF + + + #E1F5FE + #B3E5FC + #81D4FA + #4FC3F7 + #29B6F6 + #03A9F4 + #039BE5 + #0288D1 + #0277BD + #01579B + #80D8FF + #40C4FF + #00B0FF + #0091EA + + + #E0F7FA + #B2EBF2 + #80DEEA + #4DD0E1 + #26C6DA + #00BCD4 + #00ACC1 + #0097A7 + #00838F + #006064 + #84FFFF + #18FFFF + #00E5FF + #00B8D4 + + + #E0F2F1 + #B2DFDB + #80CBC4 + #4DB6AC + #26A69A + #009688 + #00897B + #00796B + #00695C + #004D40 + #A7FFEB + #64FFDA + #1DE9B6 + #00BFA5 + + + #E8F5E9 + #C8E6C9 + #A5D6A7 + #81C784 + #66BB6A + #4CAF50 + #43A047 + #388E3C + #2E7D32 + #1B5E20 + #B9F6CA + #69F0AE + #00E676 + #00C853 + + + #F1F8E9 + #DCEDC8 + #C5E1A5 + #AED581 + #9CCC65 + #8BC34A + #7CB342 + #689F38 + #558B2F + #33691E + #CCFF90 + #B2FF59 + #76FF03 + #64DD17 + + + #F9FBE7 + #F0F4C3 + #E6EE9C + #DCE775 + #D4E157 + #CDDC39 + #C0CA33 + #AFB42B + #9E9D24 + #827717 + #F4FF81 + #EEFF41 + #C6FF00 + #AEEA00 + + + #FFFDE7 + #FFF9C4 + #FFF59D + #FFF176 + #FFEE58 + #FFEB3B + #FDD835 + #FBC02D + #F9A825 + #F57F17 + #FFFF8D + #FFFF00 + #FFEA00 + #FFD600 + + + #FFF8E1 + #FFECB3 + #FFE082 + #FFD54F + #FFCA28 + #FFC107 + #FFB300 + #FFA000 + #FF8F00 + #FF6F00 + #FFE57F + #FFD740 + #FFC400 + #FFAB00 + + + #FFF3E0 + #FFE0B2 + #FFCC80 + #FFB74D + #FFA726 + #FF9800 + #FB8C00 + #F57C00 + #EF6C00 + #E65100 + #FFD180 + #FFAB40 + #FF9100 + #FF6D00 + + + #FBE9E7 + #FFCCBC + #FFAB91 + #FF8A65 + #FF7043 + #FF5722 + #F4511E + #E64A19 + #D84315 + #BF360C + #FF9E80 + #FF6E40 + #FF3D00 + #DD2C00 + + + #EFEBE9 + #D7CCC8 + #BCAAA4 + #A1887F + #8D6E63 + #795548 + #6D4C41 + #5D4037 + #4E342E + #3E2723 + + + #FAFAFA + #F5F5F5 + #EEEEEE + #E0E0E0 + #BDBDBD + #9E9E9E + #757575 + #616161 + #424242 + #303030 + #212121 + + + #ECEFF1 + #CFD8DC + #B0BEC5 + #90A4AE + #78909C + #607D8B + #546E7A + #455A64 + #37474F + #263238 + + + #000000 + #FFFFFF + + \ No newline at end of file diff --git a/AirtimeRewards/app/src/main/res/values/dimens.xml b/AirtimeRewards/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..59a0b0c --- /dev/null +++ b/AirtimeRewards/app/src/main/res/values/dimens.xml @@ -0,0 +1,3 @@ + + 16dp + diff --git a/AirtimeRewards/app/src/main/res/values/strings.xml b/AirtimeRewards/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..e66de11 --- /dev/null +++ b/AirtimeRewards/app/src/main/res/values/strings.xml @@ -0,0 +1,9 @@ + + Airtime Rewards + @string/app_name + Survey + + + Hello blank fragment + @string/app_name + diff --git a/AirtimeRewards/app/src/main/res/values/styles.xml b/AirtimeRewards/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..545b9c6 --- /dev/null +++ b/AirtimeRewards/app/src/main/res/values/styles.xml @@ -0,0 +1,20 @@ + + + + + + + +