Skip to content
This repository has been archived by the owner on Jul 18, 2024. It is now read-only.

Commit

Permalink
Merge pull request #76 from ryanwarsaw/master
Browse files Browse the repository at this point in the history
[V2] Content structuring
  • Loading branch information
secretrobotron authored Jul 31, 2018
2 parents 35e75ea + bec73a6 commit 4fa8b4a
Show file tree
Hide file tree
Showing 28 changed files with 498 additions and 532 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.ryanwarsaw.coach_erevu;

import static org.junit.Assert.assertEquals;

import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.assertEquals;

/**
* Instrumented test, which will execute on an Android device.
*
Expand Down
9 changes: 5 additions & 4 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@
android:theme="@style/AppTheme">
<!-- Need to listen to screenSize when targeting Android API 13+ -->
<activity
android:name="com.ryanwarsaw.coach_erevu.MainActivity"
android:name=".MainActivity"
android:configChanges="orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name="com.ryanwarsaw.coach_erevu.activity.ActionActivity"/>
<activity android:name="com.ryanwarsaw.coach_erevu.activity.VideoActivity"/>
<activity android:name="com.ryanwarsaw.coach_erevu.activity.QuizActivity"/>
<activity android:name=".activity.ActionActivity"/>
<activity android:name=".activity.VideoActivity"/>
<activity android:name=".activity.QuizActivity"/>
<activity android:name=".activity.TopicActivity"/>
</application>

</manifest>
74 changes: 33 additions & 41 deletions app/src/main/java/com/ryanwarsaw/coach_erevu/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,30 @@
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.widget.LinearLayout;
import android.widget.ListView;

import com.google.gson.GsonBuilder;
import com.ryanwarsaw.coach_erevu.adapter.MenuAdapter;
import com.ryanwarsaw.coach_erevu.adapter.CategoryAdapter;
import com.ryanwarsaw.coach_erevu.logging.LoggingHandler;
import com.ryanwarsaw.coach_erevu.model.Curriculum;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import lombok.Cleanup;
import lombok.Getter;

public class MainActivity extends AppCompatActivity {

@Getter
public Curriculum curriculum;
private MenuAdapter menuAdapter;
private static LoggingHandler loggingHandler;

private static String CONTENT_FILENAME = "content.json";
Expand All @@ -46,19 +49,11 @@ protected void onCreate(Bundle savedInstanceState) {
// Manually update the orientation when initially starting the app.
onConfigurationChanged(getResources().getConfiguration());

// Import the content.json file into memory, log the result.
curriculum = parseContentFile(CONTENT_FILENAME);

// Create the log file if it doesn't exist, instantiate our logger.
loggingHandler = new LoggingHandler(LOG_FILENAME);

getLoggingHandler().write(getClass().getSimpleName(), "APP_STARTED", curriculum.getVersion());

// Check edge case of the app not having appropriate permissions.
if (curriculum != null) {
// Build the main menu options from the content.json file
menuAdapter = new MenuAdapter(this, curriculum);
((ListView) findViewById(R.id.menu_options)).setAdapter(menuAdapter);
// Mostly for first-time users, makes sure we have proper permissions before inflating content.
if (!hasPermissions(this, PERMISSIONS)) {
ActivityCompat.requestPermissions(this, PERMISSIONS, 1);
} else {
inflateContentFile(CONTENT_FILENAME);
}
}

Expand All @@ -74,13 +69,11 @@ public void onConfigurationChanged(Configuration newConfig) {
}

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
if (requestCode == 1 && grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Successfully received permission approval, finish inflating the main menu.
curriculum = parseContentFile(CONTENT_FILENAME);
menuAdapter = new MenuAdapter(this, curriculum);
((ListView) findViewById(R.id.menu_options)).setAdapter(menuAdapter);
inflateContentFile(CONTENT_FILENAME);
} else {
finishAffinity(); // Failed to receive permission approval, kill the application.
}
Expand All @@ -91,29 +84,28 @@ public void onRequestPermissionsResult(int requestCode, String permissions[], in
* check the Download/ folder on your Android device for a file with the name provided. If
* it is unable to find that file, it will default to the app packaged version.
* @param fileName The fully qualified name of the file (including extension).
* @return Curriculum object representation of the content file, or null if an error occurred.
*/
private Curriculum parseContentFile(String fileName) {
if (hasPermissions(this, PERMISSIONS)) {
File externalFile = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS).getPath() + "/" + fileName);
try {
@Cleanup InputStream inputStream = externalFile.exists() ?
new FileInputStream(externalFile) : getResources().openRawResource(R.raw.content);
@Cleanup ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

byte[] buffer = new byte[inputStream.available()];
inputStream.read(buffer);
outputStream.write(buffer);

return new GsonBuilder().create().fromJson(outputStream.toString(), Curriculum.class);
} catch (IOException e) {
throw new RuntimeException("An error occurred while trying to parse file: " + fileName);
}
} else {
ActivityCompat.requestPermissions(this, PERMISSIONS, 1);
private void inflateContentFile(String fileName) {
File externalFile = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS).getPath() + "/" + fileName);
loggingHandler = new LoggingHandler(LOG_FILENAME);

try {
@Cleanup InputStream inputStream = externalFile.exists() ?
new FileInputStream(externalFile) : getResources().openRawResource(R.raw.content);
@Cleanup ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

byte[] buffer = new byte[inputStream.available()];
inputStream.read(buffer);
outputStream.write(buffer);

curriculum = new GsonBuilder().create().fromJson(outputStream.toString(), Curriculum.class);
} catch (IOException e) {
throw new RuntimeException("An error occurred while trying to parse file: " + fileName);
}
return null;

((ListView) findViewById(R.id.menu_options)).setAdapter(new CategoryAdapter(this, getCurriculum()));
getLoggingHandler().write(getClass().getSimpleName(), "APP_STARTED", getCurriculum().getVersion());
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,53 +1,79 @@
package com.ryanwarsaw.coach_erevu.activity;

import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.ryanwarsaw.coach_erevu.MainActivity;
import com.ryanwarsaw.coach_erevu.R;
import com.ryanwarsaw.coach_erevu.model.Week;
import com.ryanwarsaw.coach_erevu.model.Preferences;
import com.ryanwarsaw.coach_erevu.model.Topic;

public class ActionActivity extends AppCompatActivity {

private Week week;
private Topic topic;
private Preferences preferences;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_action);

// We use GSON to serialize and deserialize our payload so we
// can handily pass it around activities without problem.
final String payload = getIntent().getStringExtra("payload");
week = new GsonBuilder().create().fromJson(payload, Week.class);
// GSON is responsible for serializing and de-serializing our payload data
// so that we can pass it between activities using intents without problems.
final Gson gson = new GsonBuilder().create();
topic = gson.fromJson(getIntent().getStringExtra("topic"), Topic.class);
preferences = gson.fromJson(getIntent().getStringExtra("preferences"), Preferences.class);

// Update header text for the current week we're on.
((TextView) findViewById(R.id.header_text)).setText(week.getTitle());
((TextView) findViewById(R.id.header_text)).setText(topic.getTitle());

// Handle when a user interact's with the watch video button.
final Button videoButton = findViewById(R.id.watch_video_button);

// Dynamically change the button background color based on the content file, without changing
// the rest of the drawable element's background(s), or other instances of the drawable.
((GradientDrawable) ((LayerDrawable) videoButton.getBackground().mutate())
.findDrawableByLayerId(R.id.button_background))
.setColor(Color.parseColor(preferences.getVideoButtonColor()));

videoButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
final Intent intent = new Intent(ActionActivity.this, VideoActivity.class);
intent.putExtra("video_name", week.getVideoName());
intent.putExtra("video_name", topic.getVideoName());
intent.putExtra("preferences", gson.toJson(preferences));

MainActivity.getLoggingHandler().write(ActionActivity.this.getClass().getSimpleName(),
"BUTTON_VIDEO_PRESS", week.getVideoName());
"BUTTON_VIDEO_PRESS", topic.getVideoName());
ActionActivity.this.startActivity(intent);
}
});

final Button quizButton = findViewById(R.id.take_quiz_button);

// Dynamically change the button background color based on the content file, without changing
// the rest of the drawable element's background(s), or other instances of the drawable.
((GradientDrawable) ((LayerDrawable) quizButton.getBackground().mutate())
.findDrawableByLayerId(R.id.button_background))
.setColor(Color.parseColor(preferences.getQuizButtonColor()));

quizButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
if (week.getQuestions().size() > 0) {
if (topic.getQuestions().size() > 0) {
final Intent intent = new Intent(ActionActivity.this, QuizActivity.class);
intent.putExtra("payload", payload);
intent.putExtra("topic", gson.toJson(topic));
intent.putExtra("preferences", gson.toJson(preferences));

MainActivity.getLoggingHandler().write(ActionActivity.this.getClass().getSimpleName(),
"BUTTON_QUIZ_PRESS", week.getTitle());
"BUTTON_QUIZ_PRESS", topic.getTitle());
ActionActivity.this.startActivity(intent);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,40 +12,45 @@
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.ryanwarsaw.coach_erevu.MainActivity;
import com.ryanwarsaw.coach_erevu.R;
import com.ryanwarsaw.coach_erevu.adapter.AnswerAdapter;
import com.ryanwarsaw.coach_erevu.fragment.WrongAnswerFragment;
import com.ryanwarsaw.coach_erevu.model.Preferences;
import com.ryanwarsaw.coach_erevu.model.Question;
import com.ryanwarsaw.coach_erevu.model.Week;
import com.ryanwarsaw.coach_erevu.model.Topic;

public class QuizActivity extends AppCompatActivity implements View.OnClickListener {

private Week week;
private Topic topic;
private Question question;
private AnswerAdapter answerAdapter;
private int currentIndex = 0;
private Preferences preferences;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);

final String payload = getIntent().getStringExtra("payload");
week = new GsonBuilder().create().fromJson(payload, Week.class);
final Gson gson = new GsonBuilder().create();
topic = gson.fromJson(getIntent().getStringExtra("topic"), Topic.class);
preferences = gson.fromJson(getIntent().getStringExtra("preferences"), Preferences.class);

populateQuestion(currentIndex);
inflateQuestion(currentIndex);
}

private void populateQuestion(int index) {
question = week.getQuestions().get(index);
private void inflateQuestion(int index) {
question = topic.getQuestions().get(index);
currentIndex = index;

// Update the header text with current question index.
final TextView headerText = findViewById(R.id.header_text);
headerText.setText(week.getTitle() + " : " + getResources().getString(R.string.question) + " (" +
(currentIndex + 1) + " " + getResources().getString(R.string.of) + " " + week.getQuestions().size() + ")");
headerText.setText(getString(R.string.question_header, topic.getTitle(),
currentIndex + 1, topic.getQuestions().size()));

// Update the question text with the current question.
final TextView questionText = findViewById(R.id.question_text);
Expand Down Expand Up @@ -91,9 +96,9 @@ private void populateQuestion(int index) {

public void advanceToNextQuestion() {
// Populate the next question if we have remaining questions, otherwise finish activity.
if (currentIndex + 1 < week.getQuestions().size()) {
if (currentIndex + 1 < topic.getQuestions().size()) {
// We have not completed the quiz yet, so let's move onto the next question.
populateQuestion(++currentIndex);
inflateQuestion(++currentIndex);
} else {
// Build the dialog to inform the user they've finished the quiz, and present it to them.
AlertDialog alertDialog = new AlertDialog.Builder(this).create();
Expand Down Expand Up @@ -129,6 +134,7 @@ public void onClick(View v) {
Bundle args = new Bundle();
args.putString("correct_answer", question.getAnswers().get(question.getCorrectAnswerIndex() - 1));
args.putString("answer_explanation", question.getAnswerExplanation());
args.putString("preferences", new GsonBuilder().create().toJson(preferences));
fragment.setArguments(args);
fragment.show(getFragmentManager(), "wrong_answer_dialog");
}
Expand All @@ -147,8 +153,10 @@ public void onClick(View v) {

// Close the keyboard if it's still open and accessible to the user.
InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.hideSoftInputFromWindow((getCurrentFocus() == null) ? null
: getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
if (inputManager != null) {
inputManager.hideSoftInputFromWindow((getCurrentFocus() == null) ? null
: getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}

// Advance to the next question programmatically, and update the view.
advanceToNextQuestion();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.ryanwarsaw.coach_erevu.activity;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ListView;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.ryanwarsaw.coach_erevu.R;
import com.ryanwarsaw.coach_erevu.adapter.TopicAdapter;
import com.ryanwarsaw.coach_erevu.model.Category;
import com.ryanwarsaw.coach_erevu.model.Preferences;

public class TopicActivity extends AppCompatActivity {

private Category category;
private Preferences preferences;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_topic);

final Gson gson = new GsonBuilder().create();
final Intent intent = getIntent();

category = gson.fromJson(intent.getStringExtra("category"), Category.class);
preferences = gson.fromJson(intent.getStringExtra("preferences"), Preferences.class);

((ListView) findViewById(R.id.topic_list_options))
.setAdapter(new TopicAdapter(this, category, preferences));
}
}
Loading

0 comments on commit 4fa8b4a

Please sign in to comment.